delphiassemblyx86sse2basm

How to optimize this Delphi function with SSE2?


I need a hint, how to implement this Delphi function using SSE2 assembly (32 Bit). Other optimizations are welcome too. Maybe one can tell me, what kind of instructions could be used, so I have a starting point for further reading.

Actual:

const Precision = 10000;

// This function adds all Pixels into one. The pixels are weighted before adding. 
// A weight can range from 0 to "Precision". "Size" is typically 10 to 50.

function TFilter.Combine(Pixels: PByte; Weights: PCardinal; const Size: Cardinal): Cardinal;
var
  i, R, G, B, A: Cardinal;
begin
  B := Pixels^ * Weights^; Inc(Pixels);
  G := Pixels^ * Weights^; Inc(Pixels);
  R := Pixels^ * Weights^; Inc(Pixels);
  A := Pixels^ * Weights^; Inc(Pixels);
  Inc(Weights); // goto next weight
  for i := 1 to Size - 1 do
  begin
    Inc(B, Pixels^ * Weights^); Inc(Pixels);
    Inc(G, Pixels^ * Weights^); Inc(Pixels);
    Inc(R, Pixels^ * Weights^); Inc(Pixels);
    Inc(A, Pixels^ * Weights^); Inc(Pixels);
    Inc(Weights); // goto next weight
  end;
  B := B div Precision;
  G := G div Precision;
  R := R div Precision;
  A := A div Precision;

  Result := A shl 24 or R shl 16 or G shl 8 or B;
end;

Expected:

function TFilter.Combine(Pixels: PByte; Weights: PCardinal; const Size: Cardinal): Cardinal;
asm
  // Insert fast SSE2-Code here ;-)
end;

Solution

  • Rather straightforward implementation. I've changed your function prototype - regular function (against object method).

    This code works about 3x times faster than byte-per-byte function (1500 ms for 1000000 iterations on 256-element array, roughly 0.7 GB/sec at my old Athlon XP 2.2 GHz)

    function Combine(Pixels: PByte; Weights: PInteger; const Size: Cardinal): Integer;
    //x86, register calling convention - three parameters in EAX, EDX, ECX
    const
      Precision: Single = 1.0;
    asm
      pxor XMM6, XMM6 //zero const
      pxor XMM4, XMM4 // zero accum
    
    @@cycle:
      movd XMM1, [eax] //load color data
      movss XMM3, [edx]  //load weight
    
      punpcklbw XMM1, XMM6 //bytes to words
      shufps XMM3, XMM3, 0 // 4 x weight
      punpcklwd XMM1, XMM6 //words to ints
      cvtdq2ps XMM2, XMM3  //ints to singles
      cvtdq2ps XMM0, XMM1  //ints to singles
    
      mulps XMM0, XMM2    //data * weight
      addps XMM4, XMM0    //accum  = accum + data * weight
    
      add eax, 4        // inc pointers
      add edx, 4
      loop @@cycle
    
      movss XMM5, Precision
      shufps XMM5, XMM5, 0 // 4 x precision constant
    
      divps XMM4, XMM5    //accum/precision
    
      cvtps2dq XMM2, XMM4  //rounding singles to ints
      packssdw XMM2, XMM2 //ints to ShortInts
      packuswb XMM2, XMM2  //ShortInts to bytes
    
      movd eax, XMM2  //result
    end;