Это будет примерно так. Код (Text): align 16 ab rq 2 ij rq 2 mask dd 0,-1,0,-1 prime dq prime,prime ;<-Сюда подставишь числа prime2 dq 2^32-Prime,2^32-Prime ;<- ... movaps xmm0,[ab] movaps xmm1,xmm0 psrldq xmm1,32 pmuludq xmm0,xmm1 @loop: movaps xmm1,xmm0 andps xmm1,[mask] psrldq xmm0,32 pmuludq xmm0,[prime2] paddq xmm0,xmm1 pmovmskb eax,xmm0 test eax,1111000011110000b jnz @loop movaps xmm1,xmm0 pcmpgtd xmm1,[prime] andps xmm1,[prime] psubq xmm0,xmm1 movaps [ij],xmm0 Проверить не могу т.к. дома нет SSE2.
ух ты, обалденная крутизна! Мне бы биться головой об стенку целую неделю над этим... А нельзя ли пояснить только самую малость, самую творческую часть, так сказать. Как происходит выход из цикла и и команде movmsk. Понятно, что редуцируя сразу два числа, можно выйти на ситуацию когда одно число уже кончило а другое еще не отстрелялось, как узнать что старшие субполовинки обоих половин регистра уже обнулились?
Ну вот Код (Text): movaps xmm0,[ab] movaps xmm1,xmm0 andps xmm0,[mask] ;выделяем младшие 32 бита у каждого из чисел psrldq xmm1,32 ;выделяем старшие 32 бита у каждого из чисел pmuludq xmm0,xmm1 ;умножаем младшую часть на старшую @loop: movaps xmm1,xmm0 andps xmm1,[mask] ;выделяем младшие 32 бита у каждого из чисел psrldq xmm0,32 ;выделяем старшие 32 бита у каждого из чисел pmuludq xmm0,[prime2] ;умножаем старшую часть на 2^32-prime paddq xmm0,xmm1 ;прибавляем младшую часть pcmpeqd xmm1,xmm0 ;сравниваем старшую часть полученого числа с 0 ;если да - в старших 32 битах будет маска из единиц ;иначе там будет ноль movmskd eax,xmm1 ;копируем старшие биты в eax - получается 2-битовая маска cmp al,11 ;если старшие 32 бита обоих чисел равны 0 - завершаем цикл jnz @loop movaps xmm1,xmm0 pcmpgtd xmm1,[prime] ;если число больше prime andps xmm1,[prime] ;вычитаем изнего prime psubq xmm0,xmm1 ;иначе вычитаем ноль movaps [ij],xmm0 ;сохраняем результат Это не имеет значения. Если число уже "отстрелялось" дальнейшие действия не изменят его.
Это верно, я имел в виду чтобы не выйти раньше времени из цикла не дай бог, поскоку оба числа могут отстреляться не одновременно. сам по себе pmul не боиться мусора в старших разрядах, если только не преследуется иных целей, специально очищать не нужно. если равно то тоже не мешало бы вычесть и получить ноль. pcmpgtd отрабатывает такую ситуацию?
murder А как считаете, если вместо pmul вставить (d shl 20) + prime - d, оно быстрее станет? С SSE2 ситуация может быть другой чем с shld eax,edx,20
Да, похоже, что очищать старшую часть перед умножением не обязательно. Тогда видимо так Код (Text): movaps xmm1,[prime] pcmpgtd xmm1,xmm0 pandn xmm1,[prime] psubq xmm0,xmm1 То есть так? Код (Text): @loop: movaps xmm1,xmm0 pslldq xmm0,20 paddq xmm0,[prime] psubq xmm0,xmm1 xorps xmm1,xmm1 pcmpeqd xmm1,xmm0 movmskd eax,xmm1 cmp al,11 jnz @loop
Да не совсем так... Нужно тщательно выделять половинки, но двигать сначала 32 вправо а потом 20 влево большого смысла нет, видимо можно двинуть xmm0 на 12 вправо и очистить 20 младших бит через and. Но есть одна бяка, когда d=0, то будет прибавляться чистый prime а это плохо.
Для сравнения больше или равно применяется pcmpgtd+pandn. В SSE присутствует инструкция cmpnltps, но она работает с вещественными числами. SSE2 - это просто расширенный MMX. Я тоже об этом подумал. Тогда лучше оставить умножение. Что-то Mikl___ сюда не заглядывает. Отправь ему сообщение в личку.
Угу, так и сделаю... Уж больно с шифтами много гимора. Надеюсь PMUL тормозить не будет плюс досрочный выход если много ведущих нулей надеюсь там реализован в железе. Уорен загадил половину своей книжки поносом в виде занимательной математики, а того что нужно - модулярной арифметики там вроде совсем нет, а ведь это требует множества интересных трюков. еще часто используют остатки при вычислении хешей=ключей в базах данных. Читаем: Number of tables per database 2^32 - 2^20 - 1 = 4293918719 Похожее число, но там прощще - d*2^20 + d, проблемы с нулем нет.
murder, похоже наш код работать не будет, так как pcmpgtd производит знаковое сравнение. У нее есть вариант для квадров или для даблов без знака? Второе было бы еще лучше... Придумал! Инвертировать старшие биты перед сравнением... Елыпалы, опять гимморр...
Извините за молчание, у меня вот такой вариант Код (Text): .686P .model flat include windows.inc include masm32.inc includelib user32.lib includelib masm32.lib extern _imp__wsprintfA:dword extern _imp__MessageBoxA@16:dword .code start: mov edx,dword ptr q+4 mov eax,dword ptr q test edx,edx jnz a1 ;если edx = 0 и eax < 0FFF00001 тогда остаток = eax ;если edx = 0 и eax >= 0FFF00001 тогда остаток = eax - 0FFF00001 sub eax,0FFF00001h setnc dl;<-- это только для сравнения с div вообще-то не нужно push edx;<-- это только для сравнения с div вообще-то не нужно sbb esi,esi and esi,0FFF00001h add eax,esi jmp a2 ;если edx > 0 ищем частное через умножение на 1001h и сдвиг вправо на 44 бита ;100000000h/FFF00001h=1,0002442000112558721159827002697=1001h/1000h a1: mov ecx,edx shld ecx,eax,12 add ecx,edx shr ecx,12 push ecx;<-- это только для сравнения с div вообще-то не нужно ;частное в ecx ;чтобы получить остаток умножаем ecx на 0FFF00001h и отнимаем от q ;ecx*0FFF00001=ecx*2^32 - ecx*2^20 + ecx sub eax,ecx shl ecx,20 add eax,ecx a2: push eax mov edx,dword ptr q+4 mov eax,dword ptr q mov ecx,0FFF00001h div ecx push eax push edx push offset format push offset buffer call _imp__wsprintfA add esp,4*6;корректируем стек push MB_OK push offset MsgCaption push offset buffer push 0 call _imp__MessageBoxA@16 ret MsgCaption db "persicum",0 buffer db 20 dup (0) q dq ?;<--подставляем любое число из диаппазона от 0 до 0F.FFFF.FFFF.FFFFh для проверки format db 'остаток через div %08Xh',0Ah,'частное через div %08Xh',0Ah db '------------------------',0Ah db 'остаток через сдвиги %08Xh',0Ah,'частное через сдвиги %08Xh',0 end start К сожалению, без ошибок работает, если q в диапазоне от 0 до 4503599627370495. Для распространения на весь 64-битный диапазон, может быть, murder, leo или diamond помогут...
murder, спасиьо за поддержку, числодробилку ака имитацию сдвигового регистра отладил, все работает хорошо, напрягает только необходимость дополнительных шагов по инверсии старших битов для команды pcmdgtd, поскоку она знаковая. Не ожидал такой глупости. В общем , прогоняю мельницу по очереди два раза, потом объединяю все в один xmm и добиваю остаток. На заценку варианта Майкла___ уже потенции не осталось, еще нужно много работы чтобы интегрировать наш SSE2 код ко мне в прогу
Можно ещё так Код (Text): value rq 1 cw rw 1 rdivider dq 1/4293918721.0 divider dq 4293918721.0 ;0FFF00001h ... fstcw [cw] or [cw],111100000000b ;точность 64 бита, округление с отбрасыванием дробной части fldcw [cw] ... fild [value] fmul [rdivider] mov eax,dword[value] frndint fmul [divider] fistp [value] sub eax,dword[value]
Замена деления 64-битного числа на 0xFFF00001 умножениями для полного диапазона выглядит так: умножение на 0x1001000FF0FE0FD11 и сдвиг вправо на 96. Если считать, что частное влезает в 32 бита (а если делится число, меньшее 0xFFF00001^2, то частное заведомо влезает), и расписать по методу Карацубы, то один div можно заменить на следующий эквивалентный код: Код (Text): mov edx, [q+4] mov eax, [q] ; edx:eax mod 0xFFF00001 -> edi mov ebp, edx mov edi, eax mul [const1] mov ecx, eax mov esi, edx mov eax, edi add eax, ebp sbb ebx, ebx mul [const2] and ebx, [const2] add edx, ebx sub eax, ecx sbb edx, esi add eax, esi adc edx, 0 mov ecx, eax mov esi, edx mov eax, ebp mul [const3] sub ecx, eax sbb esi, edx add eax, esi adc edx, 0 add eax, edi adc edx, ebp ; edx = частное sub edi, edx shl edx, 20 add edi, edx ; edi = остаток ... ; используемые константы align 4 const1 dd 0FE0FD11h const2 dd 1000FFh + 0FE0FD11h const3 dd 1000FFh
persicum Еще один вариант -- считает правильно в диаппазоне от 0 до 0EFFF.FFFF.FFFF.FFFFh, обходимся без умножения одними сложениями и вычитаниями (на этот вариант потенции должно хватить ) Код (Text): start: mov edx,[q+4] mov eax,[q] test edx,edx jnz a1 ;если edx = 0 и eax < 0xFFF00001, тогда остаток = eax ;если edx = 0 и eax >= 0xFFF00001, тогда остаток = eax - 0xFFF00001 sub eax,0FFF00001h setnc dl;<-- это только для сравнения с div, вообще-то не нужно push edx;<-- это только для сравнения с div, вообще-то не нужно sbb esi,esi and esi,0FFF00001h add eax,esi jmp a2 ;если edx > 0 ищем частное через умножение на 1001001h ;100000000h/FFF00001h=1,0002442000112558721159827002697=1001001h/1000000h a1: mov ecx,edx mov esi,eax mov ebx,eax shrd esi,edx,12 shr edx,12 add ebx,esi adc ecx,edx shrd esi,edx,12 shr edx,12 add ebx,esi adc ecx,edx push ecx ;частное в ecx ;чтобы получить остаток умножаем ecx на 0FFF00001h и отнимаем от q ;ecx*0FFF00001=ecx*2^32 - ecx*2^20 + ecx sub eax,ecx shl ecx,20 add eax,ecx a2: push eax;<-- это только для сравнения с div, вообще-то не нужно mov edx,[q+4] mov eax,[q] mov ecx,0FFF00001h div ecx push eax push edx push offset format push offset buffer call _imp__wsprintfA add esp,4*6;корректируем стек push MB_OK push offset MsgCaption push offset buffer push 0 call _imp__MessageBoxA@16 . . . MsgCaption db "persicum",0 buffer db 20 dup (0) q dq ?;<--подставляем любое число из диаппазона от 0 до 0EFFFFFFFFFFFFFFFh для проверки format db 'остаток через div %08Xh',0Ah,'частное через div %08Xh',0Ah db '------------------------',0Ah db 'остаток через сдвиги %08Xh',0Ah,'частное через сдвиги %08Xh',0
persicum Третий и надеюсь окончательный вариант -- считаем в диаппазоне от 0 до 0FFF00000FFFFFFFF, если содержимое edx больше или равно 0FFF00001h вычисление остатка через деление на FFF00001h должно прерывать программу через переполнение Код (Text): mov edx,[q+4] mov eax,[q] test edx,edx;<-- этот кусок до метки a1 можно убрать, просто вычислений меньше jnz a1; если edx равен нулю ;если edx = 0 и eax < 0FFF00001 тогда остаток = eax ;если edx = 0 и eax >= 0FFF00001 тогда остаток = eax - 0FFF00001 sub eax,0FFF00001h sbb esi,esi and esi,0FFF00001h add eax,esi jmp a2; в eax остаток ;если edx > 0 ищем частное через умножение на 1001h и сдвиг вправо на 44 бита ;100000000h/FFF00001h=1,0002442000112558721159827002697=1001001h/1000000h a1: mov ecx,edx cmp edx,0F0000000h sbb edi,edi; придется компенсировать -1 not edi; так как умножаем на 1,001001h, а не на 1,001000FF0FE0FD11h mov esi,eax mov ebx,eax shrd esi,edx,12 shr edx,12 add ebx,esi adc ecx,edx shrd esi,edx,12 shr edx,12 add ebx,esi adc ecx,edx add ecx,edi; компенсация равна 0 или -1, если edx больше или равно 0F0000000h ;частное в ecx, чтобы получить остаток умножаем ecx на 0FFF00001h и отнимаем от q ;ecx*0FFF00001=ecx*2^32 - ecx*2^20 + ecx sub eax,ecx shl ecx,20 add eax,ecx; в eax остаток a2:
Ну этого явно маловато будет =))) Процедура должна уметь редуцировать цисла до FFE0 0100 0000 0000 хотя бы, т.е. (p-1)^2. Иначе она практически бесполезна А зачем козе баян? Процедура поста №27 работает как в АЛУшном варианте, так и практически лоб-в-лоб в SSE2 варианте. Требует только три укороченных умножения, в редчайших случаях четыре. И ващще, деление - это только один из методов получения остатка, самый наивный. Но есть и другие - например сумма цифр числа в десятичной системе есть остаток от деления на 9. Мне то частное совсем не нужно, а тока остаток и нужен. Кто работал с RSA тот знает что это такое, хотя 32-бит модуль будет коротковат для RSA, но хорош для других целей =))) Mikl___ А нельзя ли провести замер скорости кода Вашего кода и из поста №27?
сейчас диапазон от 0 до 0FFF00000FFFFFFFFh, так же как и через div, иначе div даст выход по int0, а замер скорости, все-таки корректнее делать вам самому, я же не знаю конечной цели и оборудование, на котром будет работать находится на вашей стороне