Здравствуйте Алексей Петров, Вы писали:
АП>Мне не надо считать позиции. Есть конкретная задача — максимально быстро посчитать число различий.
Есть конкретная задача — максимально быстро посчитать число различий. Надо учесть, что вопрос был задан в форуме по ассемблеру, поэтому можно предположить, что задавшего его интересовало решение на ассемблере.
А>>Человек это с трудом может закодировать — нужно знать длину каждой команды. АП>Понятно, что с трудом. Но это как раз аргумент против чрезмерного увлечения асм-ом.
Этот аргумент отдыхает. Сейчас настолько быстрые компьютеры, что небольшое замедление, вызванное (возможно) неоптимальным решением человека, не играет никакой роли (мой любимый аргумент, который я позаимстововал у дельфистов ).
Re[12]: Слишком сложно и к тому-же не верно работает :)
Здравствуйте Алексей Петров, Вы писали:
АП>Все равно пофигу. Мне не нужно выпендриваться размером программы — это болезнь начинающих.
Флеймите, уважаемый. Но, как говорится, кто к нам с мечом придет, тот от оного и погибнет. Чай и сами не дураки и в местах пофлеймистей писали (гы — RU.OS.CMP).
АП>Опытный — согласен, при желании сгенерит и лучше, но станет этим заниматься только на очень критичной по времени функции и тогда, когда на ЯВУ мысли выразить сложнее, чем на asm.
Заметьте, (не)уважаемый, вы сейчас говорите не по делу. Такое впечатление, что вы так и ждете, как кинуться в бой с криком "Асм маздай! Дельфи рулит!". Мы находимся в форуме по ассемблеру. Решения на C++ следует приводить в _другом_ форуме.
>В остальных случаях дополнительные затраты времени и сил на asm не окупаются.
Слушай, я это где-то уже слышал . Только вот к чему ты это пишешь?
АП>А размером с тобой меряться — нет у меня желания, т.к. 5 — 10 байт не имеет значения в 20мегабайтном проекте. Если конечно весь его переписать руками (только не такими кривыми, как у тебя), он из 20 станет 15 мегабайтным, но не за год, а за 10 — а это не приемлемо.
Нехорошо ты на человека наехал. Не по понятиям живешь.
Re[9]: Слишком сложно и к тому-же не верно работает :)
А> xor ecx,ecx ;; Лучше, чтобы команда инкримента ecx была несколько позже...
xor ebx,ebx ;;
inc ecx ;; Поплавок...
А> mov eax,dword ptr i1+4
А> xor eax,dword ptr i2+4
А> jnz l1
;;А>l4: inc ecx ;; Это теперь не надо!!!
А> mov eax,dword ptr i1
А> xor eax,dword ptr i2
А> jz l3
;;А>l1: bswap eax ;; Зачем так жестоко?
l1: test al,0Fh ;;
А> jz @F
А> inc ebx
А>@@: test al,0F0h
А> jz @F
А> inc ebx
А>@@: shr eax,8 ;; Очень плохо!!! Очень, тут надо что-то придумать!!!
jz l2
;; Развернём цикл мы не жадные, тем более, что есть маленькая тонкость...
А> test al,0Fh ;; Так, а теперь внимание, если это ноль, значит последняя точно не ноль!!!
А> jz @F
А> inc ebx ;; Хе-хе
А>@@: inc ebx
dec ecx ;; Приём поплавка!!!
jnz l2
.................................................
.................................................
l3:
С уважением, Edmond
Re[10]: Слишком сложно и к тому-же не верно работает :)
Делаю исправления и поправки на все предыдущие случаи и бросаю рабочий пример.
На этом и камень забиваем!!!
.386
.model flat
option casemap :none ; case sensitive
.data
str1 db 22h,33h,33h,11h,34h,33h,55h,99h
str2 db 33h,44h,77h,22h,34h,77h,44h,88h
.code
_start:
EXTERN __imp__ExitProcess@4:dword
xor eax,eax
mov ecx,0 ;; Вместо mov ecx,eax это стоит за тем, чтобы
;; метка @@loop1 и @@loop2 была выравненной
mov eax, dword ptr str1
xor eax, dword ptr str2
jz @@word2 ;; Редко выполняеться
@@loop1:
test al,al
jz short @@next1 ;; То же не часто..., но под конец частенько
test al,0fh ; Если есть совпадение (ноль),
; значит вторая часть точно не ноль
jz short @@1
; если совпадения нет ( не ноль), значит
inc ecx
test al,0f0h
jz @@next1
@@1: inc ecx
@@next1:
shr eax,8
jnz short @@loop1
@@word2:
mov eax, dword ptr str1+4
xor eax, dword ptr str2+4
; jz short @@end
@@loop2:
test al,al
jz @@next2
test al,0fh ; Если есть совпадение (ноль),
; значит вторая часть точно не ноль
jz @@2
; если совпадения нет ( не ноль), значит
inc ecx
test al,0f0h
jz @@next2
@@2: inc ecx
@@next2:
shr eax,8
jnz short @@loop2
@@end:
push ecx
call __imp__ExitProcess@4
end _start
С уважением, Edmond
Re[11]: Слишком сложно и к тому-же не верно работает :)
Здравствуйте masquer, Вы писали:
M>Здравствуйте Edmond, Вы писали:
E>>Делаю исправления и поправки на все предыдущие случаи и бросаю рабочий пример. E>>На этом и камень забиваем!!!
M>А моя реализация с ММХ (см. посты выше ) не канает?
ХМ... Ну так вообще с MMX своя проблемма.
Но если б ты её немного доканал... то...
С уважением, Edmond
Re[13]: Слишком сложно и к тому-же не верно работает :)
Тестирование провожу следующим образом:
Все фрагменты кода привожу к виду функции с прототипом int __stdcall countDiffs(__int64 i1, __int64 i2) и с запретом inline.
и вызываю её 5 раз для проверки скорости выполнения на разных входных условиях:
Сравниваемые пары:
{0x1234567890123456, 0x1234567890123456}, // Полное совпадение
{0x8234567890123456, 0x1234567890123456}, // Отличае в 1-й цифре
{0x1234567890123459, 0x1234567890123456}, // Отличае в последней цифре
{0x2345678901234591, 0x1234567890123456}, // Нет совпадений
{0x1234568890123456, 0x1234567890123456} // Отличае в середине
Скорость меряю с помощью инструкции RDTSC процессора.
Вот результаты:
1) Эталон для замера накладных расходов:
volatile int Result=0;
int __stdcall DummyCall(__int64 a, __int64 b)
{
return Result;
}
Time=48, Result= 0 — OK
Time=46, Result= 0 — Fail
Time=48, Result= 0 — Fail
Time=46, Result= 0 — Fail
Time=46, Result= 0 — Fail
AvgTime=46.8
2) Вариант реализации "в лоб" на С:
int __stdcall countDiffs(__int64 a, __int64 b)
{
__int64 Temp = a ^ b;
int result = 0;
while (Temp)
{
if (Temp & 0xF)
result ++;
Temp >>= 4;
}
return result;
}
Оказался неудачным, т.к. >> транслируется как sar, а не shr (в отличаи от Delphi, на котором это когда-то работало),
что приводит зацикливанию при различаи в старшей цифре.
3) То-же на Delphi:
function Cmp(a, b: Int64): Integer; stdcall;
var
t: Int64;
begin
t := a xor b;
Result := 0;
while t<>0 do
begin
if t and $F<>0 then
Inc(Result);
t := t shr 4;
end;
end;
Time=79, Result= 0 — OK
Time=324, Result= 1 — OK
Time=95, Result= 1 — OK
Time=288, Result=16 — OK
Time=262, Result= 1 — OK
AvgTime=209.600000
4) пред-последний вариант от Алекс
Time=58, Result= 0 — OK
Time=105, Result= 1 — OK
Time=183, Result= 1 — OK
Time=249, Result=16 — OK
Time=139, Result= 1 — OK
AvgTime=146.800000
в последнем — опять ошибка. ebx, где потом результат накапливается не обнуляется предварительно. И работает он дольше.
5) Мой вариант на asm
Time=85, Result= 0 — OK
Time=172, Result= 1 — OK
Time=85, Result= 1 — OK
Time=234, Result=16 — OK
Time=102, Result= 1 — OK
AvgTime=135.600000
6) Вариант от Chorkov
не работает корректно:
Time=222, Result= 2 — Fail
Time=223, Result= 2 — Fail
Time=222, Result= 4 — Fail
Time=223, Result=10 — Fail
Time=223, Result= 2 — Fail
AvgTime=222.600000
6) Вариант от Edmond
Time=64, Result= 0 — OK
Time=133, Result= 1 — OK
Time=111, Result= 1 — OK
Time=256, Result=16 — OK
Time=73, Result= 1 — OK
AvgTime=127.400000
Что мне понравилось, так это обилие коментариев в коде, достаточное для понимания написанного, и выравнивание
на границу комманд, достигаемое путем выбора предшествующих операций из набора эквивалентных.
Мое почтение мастеру.
Вот и все.
Предлагаю всем поставить максимальный бал победителю в качестве награды.
Здравствуйте Алексей Петров, Вы писали:
АП>Вот и все.
Хотелось бы финальный аккорд на MMX предоставить, сонный был, немного не так задание понял . А если не 5 проходов задать а, скажем. 5 000 000. Можно результаты.
Здравствуйте masquer, Вы писали:
M>Хотелось бы финальный аккорд на MMX предоставить, сонный был, немного не так задание понял . А если не 5 проходов задать а, скажем. 5 000 000. Можно результаты.
Суперски:
Time=74, Result= 0 — OK
Time=72, Result= 1 — OK
Time=74, Result= 1 — OK
Time=72, Result=16 — OK
Time=72, Result= 1 — OK
AvgTime=72.800000
А 5 или 5000000 проходов — значения не имеет — RDTSC хорошая инструкция
Здравствуйте masquer, Вы писали:
M>Здравствуйте Алексей Петров, Вы писали:
АП>>Вот и все. M>Хотелось бы финальный аккорд на MMX предоставить, сонный был, немного не так задание понял . А если не 5 проходов задать а, скажем. 5 000 000. Можно результаты.
А не мог бы ты снабдить комментариями свое творение, чтоб всем стало ясно, как оно работает. MMX-овский набор инструкция мало кто знает
Здравствуйте Алексей Петров, Вы писали:
АП>А не мог бы ты снабдить комментариями свое творение, чтоб всем стало ясно, как оно работает. MMX-овский набор инструкция мало кто знает :-Не верю (с) Станиславский
movq mm0, a ; загружаем сравниваемые числа
movq mm1, mm0
movq mm2, b
movq mm3, mm2
movq mm4, msk0f ; и маски как 64-битные числа
movq mm5, eq1
;--------------------------------------
psllw mm0, 4 ; сдвигаем каждое слово влево на 4 бита
psllw mm2, 4
psrlw mm0, 4 ; сдвигаем каждое слово вправо на 4 бита
psrlw mm2, 4
psrlw mm1, 4 ; опять :)
psrlw mm3, 4
pand mm0, mm4 ; ложим маску
pand mm1, mm4
pand mm2, mm4
pand mm3, mm4
; смысл всего этого сделать из
; mm0=1234567890123456
; mm0=0103050709010305
; mm1=0204060800020406
;--------------------------------------
pcmpeqb mm0,mm2 ; сравниваем побайтно, если байты равны, в mm0 и mm1 каждый
pcmpeqb mm1,mm3 ; соответствующий байт = FF, иначе 00
paddb mm0, mm5 ; добавим 01 к каждому байту, получится 01 если не рывны были
paddb mm1, mm5
paddb mm0, mm1 ; просуммируем полученные "неравности" для 16 mm0=0202020202020202
pxor mm1, mm1 ; обнулим mm1 для следующей команды
psadbw mm0, mm1 ; требует проц не ниже P3, суммирует абсолютные значения разницы
; соответствующих байт и результат помещает в нижнее слово mm0
movd eax, mm0 ; которое мы и сохраняем в eax
emms; для предосторожности чистим стек сопроцессора
Здравствуйте masquer, Вы писали:
M>Здравствуйте Алексей Петров, Вы писали:
АП>>А не мог бы ты снабдить комментариями свое творение, чтоб всем стало ясно, как оно работает. MMX-овский набор инструкция мало кто знает :-Не верю (с) Станиславский M>
M>
M> movq mm0, a ; загружаем сравниваемые числа
M> movq mm1, mm0
M> movq mm2, b
M> movq mm3, mm2
M> movq mm4, msk0f ; и маски как 64-битные числа
M> movq mm5, eq1
M>;--------------------------------------
M> psllw mm0, 4 ; сдвигаем каждое слово влево на 4 бита
M> psllw mm2, 4
M> psrlw mm0, 4 ; сдвигаем каждое слово вправо на 4 бита
M> psrlw mm2, 4
M> psrlw mm1, 4 ; опять :)
M> psrlw mm3, 4
M> pand mm0, mm4 ; ложим маску
M> pand mm1, mm4
M> pand mm2, mm4
M> pand mm3, mm4
M>; смысл всего этого сделать из
M>; mm0=1234567890123456
M>; mm0=0103050709010305
M>; mm1=0204060800020406
M>;--------------------------------------
M> pcmpeqb mm0,mm2 ; сравниваем побайтно, если байты равны, в mm0 и mm1 каждый
M> pcmpeqb mm1,mm3 ; соответствующий байт = FF, иначе 00
M> paddb mm0, mm5 ; добавим 01 к каждому байту, получится 01 если не рывны были
M> paddb mm1, mm5
M> paddb mm0, mm1 ; просуммируем полученные "неравности" для 16 mm0=0202020202020202
M> pxor mm1, mm1 ; обнулим mm1 для следующей команды
M> psadbw mm0, mm1 ; требует проц не ниже P3, суммирует абсолютные значения разницы
M> ; соответствующих байт и результат помещает в нижнее слово mm0
M> movd eax, mm0 ; которое мы и сохраняем в eax
M> emms ; для предосторожности чистим стек сопроцессора
M>
Здравствуйте masquer, Вы писали:
M>Здравствуйте Алексей Петров, Вы писали:
АП>>А не мог бы ты снабдить комментариями свое творение, чтоб всем стало ясно, как оно работает. MMX-овский набор инструкция мало кто знает :-Не верю (с) Станиславский M>
Здравствуйте Edmond, Вы писали:
E>Ну вот, теперь конеает!!! E>Мои поздравления
Спасибо! Побольше бы таких конкурсов, интересно и проблему решить и для самообразования полезно.
Добавлю для тех, кто на МАСМ32 пишет (как я), что директиву .MMX на .XMM заменить нужно, иначе ругаться на инструкцию psadbw будет
Re[7]: Подведем итоги?
От:
Аноним
Дата:
23.07.02 08:24
Оценка:
Здравствуйте masquer, Вы писали:
M>Спасибо! Побольше бы таких конкурсов, интересно и проблему решить и для самообразования полезно.
А вместо спасибо (или вместе) здесь принято оценки ставить
Здравствуйте Аноним, Вы писали:
А>Здравствуйте masquer, Вы писали:
M>>Спасибо! Побольше бы таких конкурсов, интересно и проблему решить и для самообразования полезно. А>А вместо спасибо (или вместе) здесь принято оценки ставить
Уже
добавлю сам себя. Если выравнять стек перед вызовом функции Diff (я свой код имею ввиду) на границу 8 байт, например так
sub esp, 8
and esp, 0fffffff0h
add esp, 8
то скорость где-то на 10-15% возрастет за счет попадания в кеш