· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.A&O —› Вычисление CRC32

. 1 . 2 . >>

Посл.отвђт Сообщенiе


Дата: Апр 22, 2004 13:24:11 · Поправил: Безпощадный даос

Привет всем, дело вот в чём.
Заканчиваю колледж пишу диплом, он почти готов, но вот настало время кое что пооптимизировать.
Пишу значит на делфи и вот остался набор функции которые оптимизировать можно только на Асме.
Раньше на нём ни строчки не писал - решил взятся всётаки полезная вещь хотябы для общего развития. Сначала взял мой конспект лекций из колледжа почитал, потом нашел Абеля "АССЕМБЛЕР И ПРОГРАММИРОВАНИЕ ДЛЯ IBM PC" почитал примерно половину (конспект при этом полетел на помойку) не плохо много узнал, но старовата книжонка всетаки Win32 надо бы. Потом понял что Help в наборе MASM 32 тоже ничего: просто понятно и не так нудно, а то этот Абель со своими вечными прерываниями и прочей Досовской хренотенью достал. Начал делать примеры (ну там function sum(a,b:integer):integer, ну вы меня понимаете) и смотреть как они а ASM кодах выглядят, разобрался с STDCALL PASCAL FASTCALL. Тут на вашем саите нашел статью об оптимизации тоже ниче.
И вот значит первое моё творение на ASME.

Так было:
procedure incPCRC32(P:Pointer;Count:integer;var CRC:cardinal);
var
        i:integer;
        b:byte;
begin
if Count<1 then Exit;
for i:=0 to Count-1 do begin
  b:=byte(Pointer(integer(P)+i)^);
  CRC:=CRC_32_tab[byte(CRC xor cardinal(b))] xor ((CRC shr 8) and $00FFFFFF);
end;
end; 

Так я сделал:
procedure IncPCRC32(P:Pointer;Count:integer;var CRC:cardinal);assembler;
asm
        {Если Сount<1 выходим}
        CMP EDX,1
        JL @@_END
        {Сохраняем EBX}
        PUSH EBX
        {Заносим в ЕБХ значение CRC}
        MOV EBX,[ECX]
        {Cохраняем адрес CRC,ЕДИ,ЕЗИ}
        PUSH ECX
        PUSH ESI
        {В ESI теперь хранится адрес последнего бита}
        MOV ESI,EAX
        ADD ESI,EDX
        {Сoхраняем CRC в ECX}
        MOV ECX,EBX
@@_for:
        {В DL заносим значение бита по адресу EAX}
        MOV DL,BYTE PTR [EAX]
        XOR CL,DL
        AND ECX,000000FFH
        {В ECX заносим значение из таблицы и высчитываем CRC}
        MOV ECX,DWORD PTR [CRC_32_TAB+ECX*4]
        SHR EBX,8
        AND EBX,00FFFFFFH
        XOR ECX,EBX
        {Сохраняем новый CRC в EBX}
        MOV EBX,ECX
        {Наращиваем указатель}
        INC EAX
        {Если указатель не в конце повторяем}
        CMP EAX,ESI
        JNZ @@_for
        POP ESI
        {Обратно вытаскиваем адрес CRC}
        POP ECX
        {Заносим результат}
        MOV [ECX],EBX
        POP EBX
@@_end:
end;


Первая просьба не смеятся если что не так, вторая просьба зацените как получилось подскажите что можно ещё переделать, посоветуйте что нибудь, хочу быть хорошим прогером :).

И к стати не плохая оптимизация получилась в два раза и более в зависимости от размера куска памяти чем он большн тем оптимизация более заметна.

1050372509__CRC32.PAS


Дата: Апр 22, 2004 15:49:10

Eraser
AND EBX,00FFFFFFH абсолютно безполезен после SHR EBX,8


Дата: Апр 22, 2004 22:32:34

Eraser
Вот что сразу приходит на ум, но это далеко не придел, вообще ессно может и ошибся, но суть должна быть ясна
procedure IncPCRC32(P:Pointer;Count:integer;var CRC:cardinal);assembler;
asm
        {Если Сount<1 выходим}
        CMP EDX,1
        JL @@_END
        {Сохраняем EBX}
        PUSH EBX
        {Заносим в ЕБХ значение CRC}
        MOV EBX,[ECX]
        {Cохраняем адрес CRC,ЕДИ,ЕЗИ}
        PUSH ECX
        PUSH ESI

        MOV ESI,EAX
        mov ecx,edx
        mov edx,ebx
@@_for:
        lodsb
        xor dl,al
        and edx,000000FFH
        mov edx,dword ptr [CRC_32_TAB+edx*4]
        shr ebx,8
        xor edx,ebx
        mov ebx,edx
        loop @@_for

        POP ESI
        {Обратно вытаскиваем адрес CRC}
        POP ECX
        {Заносим результат}
        MOV [ECX],EBX
        POP EBX
@@_end:
end;


Дата: Апр 22, 2004 23:05:18


Дата: Апр 23, 2004 13:41:13

2 Mad_C:
А что делает lodsb?
Чёто я не въехал.


Дата: Апр 23, 2004 13:53:05

А что делает lodsb?
загружает байт из esi в al и инкрементирует esi


Дата: Апр 23, 2004 14:18:23

lodsw u lodsdw соответственно?


Дата: Апр 23, 2004 14:29:12

