<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta>
      <journal-title-group>
        <journal-title>Kernel</journal-title>
      </journal-title-group>
    </journal-meta>
    <article-meta>
      <title-group>
        <article-title>Clean Blocks at the Opal Compiler</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>Nahuel Palumbo</string-name>
          <email>nahuel.palumbo@inria.fr</email>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Marcus Denker</string-name>
          <email>marcus.denker@inria.fr</email>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <aff id="aff0">
          <label>0</label>
          <institution>Univ. Lille, Inria, CNRS, Centrale Lille, UMR 9189 CRIStAL</institution>
          ,
          <addr-line>Lille</addr-line>
          ,
          <country country="FR">France</country>
        </aff>
      </contrib-group>
      <pub-date>
        <year>1011</year>
      </pub-date>
      <volume>2</volume>
      <fpage>1</fpage>
      <lpage>4</lpage>
      <abstract>
        <p>Higher-order languages encourage programmers to use lambda functions or closures. In Smalltalk, we see a lot of use of block closures, for example, in the collection API. Closures are expensive as they have to be created at runtime, impacting code execution eficiency. However, there are closures with code agnostic to the context where they have been defined and, thus, can be created at compile time. In this paper, we present Clean Blocks, an optimization implemented in the Pharo compiler for detecting and creating closures independent of the context at compile time. We also implemented a specialization for Constant Blocks, i.e., closures that only return constant values. We evaluate the impact of this optimization, statically in a new Pharo image, and dynamically by running several benchmarks and measuring closure activations and execution time. The results show that our optimization improves performance up to 1.35x, and leaves some open questions regarding the Garbage Collection overhead.</p>
      </abstract>
      <kwd-group>
        <kwd>eol&gt;Compiler</kwd>
        <kwd>Optimization</kwd>
        <kwd>Block Closure</kwd>
        <kwd>Opal</kwd>
        <kwd>Pharo</kwd>
      </kwd-group>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>1. Motivation</title>
      <p>SomeClass &gt;&gt; giveMeANumber
| x |
x := 10.</p>
      <p>^ self evaluate: [ x + 7 ]
SomeClass &gt;&gt; evaluate: blockClosure</p>
      <p>^ blockClosure eval</p>
      <p>Chart &gt;&gt; verticalTick</p>
      <p>^ decorations detect: #isVerticalTick ifNone: [ nil ]</p>
      <p>In this paper we describe
• Implementation of Clean Blocks. The implementation of compile-time closure creation
optimization in Pharo.
• Specialization for Constant Blocks. A specialization for Clean Blocks that only returns
constants, improving closures evaluation.
• Impact on the runtime. Analysis of the impact of the presented optimization on the runtime
system over many benchmarks.</p>
    </sec>
    <sec id="sec-2">
      <title>2. Implementation</title>
      <p>
        We implemented Clean Blocks in Pharo, a fully dynamic object-oriented language [
        <xref ref-type="bibr" rid="ref2">2</xref>
        ]. The optimization
has been completed and stable since December 2022 in Pharo 111, and it is used by users in newer
distributions. At the moment of this work, only Constant Blocks are already enabled by default. Clean
Blocks have to be enabled by the user; it will be enabled by default in Pharo 142.
      </p>
      <sec id="sec-2-1">
        <title>2.1. Pharo Bytecode</title>
        <p>
          Pharo code is compiled into bytecode and then executed by the Pharo Virtual Machine [
          <xref ref-type="bibr" rid="ref3">3</xref>
          ]. The bytecode
set is based on a stack machine, and the compiled methods include references to objects in a literals
section. For example, the bytecodes for the method verticalTick presented in Section 1 is:
1 &lt;07&gt; pushRcvr: 7
2 &lt;20&gt; pushConstant: #isVerticalTick
3 &lt;F9 01 00&gt; fullClosure: [ nil ]
4 &lt;A2&gt; send: detect:ifNone:
5 &lt;5C&gt; returnTop
"Push ’decorations’ variable"
"Push symbol #isVerticalTick"
"Create and Push the Closure"
"Send message #detect:ifNone:"
"Return"
        </p>
        <p>We see, in the bytecode number 3 fullClosure: [ nil ], the instruction for closure creation at
runtime. This bytecode creates the closure, binds it to the method context, and pushes the block closure
object to the stack.</p>
        <p>With the Clean Blocks optimization activated, the bytecode for the same method looks:
