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