HI,
Because I need a lot of qword multiplications (spend half of the time in FPC_MUL_QWORD) I tried to make a faster alternative:
function QMul(X, Y: QWord): QWord; assembler; nostackframe;
const
PUSH = SizeOf(DWord);
OFFS = 1 * PUSH; // function call counts as 1 push
// offsets for the arguments on the stack
Y_Lo = OFFS;
Y_Hi = OFFS + 1 * PUSH;
X_Lo = OFFS + 2 * PUSH;
X_Hi = OFFS + 3 * PUSH;
ProdA = X_Hi; // we can store the partial results here because
ProdB = Y_Hi; // X_Hi and X_Lo are only needed once.{$asmmode intel}
asm
mov eax, [esp+X_Hi]
mul [esp+Y_Lo]
mov [esp+ProdA], eax // ProdA := X_Hi * Y_Lo, ignore carry mov eax, [esp+Y_Hi]
mul [esp+X_Lo]
mov [esp+ProdB], eax // ProdB := Y_Hi * X_Lo, ignore carry
mov eax, [esp+Y_Lo]
mul [esp+X_Lo] // eax := Y_Lo * X_Lo (low 32 bit end result)
// edx := carry
add edx, [esp+ProdA]
add edx, [esp+ProdB] // add the two other products to the carry
end;This takes only 70% of the time that FPC_MUL_QWORD takes, this is the fastest one I could come up with and it will speed up my program considerably. Now I wonder whether it would be possible to somehow (ab)use SSE2 to speed it up even more. I tried to fight against these cryptic SSE instructions for a while and even got something that at least produced the correct results but it is twice as slow. Is it reasonable to try this with sse2 anyways (old PentiumM, only sse2, not 3!) or won't this lead to anything useful.
The following does two of the three multiplications in parallel but the code for adding the results and all the needed shuffling looks cumbersome and suboptimal to me (and also needs a lot of time according to oprofile). Can this be improved?
function QMulx (x,y: QWord):QWord; assembler; nostackframe;
const
Shuf_1_0 = %00010000;
Shuf_2_3 = %00100011;
Shuf___0 = %00000000;
Shuf___2 = %00000010;
Shuf___1 = %00000001;
Shuf___3 = %00000011;
asm
movlps xmm0, [esp+4] // load __10
movhps xmm0, [esp+12] // load 32__
pshufd xmm1, xmm0, Shuf_1_0 // xmm1 = _1_0
pshufd xmm2, xmm0, Shuf_2_3 // xmm2 = _2_3
pshufd xmm3, xmm0, Shuf___0 // xmm3 = ___0
pshufd xmm4, xmm0, Shuf___2 // xmm3 = ___2
PMULUDQ xmm1, xmm2
PMULUDQ xmm3, xmm4
// these must be added:
pshufd xmm5, xmm1, Shuf___0 // prod1 from H1*L2
pshufd xmm6, xmm1, Shuf___2 // prod2 from L1*H2
pshufd xmm7, xmm3, Shuf___1 // carry from L1*L2
// this is the Hi32 result, must go to EDX
paddq xmm7, xmm6
paddq xmm7, xmm5
// get the results into eax:edx
movd eax, xmm3
movd edx, xmm7
end;