1 &lt;07&gt; pushRcvr: 7
2 &lt;20&gt; pushConstant: #isVerticalTick
3 &lt;21&gt; pushConstant: [ nil ]
4 &lt;A2&gt; send: detect:ifNone:
5 &lt;5C&gt; returnTop
"Push ’decorations’ variable"
"Push symbol #isVerticalTick"
"Push the Block Closure (already on the literals)"
"Send message #detect:ifNone:"
"Return"</p>
        <p>Here, the bytecode number 3 is replaced by pushConstant: [ nil ] because the block closure
was already created at compile time and saved into the literals list.</p>
        <p>For closures that are just returning a static value, like the [ nil ] in the example, we can even push
specialized Constant Blocks that, on execution, just return a constant, speeding up closure evaluation.</p>
      </sec>
      <sec id="sec-2-2">
        <title>2.2. Compiling Clean and Constant Blocks</title>
        <p>
          We implemented the Clean Blocks optimization by extending the OpalCompiler [
          <xref ref-type="bibr" rid="ref4">4</xref>
          ]. The optimization
is based on a closure AST analysis during method compilation. When the compiler has to compile a
closure node, the following code is executed to determine if it is clean:
BlockNode &gt;&gt; isClean
"It or any embedded blocks do not need self (message receiver object)"
self isAccessingSelf ifTrue: [ ^ false ].
"It or any embedded blocks do not need the outer context to return"
self hasNonLocalReturn ifTrue: [ ^ false ].
"There are no escaping vars accessed from the outer context"
self scope hasEscapingVars ifTrue: [ ^ false ].
        </p>
        <p>^ true</p>
        <p>If the closure is clean, then the compiler instantiates a CleanBlockClosure object and generates a
pushConstant: bytecode for it. Otherwise, it just compiles a fullClosure: instruction as always.
Both examples were presented previously.</p>
        <p>The compiler checks in addition if the block is returning just a constant (literal) for specialize Constant
Blocks:
BlockNode &gt;&gt; isConstant
"Is the block just returning a literal?"
^ body statements
ifEmpty: [ true "empty block returns nil" ]
ifNotEmpty: [:statements | statements size = 1 and: [statements first isLiteralNode] ]</p>
        <p>In this case, the compiler creates an instance of ConstantBlockClosure, a subclass of
CleanBlockClosure that implements a specialized code path to return the constant when the closure
is evaluated.</p>
      </sec>
      <sec id="sec-2-3">
        <title>2.3. Execution of Constant Blocks</title>
        <p>The only diference between Constant Blocks and Clean Blocks is the evaluation of the block. Both are
created at compile time, thus have the same performance characteristics for creation. However, the
Constant Blocks overrides the value method used to evaluate the closure.</p>
        <p>FullBlockClosure&gt;&gt;#value
"Activate the receiver, creating a closure activation (MethodContext)
whose closure is the receiver and whose caller is the sender of this
message. Supply the copied values to the activation as its copied
temps. Primitive. Essential."
&lt;primitive: 207&gt;
numArgs ~= 0 ifTrue:</p>
        <p>[self numArgsError: 0].</p>
        <p>^self primitiveFailed
ConstantBlockClosure&gt;&gt;#value</p>
        <p>^literal</p>
        <p>For Full Blocks, as well as Clean Blocks, the method calls a VM primitive that executes the
CompiledBlock of the closure. The failure code is only executed on primitive failure, leading to
a fast code path for the common case with no argument error.</p>
        <p>For Constant Blocks, the constant literal is stored in the object and just returned. To model the
failure behavior related to the number of arguments eficiently, we have subclasses for zero, one,
two, and three arguments. A constant closure with four or more arguments is always compiled as a
CleanBlockClosure.</p>
        <p>ConstantBlockClosure&gt;&gt;#value: anObject</p>
        <p>self numArgsError: 1</p>
        <p>The specialized class with overrides for each case leads to code without any conditions, and thus
faster execution.</p>
        <p>The bytecode of the CompiledBlock for Constant Blocks pushes the constant and then returns from
the block:
1 17 &lt;4F&gt; pushConstant: nil
2 18 &lt;5E&gt; blockReturn’</p>
        <p>Interestingly, the ConstantBlockClosure is compiled like Clean Blocks with a CompiledBlock,
even though this bytecode is never executed. The reason for this approach is that the system does not
need any special case for code relying on the bytecode of a block. An example for this is the senders of
feature: it will find a #symbol in Constant Blocks without the need to treat them specially.</p>
      </sec>
    </sec>
    <sec id="sec-3">
      <title>3. Evaluation</title>
      <p>We divide the evaluation of the presented work into three parts: First, we measure the improvements of
