Форум: "Основная";
Текущий архив: 2005.08.07;
Скачать: [xml.tar.bz2];
Вниз
Замена Move для маленьких объёмов данных. Найти похожие ветки
← →
MegaVolt © (2005-07-20 16:01) [0]Вот решил потестировать как работает Move для маленьких объёмов данных в данном случае для перемещения одного байта. Получил любопытные результаты:
Стандартная Move 7,35 сек
Move1 1,06 сек
Move2 1,15 сек
Move3 1,06 сек
Move4 1,15 сек
1. Почему изменение типа к которому я привожу данные влияет на результат?
2. Есть ли ещё более оптимальные методы?
Вот код:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TBA = array of byte;
TBA2 = array [0..Maxint-1] of byte;
var
Form1: TForm1;
z: array [0..32767] of byte;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
i:integer;
begin
for i:=0 to 32767 do z[i]:=i;
end;
procedure move1(const Source; var Dest; Count: Integer);
var
i:integer;
begin
for i:=0 to Count-1 do
TByteArray(Dest)[i]:=TByteArray(Source)[i];
end;
procedure move2(const Source; var Dest; Count: Integer);
var
i:integer;
begin
for i:=0 to Count-1 do
TBA(@Dest)[i]:=TBA(@Source)[i];
end;
procedure move3(const Source; var Dest; Count: Integer);
var
i:integer;
begin
for i:=0 to Count-1 do
TBA2(Dest)[i]:=TBA2(Source)[i];
end;
procedure move4(const Source; var Dest; Count: Integer);
var
i:integer;
begin
for i:=0 to Count-1 do
PChar(@Dest)[i]:=PChar(@Source)[i];
end;
procedure Read(var Buffer; Count,ind:integer);
begin
move4(z[ind],Buffer,Count);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i,j,time:integer;
b:byte;
begin
time:=0;
Dec(time,GetTickCount);
for j:=0 to 1000 do
for i:=0 to 32767 do
begin
Read(b,1,i);
if b<>byte(i) then showmessage(IntToStr(b)+"<>"+IntToStr(i));
end;
Inc(time,GetTickCount);
ShowMessage(IntToStr(time));
end;
end.
← →
MBo © (2005-07-20 16:09) [1]http://dennishomepage.gugs-cats.dk/FastCodeProject.htm
http://dennishomepage.gugs-cats.dk/Libraries.htm
← →
PVOzerski © (2005-07-20 16:17) [2]Замечание к реализации: не надо связываться с динамическими массивами, если хотим получить бОльшую скорость. А уж работать с ними вот так (неявно приводя статические массивы к динамическим) просто не следует.
Еще одно замечание: в Win32 задействуются 4-байтные регистры и такое же выравнивание, и не использовать это обстоятельство, занимаясь побайтовым копированием - вряд ли лучшее решение.
Вопросы к оптимизации:
1) Интересно, а что будет, если описать диапазоны массивов начинающимися не с нуля, а с единицы? Может, тогда в теле функции и вычитать не придется.
2) А если просто привести pointer к cardinal и начать к нему прибавлять вместо адресации по индексам?
← →
Anatoly Podgoretsky © (2005-07-20 16:20) [3]PVOzerski © (20.07.05 16:17) [2]
2) А если просто привести pointer к cardinal и начать к нему прибавлять вместо адресации по индексам?
Не трожь адресную математику, а то он такого натворит.
← →
MegaVolt © (2005-07-20 16:29) [4]MBo спасибо эти странички я уже нашел предварительно порывшись по интеу. Но там куча ассемблера :( а я в нём не силён :(
PVOzerski
>А уж работать с ними вот так просто не следует.
Это в данном конкретном случае принимающий массив байт. А если принимающий массив не байт а динамический массив?
>в Win32 задействуются 4-байтные регистры
А что делать если действительно нужна побайтная обработка? Или всё равно читать по 4 байта а потом цикл от 1 до 4 крутить?
>А если просто привести pointer к cardinal и начать к нему прибавлять вместо адресации по индексам?
Что то вроде этого?
procedure move5(const Source; var Dest; Count: Integer);
var
i:integer;
begin
for i:=0 to Count-1 do
byte(pointer(Cardinal(@Dest)+i)^):=byte(pointer(Cardinal(@Source)+i)^);
end;
Получил время 2,2 сек :(
← →
MegaVolt © (2005-07-20 16:33) [5]Удалено модератором
Примечание: В персональную почту
← →
Anatoly Podgoretsky © (2005-07-20 16:39) [6]MegaVolt © (20.07.05 16:29) [4]
Что то вроде этого?
byte(pointer(Cardinal(@Dest)+i)^):=byte(pointer(Cardinal(@Source)+i)^);
Тебе разве советовали складывать с индексом, как раз на оборот
← →
PVOzerski © (2005-07-20 16:40) [7]procedure move5(const Source; var Dest; Count: Integer);
var
i:integer;
p1, p2: pointer;
c1: cardinal absolute p1;
c2: cardinal absolute p2;
begin
p1 := @Source;
p2 := @dest;
for i:=1 to Count do
begin
byte(p2^) := byte(p1^);
inc(c1);
inc(c2);
end;
end;
← →
MegaVolt © (2005-07-20 16:40) [8]Удалено модератором
Примечание: Личная переписка
← →
han_malign © (2005-07-20 16:43) [9]на вскидку
TByteArray(Source)[i] - обращение к ячейке памяти по индексированному адресу
примерно так:
mov edx, @Source
mov ecx, i
mov AL, [edx+ecx] ; сложение выполняется специальным индексным блоком процессора - без затрат
PChar(@Source)[i] (по симантике соответсвует (PChar(@Source)+i)^)
- сложение адреса + обращение
примерно так:
mov edx, @Source
add edx, i ;!!!
mov AL, [edx]
хотя тут компилятор может и оптимизировать до предыдущего варианта
отсюда разница - 1,06 / 1,15
move2 - вообще криминал
move - проверка перекрытия блоков(самое длительное) + загрузка EDI, ESI + проверка шага(4/1), копирование - само собой, для исчезающе малых объемов данных, это не выгодно(когда можно PDWORD()^:=PDWORD()^ (PWORD, PBYTE) - за две asm-операции)
moveX - в данном случае(count = 1) - проверка (Count > 0) + описанное выше + проверка (Count > 0)
отсюда разница - 7,35 / 1,06..1,15
короче пройдись в CPU View и все поймешь
реализацию movе можно посмотреть в System.pas
З.Ы. для непересекающихся блоков памяти рекомендуется пользовать copy() - и будет всем щастье
← →
MegaVolt © (2005-07-20 16:43) [10]PVOzerski Move5 1,7 сек :(
← →
MegaVolt © (2005-07-20 16:49) [11]han_malign спасибо за подробный разбор.
Вот только несколько вопросов:
1. TByteArray(Source)[i] не делает проверки на выход за 32К? Ведь в оригинале TByteArray = array [0..32767] of byte.
2. В чём криминал move2? Ведь у меня Dest может быть и динамическим массивом
3. >для непересекающихся блоков памяти рекомендуется пользовать copy()
А что делать если может возникнуть ситуация когда Dest заполняется за несколько вызовов Read?
← →
PVOzerski © (2005-07-20 16:55) [12]1) Должно зависеть от {$R+/-}
2) Если ты передал статический массив, у него нет скрытого заголовка, свойственного массивам динамическим.
← →
MegaVolt © (2005-07-20 16:59) [13]PVOzerski
>2) Если ты передал статический массив, у него нет скрытого заголовка, свойственного массивам динамическим.
А если DEST является куском реального динамического массива причём не с середины то TByteArray(Dest) это корректно будет?
← →
Anatoly Podgoretsky © (2005-07-20 17:04) [14]MegaVolt © (20.07.05 16:59) [13]
А если DEST является куском реального динамического массива причём не с середины то TByteArray(Dest) это корректно будет?
Корректно, если не будешь выходить за границы.
← →
MegaVolt © (2005-07-20 17:09) [15]Anatoly Podgoretsky
>Корректно, если не будешь выходить за границы.
Ок. Спасибо.
А есть способы ещё ускорить работу по сравнению с Move1?
← →
REA (2005-07-20 17:37) [16]>2) А если просто привести pointer к cardinal и начать к нему прибавлять вместо адресации по индексам?
приводить ничего не надо, можно просто Inc(PByte), но выигрыш от этого небольшой. Можно организовать цикл downto - будет небольшой выигрыш, но видимо не на всех процессорах.
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2005.08.07;
Скачать: [xml.tar.bz2];
Память: 0.49 MB
Время: 0.041 c