Из Зубкова:
Копирует один байт (LODSB), слово (LODSW) или двойное слово (LODSD) из памяти по адресу DS:ESI (или
DS:SI, в зависимости от разрядности адреса) в регистр AL, АХ или ЕАХ соответственно. При использовании
формы записи LODS ассемблер сам определяет из типа указанного операнда (принято указывать имя строки,
но можно использовать любой операнд подходящего типа), какую из трех форм этой команды (LODSB,
LODSW или LODSD) выбрать. Используя LODS с операндом, можно заменить регистр DS на другой с помощью
префикса замены сегмента (ES:, GS:, FS:, CS:, SS:). После выполнения команды регистр ESI (SI)
увеличивается на 1, 2 или 4 (если считывается байт, слово или двойное слово), если флаг DF = 0,
и уменьшается, если DF = 1. При использовании с префиксом REP команда LODS выполнит копирование
строки длиной в ЕСХ (или СХ), что приведет к тому, что в аккумуляторе окажется последний элемент
строки. На самом деле эту команду используют без префиксов, часто внутри цикла в паре с командой
STOS, так что LODS считывает число, другие команды выполняют над ним какие-нибудь действия,
а затем STOS записывает измененное число в то же место в памяти.


Дата: Апр 24, 2004 12:29:57 · Поправил: Eraser

Результаты тестирования.
-Прграмма загружает файл на 81mb в память.
-Вызываем GetTickCount запоминаем значение
-Высчитываем CRC всего участка памяти функцией IncPCRC32
-Опять вызываем GetTickCount и считаем разницу с начальным GetTickCount'ом, полученную разницу я взял за результат, чем меньше тем лучше.Программу делал есесно на Delphi.

Тестировал на трёх машинах
P4 1500Mhz 512mb SDRAM -результат 1400 стандартная функция, 430 моя функция, 600 функция Mad_C.
Cel 1200Mhz 384mb SDRAM -результат 1552, 1222, 1643
Cel 366Mhz 128mb SDRAM -результат 4450, 3445, 4646

Результаты колеблятся +/-15 тиков

Мои выводы:
1) Eсли оптимизировать на скорость lodsb лучше не использовать.
2)И вобще оптимизировать на ASME надо, надо .... буду.
2) Delphi компилит чтобы на всех видах процессоров был приемлимый рез-ат.

Конечно делитантский тест , но я часто встречал такие примеры тестирования, и думаю выводы по нему сделать можно.

У кого есть AMD? Догадались зачем?


Дата: Апр 24, 2004 12:43:58

Вот ещё результат
P4 2600Mhz, 256mb DDR -результат 812, 250, 460 тиков.

P.S. Добрый наш препод дал свой комп поюзать.


Дата: Апр 24, 2004 17:01:43

AMD AthlonXP 1700, 256 MB SDRAM - 360, правда, алгоритм мой (я его, правда, не оптимизировал практически) :)
А lods(b|w|d) действительно очень медлительны


Дата: Апр 24, 2004 20:29:47

Мне кажется, дело в спаривании команд. Общая логика такая: нельзя делать что-то в следующией команде, если ей требуются результаты предидущей. Пример:

lodsb
use al/ax/eax ; вызывает AGI

В вышеприведенном примере:

@@_for:
lodsb
xor dl,al ; use al after lodsb
and edx,000000FFH
mov edx,dword ptr [CRC_32_TAB+edx*4] ; use edx twice
shr ebx,8
xor edx,ebx
mov ebx,edx
loop @@_for

Я бы сделал немного иначе:
mov edi,offset CRC_32_TAB
xor eax,eax
mov edx,Start_CRC
@@GetCRC:
mov ebx,edx
mov al,[esi]
shr ebx,8
xor al,dl
inc esi
mov edx,[edi+eax*4]
xor edx,ebx
loop @@GetCRC


Дата: Апр 24, 2004 20:33:59

и еще... не совсем уверен, но возможно вместо
loop @@GetCRC лучше будет использовать
dec ecx
jnz @@GetCRC


Дата: Апр 24, 2004 20:40:39

Mad_C

Я тоже читал такие мнения - да и при анализе кодов из HLL почти не встретишь этих loop, но вот на P120 это действительно имеет смысл, на P3 800 - почти пофиг, на P4 1600 - пофигу.


Дата: Апр 25, 2004 14:36:49

1) Вот ещё функцию наточил:
procedure IncPCRC32ASMER2(P:Pointer;Count:integer;var CRC:cardinal);assembler;
asm
        CMP EDX,1
        JL @@_END
        PUSH EBX
        MOV EBX,[ECX]//Заносим в ЕБХ значение CRC
        PUSH ECX//Сохраняем адрес CRC
        MOV ECX,EDX//Заносим в ECX Length buffera
        MOV EDX,EAX//Заносим в ЕЗИ адрес буфера
        MOV EAX,EBX//Сохраняем СРС в ЕAХ
@@_for:
        XOR AL,BYTE PTR [EDX]
        INC EDX
        AND EAX,000000FFH
        MOV EAX,DWORD PTR [CRC_32_TAB+EAX*4]{В ECX заносим значение из
                                таблицы}
        SHR EBX,8
        XOR EAX,EBX
        MOV EBX,EAX//Сохраняем полученый СРС в ЕЦХ
        LOOP @@_for
        POP EAX//Обратно вытаскиваем адрес CRC
        MOV [EAX],EBX//Заносим в этот адрес результат
        POP EBX
@@_end:
end;

Скорость осталась точно такая-же, но по размеру уменьшилась.

2) Loop || dec ECX, jnz @@_for действительно пофигу.

3) Помогите разобраться с передачей в функцию методом fastcall сложных структур, например:
TRec1 = record
  i1:integer;
  i2:integer;
  w1:word;
end;

TRec2 = record
  i1:integer;
  i2:integer;
end;
function SubAddRec(Rec1:TRec1):TRec2;
begin
  Result.i1:=Rec1.i1-Rec1.w;
  Result.i2:=Rec1.i2+Rec1.w;
end;

Где в стеке или по какому адресу или в каком регистре искать их значения Rec1, и куда возвращать результат?

. 1 . 2 . >>


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.050