the specific optimized areas of the runtime, i.e., closure creation for Clean Blocks and closure execution
for Constant Blocks, to see the impact in the best scenario possible. Later, we analyse the distribution of
clean or constant closures in the base code, to approximate the scope of our optimizations on the Pharo
programs. Finally, measure the impact on the overall runtime by running more than 20 benchmarks.</p>
      <sec id="sec-3-1">
        <title>3.1. Optimization improvements</title>
        <p>Clean Blocks is an optimization against closures created at runtime, while Constant Blocks optimizes the
activation of closures. In this section, we use naive examples to measure the performance improvement
achieved by the optimizations for the same trivial block. For them, we use the tools presented in Pharo
12, especially the method BlockClosure » bench that measures how many times a block is evaluated
within 5 seconds.</p>
        <sec id="sec-3-1-1">
          <title>3.1.1. Clean Blocks: Creation</title>
          <p>Clean Blocks are created at compile time, avoiding the overhead of creating a closure at runtime. We
measure the performance gain with a naive, tiny benchmark. We compile the closure [1+2] with and
without the optimization for Clean Blocks:
"execute line by line"
OCCompilationContext optionCleanBlockClosure: true.
cleanEnabled := [ [1+2] ] bench.</p>
          <p>OCCompilationContext optionCleanBlockClosure: false.
cleanDisabled := [ [1+2] ] bench.
cleanDisabled compareTo: cleanEnabled "43351950.020/s * 9.073 = 393318782.487/s"</p>
          <p>The result of this particular case shows a speedup of 9x for the creation of Clean Blocks over Full
Blocks.</p>
        </sec>
        <sec id="sec-3-1-2">
          <title>3.1.2. Constant Blocks: Execution</title>
          <p>We compare the execution of Constant Blocks over normal activation of the CompiledBlock by the VM.
We run a trivial, tiny benchmark of evaluating the block [ nil ] compiled as ConstantBlockClosure
and as normal BlockClosure:
1 "execute line by line"
2 OCCompilationContext optionConstantBlockClosure: true.
3 block := [ nil ].
4 constEnabled := [ block value ] bench.
5
6 OCCompilationContext optionConstantBlockClosure: false.
7 block := [ nil ].
8 constDisabled := [ block value ] bench.
9
10 constDisabled compareTo: constEnabled "186672246.301/s * 1.414 = 263952774.090/s"</p>
          <p>The results show a speedup of 1.4x for the execution of Constant Blocks over CompiledBlock
activations.</p>
        </sec>
      </sec>
      <sec id="sec-3-2">
        <title>3.2. Block Distribution</title>
        <sec id="sec-3-2-1">
          <title>Blocks</title>
          <p>Full Blocks
Clean Blocks
Constant Blocks
All</p>
        </sec>
      </sec>
      <sec id="sec-3-3">
        <title>3.3. Impact on Runtime</title>
        <p>We evaluate the impact of the optimization on runtime by running 22 diferent benchmarks described
in Appendix A. We measure the number of activations for each kind of closure and the execution time,
per benchmark.</p>
        <p>
          FullCleanConst
3.3.1. Setup
We run a set of benchmarks using the Rebench framework [
          <xref ref-type="bibr" rid="ref5">5</xref>
          ]. We run all benchmarks using 30 iterations.
Micro benchmarks use 10 in-process with two (ignored) warmup iterations. Macro benchmarks,
consisting of running the test suite of several packages, use a single in-process iteration.
        </p>
        <p>All the Pharo images4 used in the evaluation were entirely recompiled with a given compiler
configuration (enable/disable Clean Blocks and/or Constant Blocks) and saved. Thus, Clean &amp; Constant Blocks
are already allocated in the heap during benchmark executions.</p>
        <p>We used a Linux Debian 5.10.140-1 distribution on a ARM64 Intel(R) Xeon(R) CPU E5620
@ 2.40GHz processor with 16 GB DDR3 of RAM.</p>
        <sec id="sec-3-3-1">
          <title>3.3.2. Closure activations</title>
          <p>To understand the impact of Clean &amp; Constant Blocks on our benchmark set, we measure the number of
Closure activations for each kind of closure. The results are presented in Figure 1.</p>
          <p>Except Slopstones, where almost all Closure activations are on Constant Blocks, all the benchmarks
