t00x, благодарю. С Код (Text): mov r9, r10 neg r9 что-то ступил - можно было вынести за цикл. Не-а, есть . См. [С. И. Ожегов Словарь русского языка], стр. 819 (в 21-ом издании).
Вот собсна. Протестировать не могу, т.к. IA32 Код (Text): xor rdx, rdx mov rax, [Y] xor rbx, rbx mov rsi, [X] xor rcx, rcx inc rcx mov rdi, rcx .loop1: bt rax, 0 adc rbx, rbx jp .l2 neg rsi add rdx, rsi .l2: and rbx, rcx sar rdx, cl rcr rax, cl add rdi, rdi jnz .loop1 mov [L], rdx mov [R], rax
KeSqueer, спасибо, но Ваш код в 1,6 раз медленнее, чем вариант от t00x. Думаю, что виноваты инструкции bt и rcr.
AssemblerIA64 Осторожнее с ней, я уже подкололся. На C2D она порой неадекватно себя ведет из-за его всяких ноутбучно-энергосберегающих приколов. Такты системной шины, которые меряет RDTSC, в нем не совпадают с тактами ядра. Померять такты ядра можно с уверенностью только через счетчики производительности, но кто сказал, что это легко
AssemblerIA64 Ох... это действительно нелегко, и очень много рассказывать... я использовал перековерканый код of Agner Fog. Вот отсюда:http://www.agner.org/optimize/#testp вместо RDTSC нужен fixed-function PMC 0x40000001. Но его надо еще сначала разрешить. Фог делает это через драйвер, ибо нужно выполнять привелигированые инструкции WRMSR. И, что особенно плохо, код получается только для одного процессора... А померять потом на K8, скажем, надо изваращаться... я когда-то одну фигню делал, хотел погонять на разных процах, тестировал на C2D, K8X2, P4E, но бросил на полдороги, некогда...
AssemblerIA64 ещё есть мысль написать через bsf и btc: Код (Text): ... mov rdx, rax shl rdx, 1 xor rdx, rax ; rdx = переход битов (0-1-0) @0: bsf rdx, rbx btc rax, rbx ... test rdx, rdx jnz @0 ... но bsf+btc будет медленнее.
AssemblerIA64 Не верю! Но буду пытаться убыстрить)) t00x О, нет! Только не bsf btc и всякое подобное! Ну, и раз уж представился случай: 1) shl rdx, 1 лучше на add rdx, rdx afaik 2) xor eax, edx портит eax. Я бы заменил на cmp eax, edx. В данном случае регистр rcx окзаывается не занятым 3) кстати, как это rcx пользуется под хранение данных и под счетчик цикла? 4) если юзать xor r15, r15 в начале цикла, r12 остается свободным вроде все, что хотел отметить [added] ЗЫ Не ожидал, что rcr НАСТОЛЬКО тормозная. При замене sar/rcr на shrd/shr скорость возрастает в 3(!) раза Код (Text): mov rax, [Y] xor rdx, rdx xor rbx, ebx mov rsi, [X] xor rcx, rcx inc rcx mov rdi, rcx .loop1: bt rax, 0 adc rbx, ebx jp .l2 neg rsi add rdx, rsi .l2: shrd rax, rdx, 1 and rbx, rcx sar rdx, 1 add rdi, rdi jnz .loop1 mov [L], rdx mov [R], rax
Мм, а не проще просто по-дольше гонять алгоритмы и сравнивать потом дельта т начала и конца, здесь статистика уже работает и никакие "приколы" результат не испортят...
Novi4ek Приколы там жестокие, я попался на выводе промежуточных значений времени, похоже этого достаточно, чтобы проц успел воткнуть C1E. И я как дурак трахался, пытаясь понять, почему не выжимается теоретическая производительность. Забавно, но под вистой были другие результаты - ближе к правильным.
AssemblerIA64 На вопрос можно ли сделать умножение еще быстрее -- отвечаю можно Извините AMD64 нет, написано под IA32, умножаем байт на байт, но так нагляднее Дело в том что вы анализируете два последних бита множимого Х и каждый раз сдвигаете Х на 1 разряд X(i) X(i-1) 0 0 - делаем сдвиг Х на 1 разряд влево 0 1 - складываем Х с частичным произведением и делаем сдвиг Х на 1 разряд влево 1 0 - вычитаем Х из частичного произведения и делаем сдвиг Х на 1 разряд влево 1 1 - делаем сдвиг Х на 1 разряд влево При использовании алгоритма Бута операции сложения и вычитания требуются только когда во множимом не совпадают значения в соседних разрядах, то есть, имеется переход от 0 к 1 или от 1 к 0. можно уменьшить время умножения в два раза если анализировать по три разряда и сдвигать множимое на два разряда. Обозначим вычитание S, сложение -A, сдвиг на 1 разряд влево - L X(i+1)|X(i)| X(i-1)| действие| коментарий 0 | 0 | 0 | LL | сдвиг Х на 2 разряда влево 0 | 0 | 1 | ALL | переход в 0-ом разряде из 1 в 0, что соответствует +1 0 | 1 | 0 | ALL | переход в 0-ом разряде из 0 в 1, что соответствует -1 и переход в 1-ом разряде из 1 в 0, что соответствует +2, итого +2-1=+1 0 | 1 | 1 | LAL | переход в 1-ом разряде из 1 в 0, что соответствует +2 1 | 0 | 0 | LSL | переход в 1-ом разряде из 0 в 1, что соответствует -2 1 | 0 | 1 | SLL | переход в 0-ом разряде из 1 в 0, что соответствует +1 и переход в 1-ом разряде из 0 в 1, что соответствует -2, итого -2+1=-1 1 | 1 | 0 | SLL | переход в 0-ом разряде из 0 в 1, что соответствует -1 1 | 1 | 1 | LL | делаем сдвиг Х на 2 разряда влево Код (Text): xor ebx,ebx ;очистить место под индекс xor eax,eax ;очистить старшую часть множителя cdq ;и место под результат mov dl,Y ;занести множитель в регистр DL test edx,edx ;Y=0? jz a2 ;если да -- выходим mov ecx,5;установить счетчик числа разрядов - за один раз обрабатываем 2 разряда shl X,1 ;умножаем X на 2, чтобы получить младший бит X(-1)=0 a0: mov bx,X ;формируем индекс в таблице переходов and bx,7 ;по маске выбираем X(i-1), X(i) и X(i+1) shr X,2 ;переходим к 2 следующим разрядам jmp table[ebx*4];ищем переход в таблице sll: sub eax,edx ;вычесть множимое jmp ll ;и сделать двойной сдвиг влево lsl: shl edx,1 ;сделать одинарный сдвиг влево sub eax,edx ;вычесть множимое jmp a1 ;сделать еще один одинарный сдвиг влево и переход в начало цикла lal: shl edx,1 ;сделать одинарный сдвиг влево add eax,edx ;прибавить множимое a1: shl edx,1 ;и сделать еще один одинарный сдвиг влево jmp a3 ;пока не обработаем все 9 разрядов all: add eax,edx ;прибавить множимое ll: shl edx,2 ;делаем двойной сдвиг влево a3: sub ecx,1 jnz a0 ;переход в начало цикла, пока не обработаем все 9 разрядов a2: push eax ;результат 248*255=63240 push offset format push offset buffer call _imp__wsprintfA pop ecx;корректируем стек pop ecx pop ecx push 0 push offset caption;заголовок push offset buffer;текст push 0 call _imp__MessageBoxA@16 retn X dw 255 ;множимое Y db 248 ;множитель table dd ll,all,all,lal,lsl,sll,sll,ll format db '%08u',0 buffer db 25 dup (0) caption db 'Booth',0 end start ;X(i+1) X(i) X(i-1) ;0 0 0 L,L ;0 0 1 +1 A,L,L ;0 1 0 +1=+2-1 A,L,L ;0 1 1 +2 L,A,L ;1 0 0 -2 L,S,L ;1 0 1 -1=-2+1 S,L,L ;1 1 0 -1 S,L,L ;1 1 1 L,L to be continue
А теперь обрабатываем по 3 разряда и уменьшаем время обработки в 1,5 раза Код (Text): xor ebx,ebx ;очистить место под индекс xor eax,eax ;очистить старшую часть множителя cdq ;и место под результат mov dl,Y ;занести множитель в регистр DL test edx,edx ;Y=0? jz a2 ;если да -- выходим mov ecx,3;установить счетчик числа разрядов - за один раз обрабатываем 3 разряда shl X,1 ;умножаем X на 2, чтобы получить младший бит X(-1)=0 a0: mov bx,X ;формируем индекс в таблице переходов and bx,0Fh ;по маске выбираем X(i+2),X(i+1),X(i) и X(i-1) shr X,3 ;переходим к 3 следующим разрядам jmp table[ebx*4];ищем переход в таблице llsl: shl edx,2 sub eax,edx jmp a4 llal: shl edx,2 add eax,edx a4: shl edx,1 jmp a1 aall: add eax,edx shl edx,1 add eax,edx jmp a1 ssll: sub eax,edx shl edx,1 sub eax,edx jmp a1 slll: sub eax,edx ;вычесть множимое jmp lll ;и сделать тройной сдвиг влево lsll: shl edx,1 ;сделать одинарный сдвиг влево sub eax,edx ;вычесть множимое jmp a1 lall: shl edx,1 add eax,edx a1: shl edx,2 jmp a3 alll: add eax,edx lll: shl edx,3 a3: sub ecx,1 jnz a0 ;переход в начало цикла, пока не обработаем все 9 разрядов a2:; в eax результат 248*255=63240 . . . . X dw 255 ;множимое Y db 248 ;множитель table dd lll,alll,alll,lall,lall,aall,aall,llal,llsl,ssll,ssll,lsll,lsll,slll,slll,lll ;X(i+2) X(i+1) X(i) X(i-1) ;0 0 0 0 L,L,L ;0 0 0 1 +1 A,L,L,L ;0 0 1 0 +1=+2-1 A,L,L,L ;0 0 1 1 +2 L,A,L,L ;0 1 0 0 +2=+4-2 L,A,L,L ;0 1 0 1 +3=+4-2+1 A,A,L,L ;0 1 1 0 +3=+4-1 A,A,L,L ;0 1 1 1 +4 L,L,A,L ;1 0 0 0 -4 L,L,S,L ;1 0 0 1 -3=-4+1 S,S,L,L ;1 0 1 0 -3=-4+2-1 S,S,L,L ;1 0 1 1 -2=-4-2 L,S,L,L ;1 1 0 0 -2 L,S,L,L ;1 1 0 1 -1=-2+1 S,L,L,L ;1 1 1 0 -1 S,L,L,L ;1 1 1 1 L,L,L если далее по аналогии 4, 5 и т.д. наблюдаем раздувание таблицы переходов -- теоретический предел это умножение по таблице Ссылки на алгоритм Booth Booth's multiplication algorithm Radix-4 Booth Encoding Radix-8 Booth Encoding to be continue
C целью уменьшения времени можно отказаться от переходов по таблице (обращение к памяти) Если внимательно посмотреть на таблицу, видно, что она симметрична ;X(i+2)|X(i+1)|X(i)|X(i-1)|действие ;0 0 0 0 L,L,L ;0 0 0 1 +1 A,L,L,L ;0 0 1 0 +1=+2-1 A,L,L,L ;0 0 1 1 +2 L,A,L,L ;0 1 0 0 +2=+4-2 L,A,L,L ;0 1 0 1 +3=+4-2+1 A,A,L,L ;0 1 1 0 +3=+4-1 A,A,L,L ;0 1 1 1 +4 L,L,A,L ;----------------ось симметрии----------------------------- ;1 0 0 0 -4 L,L,S,L ;1 0 0 1 -3=-4+1 S,S,L,L ;1 0 1 0 -3=-4+2-1 S,S,L,L ;1 0 1 1 -2=-4-2 L,S,L,L ;1 1 0 0 -2 L,S,L,L ;1 1 0 1 -1=-2+1 S,L,L,L ;1 1 1 0 -1 S,L,L,L ;1 1 1 1 L,L,L там где X(i+2)=1 заменяем вычитание на сложение, а X(i+1), X(i) и X(i-1) инверируем ;X(i+1)|X(i)|X(i-1)|действие|ebx|(ebx+1)/2 ;0 0 0 0 0 0 ;0 0 1 +1 1 1 ;0 1 0 +1 2 1 ;0 1 1 +2 3 2 ;1 0 0 +2 4 2 ;1 0 1 +3 5 3 ;1 1 0 +3 6 3 ;1 1 1 +4 7 4 видно, что коэффициент равен (ebx+1)/2 Код (Text): xor ebx,ebx ;очистить место под индекс xor eax,eax ;очистить место под результат cdq ;и старшую часть множителя mov dl,Y ;занести множитель в регистр DL test edx,edx ;Y=0? jz a2 ;если да -- выходим mov ecx,3;установить счетчик числа разрядов - за один раз обрабатываем 3 разряда shl X,1 ;умножаем X на 2, чтобы получить младший бит X(-1)=0 a0: mov bx,X ;формируем индекс для переходов mov esi,edx shl edx,3 ;переходим к 3 следующим разрядам test ebx,1000b ;узнаем в какой половине таблицы находимся jz a1 ;если X(i+2) равно 1 neg esi ;заменим вычитание на сложение not ebx ;используем симметричность таблицы a1: and ebx,111b ;по маске выбираем X(i+1),X(i) и X(i-1) shr X,3 ;переходим к 3 следующим разрядам test ebx,ebx ; либо все единицы, либо все нули jz a3 add ebx,1 shr ebx,1 cmp ebx,2 ;сравниваем с серединой диаппазона jb a5 ;ebx=1 jz a4 ;ebx=2 cmp ebx,3 jnz a6 ;ebx=4 add eax,esi a4: add esi,esi jmp a5 a6: shl esi,2 a5: add eax,esi a3: sub ecx,1 jnz a0 ;переход в начало цикла, пока не обработаем все 9 разрядов a2: ;результат в eax 248*255=63240 . . . . X dw 255 ;множимое Y db 248 ;множитель В зависимости от задачи можно выбрать оптимальное количество разрядов X(i+n),...,X(i-1) а цикл развернуть.
Mikl___ без сложных переходов специально для P4 Код (Text): xor ecx,ecx ;очистить место под индекс xor eax,eax ;очистить место под результат cdq ; всегда=0 mov edi,[Y] ;занести множитель в регистр edi test edi,edi ;Y=0? jz a2 ;если да -- выходим mov ebx,3;установить счетчик числа разрядов - за один раз обрабатываем 3 разряда shl [X],1 ;умножаем X на 2, чтобы получить младший бит X(-1)=0 a0: mov ecx,[X] ;формируем индекс для переходов mov esi,edi shl edi,3 ;переходим к 3 следующим разрядам test ecx,1000b ;узнаем в какой половине таблицы находимся jz a1 ;если X(i+2) равно 1 neg esi ;заменим вычитание на сложение not ecx ;используем симметричность таблицы a1: shr [X],3 ;переходим к 3 следующим разрядам mov ebp,esi ;сохраняем esi and ecx,111b ;по маске выбираем X(i+1),X(i) и X(i-1) add cl,1 shr cl,1 cmovz esi, edx add eax, esi ;*1 mov esi, ebp sub cl, 1 cmovz esi, edx add eax, esi ;*2 mov esi, ebp sub cl, 1 cmovz esi, edx add eax, esi ;*3 mov esi, ebp sub cl, 1 cmovz esi, edx add eax, esi ;*4 sub ebx,1 jnz a0 ;переход в начало цикла, пока не обработаем все 9 разрядов a2: ;результат в eax 248*255=63240 P.S. и X, Y dd
t00x мелкое замечание -- если результат в eax -- тогда X и Y максимум -- dw, либо X -- 3 байта тогда Y db, а CMOVcc, если мне не изменяет память были уже в Pentium Pro. А за подсказку спасибо! И всетаки, если коэффицент = 4 мне придется делать четыре сравнения и четыре сложения -- в моем варианте было два сравнения и один сдиг