Modeling Virtual Methods in Delphi

I am writing a Delphi frontend for SSE instructions. This is a class (for the sake of visibility, etc.) TSimdCpu with class N methods (one for each SSE instruction, obvious overhead is not an issue now).

Now I want to compare the performance of my (seemingly slow) code with pure pascal code doing the same thing. My first guess would be to write a similar TGenericCpu class with the same method names. But without a common base class and virtual methods, I can't just have one piece of test code that calls the methods of whatever class it needs to run tests on. Ideally, I would like something like

TestOn(TSimdCpu);
TestOn(TGenericCpu);

      

But I am at a loss on how to implement this without using delphi virtual methods. I don't want to fall back on virtual methods for two reasons, one is performance and the other is that it will only be used for testing and for all practical use it will add meaningless complexity.

Can generics be used here? Something like

TTest<T> = class
...
T.AddVector(v);
...
TTest<TSimdCpu>.Test;
TTest<TGenericCpu>.Test;

      

+3


source to share


3 answers


You want to implement something that looks like virtual methods, but doesn't use virtual methods or interfaces for performance reasons.

You need to add some indirection. Create a record containing procedural variables. To illustrate:

type
  TAddFunc = function(a, b: Double): Double;

  TMyRecord = record
    AddFunc: TAddFunc;
  end;

      

Then declare two instances of the record. One is populated with SSE functions and one is populated with non-SSE help functions.

At this point, you have what you need. You can pass these records and use the indirection indicated by them to write general test code.



This indirect action will cost you. After all, you have a manual implementation of the interfaces. Expect similar operating overhead for function calls as for interfaces.

I expect that if your operands are not large arrays, the cost of indirect corruption will skew your tests. I know you asked how to implement tests using indirection, but I personally would like to test using as close to real code as possible. This means testing direct function calls.


You are asking about generics. They don't suit you. To create a generic class that has been parameterized in the class under test, you need the class under test to derive from a common base class or implement a common interface. And then you are back to where you started.

+2


source


In your code, the main speed difference will not be between function calls.

If you look at asm the virtual method call is like

mov eax,object
mov ebx,[eax]  // get the the class info VMT
call dword ptr [ebx+##] // where ## is the virtual method offset

      

While not a virtual method

mov eax,object
call SomeAbsoluteAddress

      

And for a function pointer (on the stack)

mov eax,object
call dword ptr [ebp+##] // where ## is the pointer in the stack

      



You just get one or two lookups in the VMT class information.

I suspect that your test is over-optimized for a function pointer, since the pointer is probably on the stack. In real code, you will need to store the pointer somewhere so that you get nothing compared to calling the virtual method.

And if you define your methods as class procedure

instead procedure

, I suspect virtual class methods and function redirection will do exactly the same:

mov eax,classinfo
call dword ptr [eax+##] // where ## is the virtual method offset

      

For such a calculation, in order to speed up the process, they may not have called functions at all, but created some kind of simple JIT. Create a binary opcode stream before running the function by looking at the opcodes asm, then create a buffer containing the execution stream and execute it directly. We'll talk about performance here. It's like overlapping function calls.

I know of at least two (recent and supported) projects with such JIT compilation written in Delphi: Besen JavaScript engine and Delphi Web Script . Besen copies asm stubs to create a JIT buffer, while DWS calculates opcodes using a set of generator methods.

Also consider using a JIT tuned and optimized language if you need floating point performance. You can use for example. our Open Source SpiderMonkey Library for Delphi . You can write your code in plain JavaScript and then the optimized JIT will do its job. You may be amazed at the resulting speed: the result is usually faster than Delphi x87 native floating point code . You will get a lot of development time.

+1


source


David Heffernan's idea seems to be the only way right now. I did a quick test - here are the results:

simd 516 ms (pointer to a function, asm)
JensG 1187 ms (virtual method, asm)
generic 2797 ms (pointer to a function, pascal)
generic virtual 3360 ms (virtual method, pascal)

      

The difference between regular and virtual calls can be relatively small for pascal code, but not for asm

  if cpu = nil then
    if test.name = 'JensG' then
      for i := 1 to N do begin
        form1.JensGAdd(v1^);
        form1.JensGMul(v2^);
      end
    else
      for i := 1 to N do begin
        form1.GenericAdd(v1^);
        form1.GenericMul(v2^);
      end
  else
    for i := 1 to N do begin
      cpu.AddVector(v1^);
      cpu.MulVector(v2^);
    end;

      

0


source







All Articles