activate more Full Blocks than Clean &amp; Constant Blocks.</p>
          <p>On average, 76% of all the Closure activations are on Full Blocks. The rest 24% of the Closure
activations made on Clean &amp; Constant Blocks are divided into 40% Clean Blocks and 60% Constant Blocks.</p>
          <p>After Slopstones, the benchmarks more impacted by our optimization are Kernel, Microdown, and
RegexDNA, with Closure activations between ~40% and 35% on Clean &amp; Constant Blocks.
3.3.3. Speed up
We evaluate the impact of our optimization on performance by measuring the execution time of each
benchmark. Figure 2 presents the result per benchmark. We make the following configurations:</p>
        </sec>
        <sec id="sec-3-3-2">
          <title>Unoptimized</title>
          <p>All the closures are compiled as Full Blocks. We use this configuration as the baseline.
Optimized Using our optimization, we create Clean &amp; Constant Blocks at compile time when it is
possible.</p>
          <p>As we showed in Section 3.3.2, the benchmark Slopstones is the most beneficial from our optimization,
so it has the highest speed improvement of 1.35x.</p>
          <p>The rest of the benchmarks present a speed-up variation between 0.95x and 1.07x, being BinaryTrees
and ThreadRing the worst cases. The average speed-up of all the benchmarks in our suite is 1.02x.</p>
          <p>These unexpected slowdowns and tiny increments are related to the quantity of Clean &amp; Constant
Blocks activated during all benchmarks (24%) and due to the increasing pressure on the Garbage Collector,
which we analyze in the next section.</p>
        </sec>
        <sec id="sec-3-3-3">
          <title>3.3.4. Garbage Collection overhead</title>
          <p>As our optimization creates Clean &amp; Constant Blocks before execution, it allocates more objects in the
heap during compilation, increasing the pressure on the Garbage Collector during compilation while
reducing it at execution time. We measure the time that the Garbage Collector is running for each
benchmark on each configuration. Results are presented in Figure 3.</p>
          <p>We observe that the benchmarks with slowdown presented in Section 3.3.3, BinaryTrees and
ThreadRing, show the highest increment of Garbage Collection overhead: 1.21x and 1.18x, respectively, relative
to Full Blocks.</p>
          <p>However, there are other benchmarks where the overhead decreased compared to Full Blocks. For
example, Compiler and Microdown present a Garbage Collection overhead of 0.77x and 0.80x, respectively,
relative to Full Blocks.
4Memory snapshot of a Pharo program.</p>
          <p>optimized
ptimized</p>
          <p>O
Compiler
optimized
ptimized</p>
          <p>O</p>
          <p>On average, the Garbage Collection overhead of Clean &amp; Constant Blocks along all benchmarks is
1.01x the overhead of Full Blocks.</p>
          <p>These are unexpected results. We thought that pre-allocating closures at compile time instead of
runtime would reduce the pressure on the Garbage Collector. A better understanding of why our
optimization impacts the Garbage Collection overhead in diferent ways is something to be analyzed in
the future by the authors.</p>
        </sec>
      </sec>
    </sec>
    <sec id="sec-4">
      <title>4. Related work</title>
      <p>
        Sussman and Steele [
        <xref ref-type="bibr" rid="ref6">6</xref>
        ] introduced closures in Scheme as first-class entities that encapsulate both
function code and the lexical environment in which they were defined.
      </p>
      <p>
        Goldberg and Robson [
        <xref ref-type="bibr" rid="ref1">1</xref>
        ] describe the implementation of blocks in Smalltalk-80, including the
representation of execution contexts and optimizations for method lookup and block invocation. It
should be noted that blocks in Smalltalk 80 are not closures.
      </p>
      <p>There are well-known transformations for optimizing closures based on outer context dependencies,
10
0
300
250
1</p>
      <p>
        1
UnoptimiOzepdtimized
such as lambda lifting5, transforming closures by turning free variables into explicit parameters, thereby
reducing their dependence on outer contexts, and flat-closure [
        <xref ref-type="bibr" rid="ref7">7</xref>
        ], a technique used to optimize how
variables from enclosing scopes are captured and accessed. Keep et al., [
        <xref ref-type="bibr" rid="ref8">8</xref>
        ] presents a set of optimizations
for closures creation and invocation, including free variables elimination and closure sharing. Clean
Blocks are one of the optimizations presented (Case 2a).
      </p>
      <p>Our optimization is not based on closure transformation, but on an escape analysis to determine if the
