А для суммы двух регистров по модулю нельзя ли код изобразить? Что то у меня получается что нужно маскировать два раза...
Может быть так Код (Text): x dd ? y dd ? .... movq mm1,qword[x] movq mm0,qword[x] ;mm0 = y | x psrad mm1,31 pxor mm0,mm1 psubd mm0,mm1 ;mm0 = abs(y) | abs(x) pshufw mm1,mm0,1110b ;mm1 = abs(y) paddd mm0,mm1 ;mm0 = abs(x)+abs(y) movd eax,mm0 ;eax - ответ А лучше весь алгоритм переписать на MMX
По любому я буду писать код в трех вариантах, для ALU, MMX и SSE2. Еще наверное круче смешивать MMX и SSE2, но это я не умею и не практикую. Не совсем уверен, то ли это что нужно... Это я опять про голимые остатки. Типа Код (Text): add eax, edx jnc .skip1 sub eax, F3000001 .skip1 cmp eax, F3000001 jb .skip2 sub eax, F3000001 .skip2 Пока юзаю этот наивный код, потом буду чистить от JMP
Может преобразовать этот код с применением метода дихотомии (сложность O(log2n))? Но в таком случае будут применяться большие таблицы (размер таблицы зависит от максимальной возможной суммы).
Mikl___ Прошу прошения, F3000001 конечно допускает упрощенное взятие остатка, но не до такой степени просто, как FFF00001. Теперь я работаю по модулю именно FFF00001. Миллион точек должно хватить для FFT. Есть еще второе число на 32 бит в запасе подобной Мерсено-Ферматной структуры с тремя весами. Вот и задачка вырисовывается - мгновенное взятие остатка (по модулю) FFF00001. Причем DIV и MUL точно не нужны, можно видимо на одних шифтах.
Я подозреваю что все эти асмерские шняги пригодны главным образом только для стандартных типов, а если нужно поделить 64-бит на 32-бит то лучше DIVа ничего не придумаешь? А самый лучший способ избавиться от делений это считать по модулю чисел Ферма, Мерсена и их обобщений. Никаких тебе делений и даже умножений не нужно в этом случае. Ну што там у нас с FFF00001 ?
Mikl___, murder, ку-ку, where you at? На данный момент я нашел следующую неуклюжую конструкцию Код (Text): mul edx @loop: mov ecx,eax mov eax,2^32-Prime mul edx add eax,ecx adc edx,0 jnz @loop sub eax,Prime sbb edx,edx and edx,Prime add eax,edx С шифтами мне не удалось получить хороших результатов, шифты очень ценятся при разработке микросхем, а на PC и MUL сам по себе весьма неплох. Так вот, это код работает в 1.7 раза быстрее взятия остатка через один-единственный DIV. Это просто невероятно, до чего же DIV чудовищно-тормозная команда!
persicum Если честно - ничего не понял. Это что? Взятие остатка от деления на константу для 64-битных чисел? Попробуй так Код (Text): fild [divider] fild [value] fprem fistp [reminder] fstp st(0) AMD пишет, что fprem занимает 9+e+n тактов. Что бы это могло значить?
Ага, остаток от деления 64-bit на константу 32-bit, coвершенно верно. Может быть полезно для криптографии и кодирования. Поскоку я в плавучке х87 совсем не соображаю, то можно плиз полный код на входе edx, eax, на выходе еах? Чтоб в память не лазить? А я тогда побенчу. На выходе в eax остаток от деления произведения edx*eax на константу
С FPU это невозможно Все действия через память. В том коде divider - делитель value - делимое reminder - остаток
murder А не лажа ли этот fprem? 64 раза вычитает а потом отваливается... Это только частичный остаток.
Развивая идею из поста №24 написал такое Код (Text): var divider: array [0..32] of int64=($FFF00001, $1FFE00002, $3FFC00004, $7FF800008, $FFF000010, $1FFE000020, $3FFC000040, $7FF8000080, $FFF0000100, $1FFE0000200, $3FFC0000400, $7FF80000800, $FFF00001000, $1FFE00002000, $3FFC00004000, $7FF800008000, $FFF000010000, $1FFE000020000, $3FFC000040000, $7FF8000080000, $FFF0000100000, $1FFE0000200000, $3FFC0000400000, $7FF80000800000, $FFF00001000000, $1FFE00002000000, $3FFC00004000000, $7FF800008000000, $FFF000010000000, $1FFE000020000000, $3FFC000040000000, $7FF8000080000000, $FFF0000100000000); value: int64; i: dword; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin randomize; value:=random(5454545121798); asm mov edx,dword[value+4] xor ecx,ecx bsr ecx,edx sete dl inc ecx sub cl,dl mov eax,dword[value] mov edx,dword[value+4] @1: sub eax,dword[divider+ecx*8] sbb edx,dword[divider+ecx*8+4] jnl @1 add eax,dword[divider+ecx*8] adc edx,dword[divider+ecx*8+4] dec ecx jnl @1 mov i,eax end; form1.Caption:=inttostr(i)+'='+inttostr(value mod $FFF00001); end;
То есть ты предлагаешь вызывать fprem или просто вычитать это дело 32 раза, заглядывая в таьлицу 32 раза? Можно сделать с двумя таблами на 64 К входов и заглядывать в них всего по разу, но не факт что это быстрее будет умножений в цикле, как у меня. Замеры можно?
и еще, задачка не в том чтобы сымитировать DIV на машине где его нету, задача в том чтобы обогнать его там где он есть и хорошо работает. Табличные методы универсальны и пригодны для любой константы, а тут у нас редукция идет по формуле d*2^32 + a = a + d*2^20 - d
Сложность алгоритма O(log2n) Вот функция взятия 64-битного остатка от деления из AMD Code Optimization guide Код (Text): ;[ESP+8]:[ESP+4] делимое ;[ESP+16]:[ESP+12] делитель ;EDX:EAX остаток _ullrem: PUSH EBX ;save EBX as per calling convention MOV ECX, [ESP+20] ;divisor_hi MOV EBX, [ESP+16] ;divisor_lo MOV EDX, [ESP+12] ;dividend_hi MOV EAX, [ESP+8] ;dividend_lo TEST ECX, ECX ;divisor > 2^32–1? JNZ .r_big_divisor ;yes, divisor > 32^32–1 CMP EDX, EBX ;only one division needed? (ECX = 0) JAE @f ;need two divisions DIV EBX ;EAX = quotient_lo MOV EAX, EDX ;EAX = remainder_lo MOV EDX, ECX ;EDX = remainder_hi = 0 POP EBX ;restore EBX as per calling convention RET @@: MOV ECX, EAX ;save dividend_lo in ECX MOV EAX, EDX ;get dividend_hi XOR EDX, EDX ;zero extend it into EDX:EAX DIV EBX ;EAX = quotient_hi, EDX = intermediate remainder MOV EAX, ECX ;EAX = dividend_lo DIV EBX ;EAX = quotient_lo MOV EAX, EDX ;EAX = remainder_lo XOR EDX, EDX ;EDX = remainder_hi = 0 POP EBX ;restore EBX as per calling convention RET .r_big_divisor: PUSH EDI ;save EDI as per calling convention MOV EDI, ECX ;save divisor_hi SHR EDX, 1 ;shift both divisor and dividend right RCR EAX, 1 ;by 1 bit ROR EDI, 1 RCR EBX, 1 BSR ECX, ECX ;ECX = number of remaining shifts SHRD EBX, EDI, CL ;scale down divisor and dividend such SHRD EAX, EDX, CL ;that divisor is less than 2^32 SHR EDX, CL ; (i.e. fits in EBX) ROL EDI, 1 ;restore original divisor (EDI:ESI) DIV EBX ;compute quotient MOV EBX, [ESP+12] ;dividend lo-word MOV ECX, EAX ;save quotient IMUL EDI, EAX ;quotient * divisor hi-word (low only) MUL DWORD [ESP+20] ;quotient * divisor lo-word ADD EDX, EDI ;EDX:EAX = quotient * divisor SUB EBX, EAX ;dividend_lo – (quot.*divisor)–lo MOV ECX, [ESP+16] ;dividend_hi MOV EAX, [ESP+20] ;divisor_lo SBB ECX, EDX ;subtract divisor * quot. from dividend SBB EDX, EDX ;(remainder < 0)? 0xFFFFFFFF : 0 AND EAX, EDX ;(remainder < 0)? divisor_lo : 0 AND EDX, [ESP+24] ;(remainder < 0)? divisor_hi : 0 ADD EAX, EBX ;remainder += (remainder < 0)? ADC EDX, ECX ;divisor : 0 POP EDI ;restore EDI as per calling convention POP EBX ;restore EBX as per calling convention RET
murder, а не переписать ли Вам код из поста н27 на SSE2? Так Вы бы могли внести свой вклад в мою прогу и навечно вписать в мануал свой ник золотыми буквами... Я очень разбалован флагами и не представляю как там на SSE2 чегото сравнивать и ловить переполнения, но любителям Уоренна это не сложно. В общем, редукцию в цикле нужно будет делать для двух пар чисел по очереди, а окончательную редукцию можно сделать запихнув все в один регистр xmm. В общем процедура принимает abcd и efgh, а возвращает ijkl где i=(a*e) mod fff00001 j=(b*f) mod fff00001 и т.д.