Hi all --
[ #() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak'] ] bench '127,000,000 per second. 7.85 nanoseconds per run. 0 % GC time.' [ #() ifEmpty: ['hallo'] ifNotEmpty: ['squeak'] ] bench 7,580,000 per second. 132 nanoseconds per run. 78.0088 % GC time.'
Why is that? :-) Why is there an extra GC?
Here is the current implementation of #ifEmpty:ifNotEmpty:
ifEmpty: emptyBlock ifNotEmpty: notEmptyBlock self isEmpty ifTrue: [^ emptyBlock value]. ^ notEmptyBlock cull: self
Best, Marcel
Hm ... ifTrue:ifFalse: sends are inlined whereas ifEmpty:ifNotEmpty: receives regular block closures, right? By the way, it's 5 % GC time for me only.
Monitoring BlockClosure allSubInstances size reveals that there are being created significantly more blocks in the image while running
[ [ [ #() ifEmpty: ['hallo'] ifNotEmpty: ['squeak'] ] repeat ] valueWithin: 10 seconds onTimeout: [] ] forkAt: 30.
Turns out that Object>>#value is faster (though less idiomatic):
[ #() ifEmpty: ['hallo'] ifNotEmpty: ['squeak'] ] bench '21,000,000 per second. 47.7 nanoseconds per run. 4.999 % GC time.' [ #() ifEmpty: 'hallo' ifNotEmpty: 'squeak' ] bench '43,400,000 per second. 23.1 nanoseconds per run. 0 % GC time.'
[ #() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak'] ] bench '52,900,000 per second. 18.9 nanoseconds per run. 0 % GC time.'
pushFullClosure: seems to create block instances eagerly (as opposed to Context instances).
Could a clever bytecode optimization share BlockClosure instances with the same/empty set of enclosed variables?
For now, just do it ourselves:
a := ['hallo']. b := ['squeak']. [#() ifEmpty: a ifNotEmpty: b ] bench '31,000,000 per second. 32.2 nanoseconds per run. 0 % GC time.'
Best, Christoph
________________________________ Von: Marcel Taeumel via Squeak-dev squeak-dev@lists.squeakfoundation.org Gesendet: Dienstag, 19. September 2023 13:14:05 An: gettimothy via Squeak-dev Cc: Taeumel, Marcel Betreff: [squeak-dev] Noticeable GC load for ifEmpty:ifNotEmpty:?
Hi all --
[ #() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak'] ] bench '127,000,000 per second. 7.85 nanoseconds per run. 0 % GC time.' [ #() ifEmpty: ['hallo'] ifNotEmpty: ['squeak'] ] bench 7,580,000 per second. 132 nanoseconds per run. 78.0088 % GC time.'
Why is that? :-) Why is there an extra GC?
Here is the current implementation of #ifEmpty:ifNotEmpty:
ifEmpty: emptyBlock ifNotEmpty: notEmptyBlock self isEmpty ifTrue: [^ emptyBlock value]. ^ notEmptyBlock cull: self
Best, Marcel
Ah, yes. push-full-closure. That needs to be garbage collected. That idea with sharing instances of FullBlockClosure is interesting ... not sure about long-running blocks though and re-entrancy... :-)
By the way, it's 5 % GC time for me only.
This depends on the current size of your object memory (or old space) and probably about the current load of the GC's internal data structures :-) I was just worried bout that "zero vs. not zero" observation.
Best, Marcel Am 19.09.2023 15:03:42 schrieb Thiede, Christoph christoph.thiede@student.hpi.uni-potsdam.de: Hm ... ifTrue:ifFalse: sends are inlined whereas ifEmpty:ifNotEmpty: receives regular block closures, right? By the way, it's 5 % GC time for me only.
Monitoring BlockClosure allSubInstances size reveals that there are being created significantly more blocks in the image while running [ [ [ #() ifEmpty: ['hallo'] ifNotEmpty: ['squeak'] ] repeat ] valueWithin: 10 seconds onTimeout: [] ] forkAt: 30.
Turns out that Object>>#value is faster (though less idiomatic):
[ #() ifEmpty: ['hallo'] ifNotEmpty: ['squeak'] ] bench '21,000,000 per second. 47.7 nanoseconds per run. 4.999 % GC time.' [ #() ifEmpty: 'hallo' ifNotEmpty: 'squeak' ] bench '43,400,000 per second. 23.1 nanoseconds per run. 0 % GC time.' [ #() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak'] ] bench '52,900,000 per second. 18.9 nanoseconds per run. 0 % GC time.'
pushFullClosure: seems to create block instances eagerly (as opposed to Context instances). Could a clever bytecode optimization share BlockClosure instances with the same/empty set of enclosed variables? For now, just do it ourselves:
a := ['hallo']. b := ['squeak']. [#() ifEmpty: a ifNotEmpty: b ] bench '31,000,000 per second. 32.2 nanoseconds per run. 0 % GC time.'
Best, Christoph Von: Marcel Taeumel via Squeak-dev squeak-dev@lists.squeakfoundation.org Gesendet: Dienstag, 19. September 2023 13:14:05 An: gettimothy via Squeak-dev Cc: Taeumel, Marcel Betreff: [squeak-dev] Noticeable GC load for ifEmpty:ifNotEmpty:? Hi all --
[ #() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak'] ] bench '127,000,000 per second. 7.85 nanoseconds per run. 0 % GC time.' [ #() ifEmpty: ['hallo'] ifNotEmpty: ['squeak'] ] bench 7,580,000 per second. 132 nanoseconds per run. 78.0088 % GC time.'
Why is that? :-) Why is there an extra GC?
Here is the current implementation of #ifEmpty:ifNotEmpty:
ifEmpty: emptyBlock ifNotEmpty: notEmptyBlock self isEmpty ifTrue: [^ emptyBlock value]. ^ notEmptyBlock cull: self
Best, Marcel
Hi Marcel,
hint: look at the bytecode. The bytecode compiler inlined most blocks into ifTrue:ifFalse: et al hence this
#() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak']
produces no closures, compiling to conditional jump bytecodes. Whereas this
#() ifEmpty: ['hallo'] ifNotEmpty: ['squeak']
produces two closures as arguments.
Apologies about the text size change
_,,,^..^,,,_ (phone)
On Sep 19, 2023, at 4:14 AM, Marcel Taeumel via Squeak-dev squeak-dev@lists.squeakfoundation.org wrote:
Hi all --
[ #() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak'] ] bench '127,000,000 per second. 7.85 nanoseconds per run. 0 % GC time.' [ #() ifEmpty: ['hallo'] ifNotEmpty: ['squeak'] ] bench 7,580,000 per second. 132 nanoseconds per run. 78.0088 % GC time.'
Why is that? :-) Why is there an extra GC?
Here is the current implementation of #ifEmpty:ifNotEmpty:
ifEmpty: emptyBlock ifNotEmpty: notEmptyBlock self isEmpty ifTrue: [^ emptyBlock value]. ^ notEmptyBlock cull: self
Best, Marcel
Hi,
Taking Eliots point:
On 20. Sep 2023, at 20:10, Eliot Miranda eliot.miranda@gmail.com wrote:
Hi Marcel,
hint: look at the bytecode. The bytecode compiler inlined most blocks into ifTrue:ifFalse: et al hence this
#() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak']
produces no closures, compiling to conditional jump bytecodes. Whereas this
#() ifEmpty: ['hallo'] ifNotEmpty: ['squeak']
produces two closures as arguments.
The crucial thing is the _sender_ not the implementation of ifEmpty:ifNotEmpty: (The objects are created there, if at all)
Best regards -Tobias
Apologies about the text size change
_,,,^..^,,,_ (phone)
On Sep 19, 2023, at 4:14 AM, Marcel Taeumel via Squeak-dev squeak-dev@lists.squeakfoundation.org wrote:
Hi all -- [ #() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak'] ] bench '127,000,000 per second. 7.85 nanoseconds per run. 0 % GC time.' [ #() ifEmpty: ['hallo'] ifNotEmpty: ['squeak'] ] bench 7,580,000 per second. 132 nanoseconds per run. 78.0088 % GC time.'
Why is that? :-) Why is there an extra GC?
Here is the current implementation of #ifEmpty:ifNotEmpty:
ifEmpty: emptyBlock ifNotEmpty: notEmptyBlock self isEmpty ifTrue: [^ emptyBlock value]. ^ notEmptyBlock cull: self
Best, Marcel
Ha. And here I thought that such closures can be added and removed without the GC if nobody needs access to thisContext or similar. :-) Just push it on the stack and then pop it.
Best, Marcel Am 20.09.2023 20:30:10 schrieb Tobias Pape das.linux@gmx.de: Hi,
Taking Eliots point:
On 20. Sep 2023, at 20:10, Eliot Miranda wrote:
Hi Marcel,
hint: look at the bytecode. The bytecode compiler inlined most blocks into ifTrue:ifFalse: et al hence this
#() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak']
produces no closures, compiling to conditional jump bytecodes. Whereas this
#() ifEmpty: ['hallo'] ifNotEmpty: ['squeak']
produces two closures as arguments.
The crucial thing is the _sender_ not the implementation of ifEmpty:ifNotEmpty: (The objects are created there, if at all)
Best regards -Tobias
Apologies about the text size change
_,,,^..^,,,_ (phone)
On Sep 19, 2023, at 4:14 AM, Marcel Taeumel via Squeak-dev wrote:
Hi all -- [ #() isEmpty ifTrue: ['hallo'] ifFalse: ['squeak'] ] bench '127,000,000 per second. 7.85 nanoseconds per run. 0 % GC time.' [ #() ifEmpty: ['hallo'] ifNotEmpty: ['squeak'] ] bench 7,580,000 per second. 132 nanoseconds per run. 78.0088 % GC time.'
Why is that? :-) Why is there an extra GC?
Here is the current implementation of #ifEmpty:ifNotEmpty:
ifEmpty: emptyBlock ifNotEmpty: notEmptyBlock self isEmpty ifTrue: [^ emptyBlock value]. ^ notEmptyBlock cull: self
Best, Marcel
squeak-dev@lists.squeakfoundation.org