block can be pre-allocated at compile time. However, we are interested to see how those optimizations
work with Clean Blocks.</p>
      <p>The Java Virtual Machine [9] implements non-capturing (stateless) lambdas for expressions that do
not reference any variables from their enclosing scope. They are similar to Clean Blocks, but instead
of creating the closures at compile time, they implemented a factory method that returns the same
singleton object at runtime.
5https://en.wikipedia.org/wiki/Lambda_lifting</p>
    </sec>
    <sec id="sec-5">
      <title>5. Future work</title>
      <p>Future work is planned in two main directions. For one, we need to improve the analysis to find the
reason for the GC impact. The second idea is to analyze more optimizations.</p>
      <sec id="sec-5-1">
        <title>5.1. Improving the analysis</title>
        <p>For now, we restricted the dynamic analysis to closure activation (that is, execution of a closure). But as
clean blocks shift the creation from run-time to compile-time, we need to analyze how many closures
are created during a benchmark run.</p>
        <p>As already mentioned, we see Garbage Collection overhead that we cannot explain: clean blocks shift
closure creation from runtime to compile time, yet we see GC overhead increase. We plan to analyse in
detail the memory footprint of the benchmarks with the optimizations active to determine the causes
of the overhead, i.e., which part of the GC is spending more time, and understand the relations with our
work.</p>
      </sec>
      <sec id="sec-5-2">
        <title>5.2. More optimizations</title>
        <p>The closure creation byte-code has a, right now not used, flag to create a full closure at runtime without
an outer context. For execution, we only need an outer context if the closure has a non-local return. It
will be interesting to analyze how many closures need to be compiled with an outer context and what
the performance characteristics are.</p>
        <p>A second idea is to experiment to see if we can avoid accessing outer variables directly by using the
debugger infrastructure for specific patterns. This would allow us to turn non-clean blocks into clean
blocks at the expense of the speed of variable access. This could be useful as we see one pattern often: a
non-clean closure is handed over as a parameter for a failure case. The closure has to be created for
every execution of the method, but then it is rarely executed. If it is executed, execution happens in all
cases with the outer context still on the stack. This makes the variables thus accessible reflectively from
the context, without the need for the block being compiled as a full closure.</p>
      </sec>
    </sec>
    <sec id="sec-6">
      <title>Declaration on Generative AI</title>
      <p>The authors have not employed any Generative AI tools.
Machinery, New York, NY, USA, 2012, pp. 30–35. URL: https://doi.org/10.1145/2661103.2661106. doi:10.
1145/2661103.2661106.
[9] T. Lindholm, F. Yellin, G. Bracha, A. Buckley, The Java virtual machine specification, Pearson</p>
      <p>Education, 2014.</p>
    </sec>
    <sec id="sec-7">
      <title>A. Benchmarks</title>
      <p>Our set of classical micro benchmarks6 is as follows:
BinaryTrees. An adaptation of Hans Boehm’s GCBench for Garbage Collection.
Chameleons. Creates many threads to wait for mutex semaphores.
ChameneosRedux. Small problem size for Chameleons benchmark.
DeltaBlue. Classic object-oriented constraint solver.</p>
      <p>Fasta. DNA sequence generation algorithm based on weighted random selection.
KNucleotide. Map the DNA letters into a hash table to accumulate count values.
Mandelbrot. Plot the Mandelbrot set on an N-by-N bitmap.</p>
      <p>Meteor. Uses ByteStrings to solve a puzzle.</p>
      <p>NBody. Classic n-body simulation of the solar system.</p>
      <p>PiDigits. Generates and prints the first N digits of Pi.</p>
      <p>RegexDNA. A simple regex pattern and actions to manipulate FASTA format data.
ReverseComplement. Computes the reverse complement of a DNA sequence.
Richards. Simulates a task dispatcher on a multitasking operating system.
Slopstones. Smalltalk Low-level OPeration Stones, a series of low-level operations.
SpectralNorm. Calculate the spectral norm of a N-by-N matrix.</p>
      <p>ThreadRing. A token ring algorithm using threads and mutex semaphores.</p>
      <p>In addition, we run some applications as macro benchmarks:
