Hi,
the code of primitive 60 seems to be assuming that the method has exactly one argument, ignoring the actual number of arguments. This seems to be an unnecessary restriction, which makes it cumbersome/impossible to use it in methods which have more arguments, like #at:ifAbsent:.
Since the primitive uses two objects from the top of the stack, it's possible to write a hackish method to see the potential benefits:
ifAbsent: aBlock receiver: receiver at: index
<primitive: 60> ^aBlock value
The primitive will treat the variable receiver as the receiver, and index as the index. The real receiver is not used at all.
This method performs 37% better than #at:ifAbsent for the case where the index is valid.
Does it have any benefits not taking the actual number of arguments into account in this primitive?
Cheers, Levente
I think you are suggesting to use the argumentCount to determine the actual receiver and the first parameter, rather than assume that there is exactly one argument so that the top two stack values are used. Is that right?
The only cost that I can see associated with this would be related to finding the receiver and the first parameter based on argumentCount. It might be a negligible difference, so it would be worth trying it to find out.
Dave
On Sat, Oct 19, 2013 at 10:53:40PM +0200, Levente Uzonyi wrote:
Hi,
the code of primitive 60 seems to be assuming that the method has exactly one argument, ignoring the actual number of arguments. This seems to be an unnecessary restriction, which makes it cumbersome/impossible to use it in methods which have more arguments, like #at:ifAbsent:.
Since the primitive uses two objects from the top of the stack, it's possible to write a hackish method to see the potential benefits:
ifAbsent: aBlock receiver: receiver at: index
<primitive: 60> ^aBlock value
The primitive will treat the variable receiver as the receiver, and index as the index. The real receiver is not used at all.
This method performs 37% better than #at:ifAbsent for the case where the index is valid.
Does it have any benefits not taking the actual number of arguments into account in this primitive?
Cheers, Levente
On Sat, 19 Oct 2013, David T. Lewis wrote:
I think you are suggesting to use the argumentCount to determine the actual receiver and the first parameter, rather than assume that there is exactly one argument so that the top two stack values are used. Is that right?
Exactly.
Levente
The only cost that I can see associated with this would be related to finding the receiver and the first parameter based on argumentCount. It might be a negligible difference, so it would be worth trying it to find out.
Dave
On Sat, Oct 19, 2013 at 10:53:40PM +0200, Levente Uzonyi wrote:
Hi,
the code of primitive 60 seems to be assuming that the method has exactly one argument, ignoring the actual number of arguments. This seems to be an unnecessary restriction, which makes it cumbersome/impossible to use it in methods which have more arguments, like #at:ifAbsent:.
Since the primitive uses two objects from the top of the stack, it's possible to write a hackish method to see the potential benefits:
ifAbsent: aBlock receiver: receiver at: index
<primitive: 60> ^aBlock value
The primitive will treat the variable receiver as the receiver, and index as the index. The real receiver is not used at all.
This method performs 37% better than #at:ifAbsent for the case where the index is valid.
Does it have any benefits not taking the actual number of arguments into account in this primitive?
Cheers, Levente
On Sat, Oct 19, 2013 at 10:53:40PM +0200, Levente Uzonyi wrote:
Hi,
the code of primitive 60 seems to be assuming that the method has exactly one argument, ignoring the actual number of arguments. This seems to be an unnecessary restriction, which makes it cumbersome/impossible to use it in methods which have more arguments, like #at:ifAbsent:.
Since the primitive uses two objects from the top of the stack, it's possible to write a hackish method to see the potential benefits:
ifAbsent: aBlock receiver: receiver at: index
<primitive: 60> ^aBlock value
The primitive will treat the variable receiver as the receiver, and index as the index. The real receiver is not used at all.
This method performs 37% better than #at:ifAbsent for the case where the index is valid.
Does it have any benefits not taking the actual number of arguments into account in this primitive?
Cheers, Levente
I tried a rather casual benchmark. In the VM, I changed from this:
commonAt: stringy "This code is called if the receiver responds primitively to at:. If this is so, it will be installed in the atCache so that subsequent calls of at: or next may be handled immediately in bytecode primitive routines." | index rcvr atIx result | index := self positive32BitValueOf: (self stackTop). "Sets primFailCode" rcvr := self stackValue: 1.
To this:
commonAt: stringy "This code is called if the receiver responds primitively to at:. If this is so, it will be installed in the atCache so that subsequent calls of at: or next may be handled immediately in bytecode primitive routines." | index rcvr atIx result | index := self positive32BitValueOf: (self stackValue: argumentCount - 1). "Sets primFailCode" rcvr := self stackValue: argumentCount.
Then I ran the following test with each VM:
(1 to: 25) collect: [:foo | size := 100000000. bigArray := Array new: size. 1 to: size do: [:i | bigArray at: i put: i]. Smalltalk garbageCollect. Time millisecondsToRun: [1 to: size do: [:i | bigArray at: i]]].
I ended up with less than 1% performance difference between the two VMs for the common case of #at: access to an array.
This was not a very careful test, but it suggests that your idea could be used to improve at:ifAbsent: performance without causing significant performance problems elsewhere.
Dave
Thanks for testing it. It looks promising.
Levente
On Sat, 19 Oct 2013, David T. Lewis wrote:
On Sat, Oct 19, 2013 at 10:53:40PM +0200, Levente Uzonyi wrote:
Hi,
the code of primitive 60 seems to be assuming that the method has exactly one argument, ignoring the actual number of arguments. This seems to be an unnecessary restriction, which makes it cumbersome/impossible to use it in methods which have more arguments, like #at:ifAbsent:.
Since the primitive uses two objects from the top of the stack, it's possible to write a hackish method to see the potential benefits:
ifAbsent: aBlock receiver: receiver at: index
<primitive: 60> ^aBlock value
The primitive will treat the variable receiver as the receiver, and index as the index. The real receiver is not used at all.
This method performs 37% better than #at:ifAbsent for the case where the index is valid.
Does it have any benefits not taking the actual number of arguments into account in this primitive?
Cheers, Levente
I tried a rather casual benchmark. In the VM, I changed from this:
commonAt: stringy "This code is called if the receiver responds primitively to at:. If this is so, it will be installed in the atCache so that subsequent calls of at: or next may be handled immediately in bytecode primitive routines." | index rcvr atIx result | index := self positive32BitValueOf: (self stackTop). "Sets primFailCode" rcvr := self stackValue: 1.
To this:
commonAt: stringy "This code is called if the receiver responds primitively to at:. If this is so, it will be installed in the atCache so that subsequent calls of at: or next may be handled immediately in bytecode primitive routines." | index rcvr atIx result | index := self positive32BitValueOf: (self stackValue: argumentCount - 1). "Sets primFailCode" rcvr := self stackValue: argumentCount.
Then I ran the following test with each VM:
(1 to: 25) collect: [:foo | size := 100000000. bigArray := Array new: size. 1 to: size do: [:i | bigArray at: i put: i]. Smalltalk garbageCollect. Time millisecondsToRun: [1 to: size do: [:i | bigArray at: i]]].
I ended up with less than 1% performance difference between the two VMs for the common case of #at: access to an array.
This was not a very careful test, but it suggests that your idea could be used to improve at:ifAbsent: performance without causing significant performance problems elsewhere.
Dave
vm-dev@lists.squeakfoundation.org