Simulating virtual methods in Delphi
I am writing a Delphi interface for SSE instructions. It's a class (for visibility sake, etc) TSimdCpu with N class methods (one per each SSE instruction; the obvious performance overhead is not an issue right now).
Now I want to compare performance of my code (slow as it is) with pure pascal code doing same thing. My first guess would be to write a similar class TGenericCpu with same method names. But without the common base class and virtual methods I can't just have one piece of testing code that would call methods of whatever class it's supposed to run tests on. Ideally, I would like something like
TestOn(TSimdCpu);
TestOn(TGenericCpu);
But I am lost on how to implement this without using delphi's virtual methods. I don't want to fall back to virtual methods for two reasons: one is performance and another is that it's only going to be used for testing and for all practical use it would be adding meaningless complexity.
Can generics be useful here? Something like
TTest<T> = class
...
T.AddVector(v);
...
TTest<TSimdCpu>.Test;
TTest<TGenericCpu>.Test;
You want to implement something that looks like virtual methods but does not use virtual methods or interfaces for performance reasons.
You need to add some indirection. Create a record that holds procedural variables. For illustration:
type
TAddFunc = function(a, b: Double): Double;
TMyRecord = record
AddFunc: TAddFunc;
end;
Then declare two instances of the record. One populated with the SSE functions, and one populated with the non-SSE reference functions.
At this point you have what you need. You can pass these records around and use the indirection they provide to write generic test code.
This indirection will cost though. After all, what you have here is a manual implementation of interfaces. Expect a similar performance overhead for function call as for interfaces.
I expect that, unless your operands are large arrays, that the indirection cost will skew your benchmarks. I know that you asked specifically how to implement the tests using indirection, but I personally would want to test using as close to the real code as possible. That means testing direct function calls.
You ask about generics. They are no use to you. In order to make a generic class that was parameterised on the class under test, you'd need the class under test to be derived from a common base class, or implement a common interface. And then you are back where you started.
In your code, the main speed difference won't be between function calls.
If you take a look at the asm, a virtual method call is something like
mov eax,object
mov ebx,[eax] // get the the class info VMT
call dword ptr [ebx+##] // where ## is the virtual method offset
Whereas a non virtual method is
mov eax,object
call SomeAbsoluteAddress
And for a pointer to a function (on the stack)
mov eax,object
call dword ptr [ebp+##] // where ## is the pointer in the stack
You are just gaining one or two lookups in the class info VMT.
I suspect your test are over-optimized for the pointer to a function, since the pointer is probably on the stack. On real code, you would have to store the pointer somewhere, so you would gain nothing in comparison to a virtual method call.
And if you define your methods as class procedure
instead of procedure
, I suspect the class virtual methods and the function redirection will perform exactly the same:
mov eax,classinfo
call dword ptr [eax+##] // where ## is the virtual method offset
For such calculation, what would really speed up the process may be not calling functions at all, but creating some kind of simple JIT. Create the binary opcode flow before running the function, by looking at the asm opcodes, then create a buffer containing the execution flow, and directly execute it. Here we would talk about performance. It is similar to inlining function calls.
I know at least two (recent and maintained) projects with such JIT compilation written in Delphi: Besen JavaScript engine and Delphi Web Script. Besen copies asm stubs to create the JITted buffer, whereas DWS computes the opcodes via a set of generator methods.
Also consider using a language with tuned and optimized JIT if you need floating point performance. You could use eg our Open Source SpiderMonkey library for Delphi. You could write your code in plain JavaScript, then let the optimized JIT do its work. You may be amazed by the resulting speed: the result is usually faster than Delphi x87 native code, for floating point. You would gain a lot of development time.
David Heffernan's idea seems like the only way for 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 normal and virtual function calls could 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;
链接地址: http://www.djcxy.com/p/35020.html
上一篇: 在虚拟树中设置节点状态
下一篇: 在Delphi中模拟虚拟方法