Compiler. Tests for source-to-bytecode Pharo compiler.</p>
      <p>File Tests for file system access library.</p>
      <p>Kernel Tests for basic language features.</p>
      <p>Microdown. Tests for markup documentation library.</p>
      <p>Startup. Just evaluate 1 + 1, measure the time for starting up the system.</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          [1]
          <string-name>
            <given-names>A.</given-names>
            <surname>Goldberg</surname>
          </string-name>
          ,
          <string-name>
            <given-names>D.</given-names>
            <surname>Robson</surname>
          </string-name>
          ,
          <source>Smalltalk</source>
          <volume>80</volume>
          :
          <article-title>the Language and its Implementation, Addison Wesley</article-title>
          , Reading, Mass.,
          <year>1983</year>
          . URL: http://stephane.ducasse.free.fr/FreeBooks/BlueBook/Bluebook.pdf.
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          [2]
          <string-name>
            <given-names>S.</given-names>
            <surname>Ducasse</surname>
          </string-name>
          , G. Rakic,
          <string-name>
            <given-names>S.</given-names>
            <surname>Kaplar</surname>
          </string-name>
          ,
          <string-name>
            <surname>Q. D. O.</surname>
          </string-name>
          <article-title>written by A</article-title>
          .
          <string-name>
            <surname>Black</surname>
            ,
            <given-names>S.</given-names>
          </string-name>
          <string-name>
            <surname>Ducasse</surname>
            ,
            <given-names>O.</given-names>
          </string-name>
          <string-name>
            <surname>Nierstrasz</surname>
            ,
            <given-names>D. P. with D.</given-names>
          </string-name>
          <string-name>
            <surname>Cassou</surname>
            ,
            <given-names>M.</given-names>
          </string-name>
          <string-name>
            <surname>Denker</surname>
          </string-name>
          , Pharo 9 by Example,
          <source>Book on Demand - Keepers of the lighthouse</source>
          ,
          <year>2022</year>
          . URL: http://books.pharo.org.
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          [3]
          <string-name>
            <given-names>C.</given-names>
            <surname>Béra</surname>
          </string-name>
          ,
          <string-name>
            <given-names>E.</given-names>
            <surname>Miranda</surname>
          </string-name>
          ,
          <article-title>A bytecode set for adaptive optimizations</article-title>
          ,
          <source>in: International Workshop on Smalltalk Technologies (IWST)</source>
          ,
          <year>2014</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          [4]
          <string-name>
            <given-names>C.</given-names>
            <surname>Béra</surname>
          </string-name>
          ,
          <string-name>
            <given-names>M.</given-names>
            <surname>Denker</surname>
          </string-name>
          ,
          <article-title>Towards a flexible pharo compiler</article-title>
          ,
          <source>in: International Workshop on Smalltalk Technologies</source>
          <year>2013</year>
          ,
          <year>2013</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          [5]
          <string-name>
            <given-names>S.</given-names>
            <surname>Marr</surname>
          </string-name>
          , Rebench: Execute and document benchmarks reproducibly,
          <year>2018</year>
          . doi:
          <volume>10</volume>
          .5281/zenodo. 1311762, version 1.0.
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          [6]
          <string-name>
            <given-names>G. J.</given-names>
            <surname>Sussman</surname>
          </string-name>
          ,
          <string-name>
            <given-names>G. L.</given-names>
            <surname>Steele</surname>
          </string-name>
          ,
          <string-name>
            <surname>Scheme:</surname>
          </string-name>
          <article-title>An interpreter for extended lambda calculus</article-title>
          ,
          <source>Higher-Order and Symbolic Computation</source>
          <volume>11</volume>
          (
          <year>1998</year>
          )
          <fpage>405</fpage>
          -
          <lpage>439</lpage>
          . doi:
          <volume>10</volume>
          .1023/A:
          <fpage>1010035624696</fpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          [7]
          <string-name>
            <given-names>M. A.</given-names>
            <surname>Ertl</surname>
          </string-name>
          ,
          <string-name>
            <given-names>B.</given-names>
            <surname>Paysan</surname>
          </string-name>
          ,
          <article-title>Closures-the forth way</article-title>
          ,
          <source>in: 34th EuroForth Conference</source>
          ,
          <year>2018</year>
          , pp.
          <fpage>17</fpage>
          -
          <lpage>30</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          [8]
          <string-name>
            <given-names>A. W.</given-names>
            <surname>Keep</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Hearn</surname>
          </string-name>
          ,
          <string-name>
            <given-names>R. K.</given-names>
            <surname>Dybvig</surname>
          </string-name>
          ,
          <article-title>Optimizing closures in o(0) time</article-title>
          , in
          <source>: Proceedings of the 2012 Annual Workshop on Scheme and Functional Programming</source>
          , Scheme '
          <volume>12</volume>
          ,
          <string-name>
            <surname>Association</surname>
          </string-name>
          for Computing
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>