On Mon, Jun 22, 2015 at 11:05:30AM -0700, Eliot Miranda wrote:
On Mon, Jun 22, 2015 at 9:35 AM, David T. Lewis <lewis@mail.msen.com> wrote:
That sounds right to me too. But it would be a worthwhile experiment to set up a test to confirm it. Maybe take one or more methods that call numbered primitives, and recode them to call the primitives by name. Then measure and see if anything got slower.
Dave
On Mon, Jun 22, 2015 at 10:40 AM, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Hi Esteban,
you can set up a test using the LargeInteger comparison primitives. They're both named and numberd. e.g.
23 primitiveLessThanLargeIntegers
So you can write e.g.
LargePositiveInteger>>#< anInteger "Primitive. Compare the receiver with the argument and answer true if the receiver is less than the argument. Otherwise answer false. Fail if the argument is not a SmallInteger or a LargePositiveInteger less than 2-to-the-30th (1073741824). Optional. See Object documentation whatIsAPrimitive."
<primitive: 23> ^super < anInteger
as
numberedLessThan: anInteger "Primitive. Compare the receiver with the argument and answer true if the receiver is less than the argument. Otherwise answer false. Fail if the argument is not a SmallInteger or a LargePositiveInteger less than 2-to-the-30th (1073741824). Optional. See Object documentation whatIsAPrimitive."
<primitive: 23> ^super < anInteger
namedLessThan: anInteger "Primitive. Compare the receiver with the argument and answer true if the receiver is less than the argument. Otherwise answer false. Fail if the argument is not a SmallInteger or a LargePositiveInteger less than 2-to-the-30th (1073741824). Optional. See Object documentation whatIsAPrimitive."
<primitive: 'primitiveLessThanLargeIntegers'> ^super < anInteger
and test it with two suitable large integers. Will you report back? I'd like to know the answer. Named primitive invocation should be slightly slower. As Cl??ment says, a return different address is written to the stack, overwriting the primitive code, but that return path is essentially the same as for numbered primtiives. So I expect that there will be almost no measurable difference.
Wow, it is indeed a significant difference. Substituting the return address must invoke all sorts of cost in an x86 cpu. Here are 2 x 5 runs
| i | i := SmallInteger maxVal + 1. (1 to: 6) collect: [:j| {[1 to: 10000000 do: [:k| i numberedLessThan: i]] timeToRun. [1 to: 10000000 do: [:k| i namedLessThan: i]] timeToRun}]
#(#(191 283) #(211 375) #(281 405) #(300 411) #(281 421) #(296 409)) #(#(186 267) #(201 273) #(210 364) #(294 410) #(313 400) #(292 405))
So the overhead is of the order of (100ms / 10,000,000) per call. e.g. around 10ns per named primitive call. Interesting :-)
On an interpreter VM, the results are as Tim and I initially expected: | i | i := SmallInteger maxVal + 1. (1 to: 6) collect: [:j| {[1 to: 10000000 do: [:k| i numberedLessThan: i]] timeToRun. [1 to: 10000000 do: [:k| i namedLessThan: i]] timeToRun}] ==> #(#(791 789) #(793 794) #(793 790) #(791 791) #(790 794) #(795 789)) With a Cog VM, the numbered primitives are significantly faster: | i | i := SmallInteger maxVal + 1. (1 to: 6) collect: [:j| {[1 to: 10000000 do: [:k| i numberedLessThan: i]] timeToRun. [1 to: 10000000 do: [:k| i namedLessThan: i]] timeToRun}] ==> #(#(542 670) #(542 668) #(544 678) #(546 680) #(540 666) #(540 680)) On Mon, Jun 22, 2015 at 07:16:43PM +0200, Cl??ment Bera wrote:
Well it also depends if the primitive is generated by the JIT. If you rewrite SmallInteger>>#+ from primitive 1 to a named primitive the overhead will be more important than just the searching/loading/linking because the JIT won't compile it to n-code anymore.
So maybe this is the reason for the difference. Note: this is with an old Cog VM, because I lost my primary PC and can't restore it right now. But I think the results are relevant WRT this discussion. /usr/local/lib/squeak/4.0-2776/squeak Croquet Closure Cog VM [CoInterpreter VMMaker.oscog-eem.331] Dave