Hi Igor,<div><br></div><div> great news!<br><br><div class="gmail_quote">On Fri, Sep 21, 2012 at 7:59 AM, Igor Stasenko <span dir="ltr"><<a href="mailto:siguctua@gmail.com" target="_blank">siguctua@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
Hello there,<br>
<br>
so, we're entered a new area, where native code, generated from image<br>
side can be run directly by JIT.<br>
This feature was one of the first things which i wanted to try, once<br>
Eliot released Cog :)<br>
<br>
The way how we do that, is when VM decides to JIT a specific method,<br>
we copying the native code (from method trailer)<br>
directly into the method's code.<br>
All you need to do is to use special primitive for that 220 (<br>
#primitiveVoltage)<br>
<br>
So, a first question, which we wanted to be answered is how faster to<br>
run native code by JIT,<br>
comparing to running native code via NativeBoost primitive , which is<br>
#primitiveNativeCall..<br>
<br>
For here are methods, which just answer 42:<br>
<br>
This one using #primitiveNativeCall<br>
<br>
nbFoo2<br>
<primitive: #primitiveNativeCall module: #NativeBoostPlugin error: errorCode><br>
<br>
^ NBNativeCodeGen methodAssembly: [:gen :proxy :asm |<br>
asm noStackFrame.<br>
asm<br>
mov: (42 << 1) + 1 to: asm EAX;<br>
ret.<br>
]<br>
<br>
And this one uses JIT:<br>
<br>
nbFoo<br>
<primitive: 220 error: errorCode><br>
<br>
[ errorCode = ErrRunningViaInterpreter ] whileTrue: [ ^ self nbFoo ].<br>
<br>
^ NBNativeCodeGen jitMethodAssembly: [:gen :proxy :asm |<br>
asm noStackFrame.<br>
asm<br>
mov: (42 << 1) + 1 to: asm EDX;<br>
ret: 4 asUImm.<br>
]<br>
<br>
And this one is code which JIT can do:<br>
<br>
nbFoo42<br>
^ 42<br>
<br>
So, here the numbers:<br>
<br>
Time to run via #primitiveNativeCall :<br>
<br>
[100000000 timesRepeat: [ MyClass nbFoo2 ] ] timeToRun<br>
6995<br>
<br>
Time to run via JIT:<br>
<br>
[100000000 timesRepeat: [ MyClass nbFoo ] ] timeToRun<br>
897<br>
<br>
Time to run JITed method:<br>
<br>
[100000000 timesRepeat: [ MyClass nbFoo42 ] ] timeToRun<br>
899<br>
<br>
so, as you can see, the JITed method and our custom generated code is<br>
on par (which is logical ;).<br>
<br>
Time to run an empty loop:<br>
<br>
[100000000 timesRepeat: [ ] ] timeToRun 679<br>
<br>
<br>
So, here the result, if we extract the loop overhead, we can see the<br>
difference in<br>
calling our native code when it uses JIT vs using #primitiveNativeCall :<br>
<br>
(6995 - 679 ) / (897- 679) asFloat 28.972477064220183<br>
<br>
28 times faster!!!!<br>
<br>
So, with this new feature, we now can make our generated code to run<br>
with unmatched speed,<br>
without overhead related to #primitiveNativeCall.<br>
This is especially useful for implementing primives which involving<br>
heavy numeric crunching.<br>
<br>
I would release this code to public, but there's one little<br>
discrepancy i need to deal with first:<br>
<br>
(one little problem, which i hope Eliot can help to solve)<br>
<br>
it looks like primitivePerform: never enters the JIT mode, but always<br>
executing the method via interpreter.<br></blockquote><div><br></div><div>I'll take a look. This is all very detailed so I'll need a little time.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
This is why you see this code:<br>
[ errorCode = ErrRunningViaInterpreter ] whileTrue: [ ^ self nbFoo ].<br>
<br>
because if i do it inside of NBNativeCodeGen>>jitMethodAssembly:,<br>
which checks for same error and retries the send using perform<br>
primitive, it never enters the JIT mode,<br>
resulting in endless loop :(<br>
<br>
This is despite the fact that method is JITed, because we enforce the<br>
JITing of that method during error handling:<br>
<br>
lastError = ErrRunningViaInterpreter ifTrue: [<br>
"a method contains native code, but executed by interpreter "<br>
method forceJIT ifFalse: [ self error: 'Failed to JIT the compiled<br>
method. Try reducing it''s size ' ].<br>
^ self retrySend: aContext<br>
].<br>
<br>
The #forceJit is the primitive which i implemented like following:<br>
<br>
primitiveForceJIT<br>
<br>
<export: true ><br>
<br>
| val result |<br>
<br>
val := self stackTop.<br>
<br>
(self isIntegerObject: val) ifTrue: [ ^ self primitiveFail ].<br>
(self isCompiledMethod: val) ifFalse: [ ^ self primitiveFail ].<br>
<br>
(self methodHasCogMethod: val) ifFalse: [<br>
cogit cog: val selector: objectMemory nilObject ].<br>
<br>
result := (self methodHasCogMethod: val ) ifTrue: [ objectMemory<br>
trueObject ] ifFalse: [ objectMemory falseObject ].<br>
<br>
^ self pop: 1 thenPush: result.<br>
<br>
As you can see from its usage, if VM, for some reason will fail to jit<br>
the method, the primitive will answer false,<br>
and we will stop with an error.. Which apparently never happens.<br>
Still, a #primitivePerform seems like ignoring that the method<br>
contains machine code an always runs it interpreted :(<br>
<br>
I do not like the idea, that users will be forced to manually put such<br>
loops in every method they will write..<br>
any ideas/suggestions how to overcome that?<span class="HOEnZb"><font color="#888888"><br></font></span></blockquote><div><br></div><div>Yes. The JIT should be told that methods that have NB code should be jitted. But right now I don't understand enough of how NB code is generated and methods marked that they have NB code etc to know exactly how to do this. I need to play around a bit. </div>
</div><br><br clear="all"><div><br></div>-- <br>best,<div>Eliot</div><br>
</div>