<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Eli Bendersky's website - C &amp; C++</title><link href="https://eli.thegreenplace.net/" rel="alternate"></link><link href="https://eli.thegreenplace.net/feeds/c-c.atom.xml" rel="self"></link><id>https://eli.thegreenplace.net/</id><updated>2025-11-25T03:49:52-08:00</updated><entry><title>Notes on the WASM Basic C ABI</title><link href="https://eli.thegreenplace.net/2025/notes-on-the-wasm-basic-c-abi/" rel="alternate"></link><published>2025-11-24T19:47:00-08:00</published><updated>2025-11-25T03:49:52-08:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2025-11-24:/2025/notes-on-the-wasm-basic-c-abi/</id><summary type="html">&lt;p&gt;The &lt;a class="reference external" href="https://github.com/WebAssembly/tool-conventions/tree/main"&gt;WebAssembly/tool-conventions&lt;/a&gt;
repository contains &amp;quot;Conventions supporting interoperability between tools
working with WebAssembly&amp;quot;.&lt;/p&gt;
&lt;p&gt;Of special interest, in contains the &lt;a class="reference external" href="https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md"&gt;Basic C ABI&lt;/a&gt; - an ABI
for representing C programs in WASM. This ABI is followed by compilers like Clang
with the &lt;tt class="docutils literal"&gt;wasm32&lt;/tt&gt; target. Rust is &lt;a class="reference external" href="https://blog.rust-lang.org/2025/04/04/c-abi-changes-for-wasm32-unknown-unknown/"&gt;also switching to this ABI&lt;/a&gt;
for …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The &lt;a class="reference external" href="https://github.com/WebAssembly/tool-conventions/tree/main"&gt;WebAssembly/tool-conventions&lt;/a&gt;
repository contains &amp;quot;Conventions supporting interoperability between tools
working with WebAssembly&amp;quot;.&lt;/p&gt;
&lt;p&gt;Of special interest, in contains the &lt;a class="reference external" href="https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md"&gt;Basic C ABI&lt;/a&gt; - an ABI
for representing C programs in WASM. This ABI is followed by compilers like Clang
with the &lt;tt class="docutils literal"&gt;wasm32&lt;/tt&gt; target. Rust is &lt;a class="reference external" href="https://blog.rust-lang.org/2025/04/04/c-abi-changes-for-wasm32-unknown-unknown/"&gt;also switching to this ABI&lt;/a&gt;
for &lt;tt class="docutils literal"&gt;extern &amp;quot;C&amp;quot;&lt;/tt&gt; code.&lt;/p&gt;
&lt;p&gt;This post contains some notes on this ABI, with annotated code samples and
diagrams to help visualize what the emitted WASM code is doing. Hereafter, &amp;quot;the
ABI&amp;quot; refers to this Basic C ABI.&lt;/p&gt;
&lt;div class="section" id="preface-the-wasm-value-stack-and-linear-memory"&gt;
&lt;h2&gt;Preface: the WASM value stack and linear memory&lt;/h2&gt;
&lt;p&gt;In these notes, annotated WASM snippets often contain descriptions of the state
of the WASM value stack at a given point in time. Unless otherwise specified,
&amp;quot;TOS&amp;quot; refers to &amp;quot;Top Of value Stack&amp;quot;, and the notation &lt;tt class="docutils literal"&gt;[ x&amp;nbsp; y ]&lt;/tt&gt; means the
stack has &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; on top, with &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; right under it (and possibly some other
stuff that's not relevant to the discussion under &lt;tt class="docutils literal"&gt;x&lt;/tt&gt;); in this notation,
the stack grows &amp;quot;to the right&amp;quot;.&lt;/p&gt;
&lt;p&gt;The WASM value stack has no linear memory representation and cannot be
addressed, so it's meaningless to discuss whether the stack grows towards lower
or higher addresses. The value stack is simply an abstract stack, where values
can be pushed onto or popped off its &amp;quot;top&amp;quot;.&lt;/p&gt;
&lt;p&gt;Whenever addressing is required, the ABI specifies explicitly managing a
separate stack in linear memory. This stack is very similar to how stacks are
managed in hardware assembly languages (except that in the ABI this stack
pointer is held in a global variable, and is not a special register), and it's
called the &amp;quot;linear stack&amp;quot;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="scalar-parameters-and-returns"&gt;
&lt;h2&gt;Scalar parameters and returns&lt;/h2&gt;
&lt;p&gt;By &amp;quot;scalar&amp;quot; I mean basic C types like &lt;tt class="docutils literal"&gt;int&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;double&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;char&lt;/tt&gt;. For
these, using the WASM value stack is sufficient, since WASM functions can accept
an arbitrary number of scalar parameters.&lt;/p&gt;
&lt;p&gt;This C function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add_three&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Will be compiled into something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nv"&gt;$add_three&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;     &lt;span class="c1"&gt;;; [ y ]&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;     &lt;span class="c1"&gt;;; [ y  x ]&lt;/span&gt;
  &lt;span class="nb"&gt;i32.add&lt;/span&gt;         &lt;span class="c1"&gt;;; [ x+y ]&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;     &lt;span class="c1"&gt;;; [ x+y  z ]&lt;/span&gt;
  &lt;span class="nb"&gt;i32.add&lt;/span&gt;         &lt;span class="c1"&gt;;; [ x+y+z ]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And can be called by pushing three values onto the stack and invoking
&lt;tt class="docutils literal"&gt;call $add_three&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;The ABI specifies that all integral types 32-bit and smaller will be passed
as &lt;tt class="docutils literal"&gt;i32&lt;/tt&gt;, with the smaller types appropriately sign or zero extended. For
example, consider this C function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add_three_chars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It's compiled to the almost same code as &lt;tt class="docutils literal"&gt;add_three&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nv"&gt;$add_three_chars&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nb"&gt;i32.add&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;i32.add&lt;/span&gt;
  &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="err"&gt;.ext&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="err"&gt;_s&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Except the last &lt;tt class="docutils literal"&gt;i32.extend8_s&lt;/tt&gt;, which takes the lowest 8 bits of the value
on TOS and sign-extends them to the full &lt;tt class="docutils literal"&gt;i32&lt;/tt&gt; (effectively ignoring all
the higher bits). Similarly, when &lt;tt class="docutils literal"&gt;$add_three_chars&lt;/tt&gt; is called, each of its
parameters goes through &lt;tt class="docutils literal"&gt;i32.extend8_s&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;There are additional oddities that we won't get deep into, like passing
&lt;tt class="docutils literal"&gt;__int128&lt;/tt&gt; values via two &lt;tt class="docutils literal"&gt;i64&lt;/tt&gt; parameters.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pointers"&gt;
&lt;h2&gt;Pointers&lt;/h2&gt;
&lt;p&gt;C pointers are just scalars, but it's still educational to review how
they are handled in the ABI. Pointers to any type are passed in &lt;tt class="docutils literal"&gt;i32&lt;/tt&gt; values;
the compiler knows they are pointers, though, and emits the appropriate
instructions. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add_indirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Is compiled to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nv"&gt;$add_indirect&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;         &lt;span class="c1"&gt;;; [ ptr_sum ]&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;         &lt;span class="c1"&gt;;; [ ptr_sum  y ]&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;         &lt;span class="c1"&gt;;; [ ptr_sum  y  x ]&lt;/span&gt;
  &lt;span class="nb"&gt;i32.add&lt;/span&gt;             &lt;span class="c1"&gt;;; [ ptr_sum  x+y ]&lt;/span&gt;
  &lt;span class="nb"&gt;local.tee&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;         &lt;span class="c1"&gt;;; x &amp;lt;- x+y, leaving stack intact&lt;/span&gt;
  &lt;span class="nb"&gt;i32.store&lt;/span&gt;           &lt;span class="c1"&gt;;; store x+y into *ptr_sum&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;         &lt;span class="c1"&gt;;; [ x+y ] for returning&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Recall that in WASM, there's no difference between an &lt;tt class="docutils literal"&gt;i32&lt;/tt&gt; representing an
address in linear memory and an &lt;tt class="docutils literal"&gt;i32&lt;/tt&gt; representing just a number.
&lt;tt class="docutils literal"&gt;i32.store&lt;/tt&gt; expects &lt;tt class="docutils literal"&gt;[ addr&amp;nbsp; value ]&lt;/tt&gt; on TOS, and does &lt;tt class="docutils literal"&gt;*addr = value&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Note that the &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; parameter isn't needed any longer after the sum is
computed, so it's reused later on to hold the return value. WASM parameters are
treated just like other locals (as in C).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="passing-parameters-through-linear-memory"&gt;
&lt;h2&gt;Passing parameters through linear memory&lt;/h2&gt;
&lt;p&gt;According to the ABI, while scalars and single-element structs or unions are
passed to a callee via WASM function parameters (as shown above), for larger
aggregates the compiler utilizes linear memory.&lt;/p&gt;
&lt;p&gt;Specifically, each function gets a &amp;quot;frame&amp;quot; in a region of linear memory
allocated for the linear stack. This region grows downwards from high
to low addresses &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;, and the global &lt;tt class="docutils literal"&gt;$__stack_pointer&lt;/tt&gt; points at the bottom
of the frame:&lt;/p&gt;
&lt;img alt="WASM C ABI linear stack" class="align-center" src="https://eli.thegreenplace.net/images/2025/wasm-c-abi-linear-stack.png" /&gt;
&lt;p&gt;Consider this code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="n"&gt;__attribute__&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;noinline&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pair_calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;do_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pair_calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When &lt;tt class="docutils literal"&gt;do_work&lt;/tt&gt; is compiled to WASM, prior to calling &lt;tt class="docutils literal"&gt;pair_calculate&lt;/tt&gt; it
copies &lt;tt class="docutils literal"&gt;pp&lt;/tt&gt; into a location in linear memory, and passes the address of this
location to &lt;tt class="docutils literal"&gt;pair_calculate&lt;/tt&gt;. This location is on the linear stack, which
is maintained using the &lt;tt class="docutils literal"&gt;$__stack_pointer&lt;/tt&gt; global. Here's the compiled WASM
for &lt;tt class="docutils literal"&gt;do_work&lt;/tt&gt; (I also gave its local variable a meaningful name, for
readability):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nv"&gt;$do_work&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;$sp&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;global.get&lt;/span&gt; &lt;span class="nv"&gt;$__stack_pointer&lt;/span&gt;
  &lt;span class="nb"&gt;i32.const&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
  &lt;span class="nb"&gt;i32.sub&lt;/span&gt;
  &lt;span class="nb"&gt;local.tee&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;                   &lt;span class="c1"&gt;;; sp &amp;lt;- __stack_pointer - 16&lt;/span&gt;
  &lt;span class="nb"&gt;global.set&lt;/span&gt; &lt;span class="nv"&gt;$__stack_pointer&lt;/span&gt;   &lt;span class="c1"&gt;;; update __stack_pointer as well&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nb"&gt;i32.store&lt;/span&gt; &lt;span class="k"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;           &lt;span class="c1"&gt;;; mem[sp+12] = y&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nb"&gt;i32.store&lt;/span&gt; &lt;span class="k"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;            &lt;span class="c1"&gt;;; mem[sp+8] = x&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;                   &lt;span class="c1"&gt;;; [ sp ]&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;                   &lt;span class="c1"&gt;;; [ sp  sp ]&lt;/span&gt;

  &lt;span class="c1"&gt;;; Do a 64-bit load from mem[sp+8], this loads the entire pair into&lt;/span&gt;
  &lt;span class="c1"&gt;;; a single i64.&lt;/span&gt;
  &lt;span class="nb"&gt;i64.load&lt;/span&gt; &lt;span class="k"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="k"&gt;align&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
  &lt;span class="nb"&gt;i64.store&lt;/span&gt;                     &lt;span class="c1"&gt;;; mem[sp] = pair-as-i64&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;                   &lt;span class="c1"&gt;;; [ sp ]&lt;/span&gt;
  &lt;span class="nb"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;$pair_calculate&lt;/span&gt;          &lt;span class="c1"&gt;;; call pair_calculate, passing it sp&lt;/span&gt;
  &lt;span class="nb"&gt;local.set&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                   &lt;span class="c1"&gt;;; x = result&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;                   &lt;span class="c1"&gt;;; [ sp ]&lt;/span&gt;
  &lt;span class="nb"&gt;i32.const&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
  &lt;span class="nb"&gt;i32.add&lt;/span&gt;                       &lt;span class="c1"&gt;;; [ sp+16 ]&lt;/span&gt;
  &lt;span class="nb"&gt;global.set&lt;/span&gt; &lt;span class="nv"&gt;$__stack_pointer&lt;/span&gt;   &lt;span class="c1"&gt;;; __stack_pointer back to its original value&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                   &lt;span class="c1"&gt;;; [ x ] for return&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some notes about this code:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;There are two instance of the pair &lt;tt class="docutils literal"&gt;pp&lt;/tt&gt; in linear memory prior to the call
to &lt;tt class="docutils literal"&gt;pair_calculate&lt;/tt&gt;: the original one from the initialization statement
(at offset 8), and a copy created for passing into &lt;tt class="docutils literal"&gt;pair_calculate&lt;/tt&gt; (at
offset 0). Theoretically, as &lt;tt class="docutils literal"&gt;pp&lt;/tt&gt; is unused used after the call, the
compiler could do better here and keep only a single copy.&lt;/li&gt;
&lt;li&gt;The stack pointer is decremented by 16, and restored at the end of the
function.&lt;/li&gt;
&lt;li&gt;The first few instructions - where the stack pointer is adjusted - are
usually called the &lt;em&gt;prologue&lt;/em&gt; of the function. In the same vein, the last
few instructions where the stack pointer is reset back to where it was at
the entry are called the &lt;em&gt;epilogue&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before &lt;tt class="docutils literal"&gt;pair_calculate&lt;/tt&gt; is called, the linear stack looks like this:&lt;/p&gt;
&lt;img alt="WASM C ABI linear stack view at entry to pair_calculate" class="align-center" src="https://eli.thegreenplace.net/images/2025/wasm-c-abi-passing-pair.png" /&gt;
&lt;p&gt;Following the ABI, the code emitted for &lt;tt class="docutils literal"&gt;pair_calculate&lt;/tt&gt; takes &lt;tt class="docutils literal"&gt;Pair*&lt;/tt&gt;
(by reference, instead of by value as the original C code):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nv"&gt;$pair_calculate&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;           &lt;span class="c1"&gt;;; [ addr ]&lt;/span&gt;

  &lt;span class="c1"&gt;;; Recall that what&amp;#39;s stored at mem[addr] is a pair, with &amp;#39;x&amp;#39;, followed by &amp;#39;y&amp;#39;.&lt;/span&gt;
  &lt;span class="nb"&gt;i32.load&lt;/span&gt; &lt;span class="k"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;     &lt;span class="c1"&gt;;; [ pair.y ]&lt;/span&gt;
  &lt;span class="nb"&gt;i32.const&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="nb"&gt;i32.mul&lt;/span&gt;               &lt;span class="c1"&gt;;; [ 3*pair.y ]&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nb"&gt;i32.load&lt;/span&gt;              &lt;span class="c1"&gt;;; [ 3*pair.y  pair.x ]&lt;/span&gt;
  &lt;span class="nb"&gt;i32.const&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
  &lt;span class="nb"&gt;i32.mul&lt;/span&gt;               &lt;span class="c1"&gt;;; [ 3*pair.y  7*pair.x ]&lt;/span&gt;
  &lt;span class="nb"&gt;i32.add&lt;/span&gt;               &lt;span class="c1"&gt;;; [ 3*pair.y+7*pair.x ]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each function that needs linear stack space is responsible for adjusting the
stack pointer and restoring it to its original place at the end. This naturally
enables nested function calls; suppose we have some function &lt;tt class="docutils literal"&gt;a&lt;/tt&gt; calling
function &lt;tt class="docutils literal"&gt;b&lt;/tt&gt; which, in turn, calls function &lt;tt class="docutils literal"&gt;c&lt;/tt&gt;, and let's assume all of
these need to allocate space on the linear stack. This is how the linear
stack looks after &lt;tt class="docutils literal"&gt;c&lt;/tt&gt;'s prologue:&lt;/p&gt;
&lt;img alt="WASM C ABI linear stack showing nested frames" class="align-center" src="https://eli.thegreenplace.net/images/2025/wasm-c-abi-nested-frames.png" /&gt;
&lt;p&gt;Since each function knows how much stack space it has allocated, it's able to
properly restore &lt;tt class="docutils literal"&gt;$__stack_pointer&lt;/tt&gt; to the bottom of its caller's frame
before returning.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="returning-values-through-linear-memory"&gt;
&lt;h2&gt;Returning values through linear memory&lt;/h2&gt;
&lt;p&gt;What about returning values of aggregate types? According to the ABI, these
are also handled indirectly; a pointer parameter is &lt;em&gt;prepended&lt;/em&gt; to the
parameter list of the function. The function writes its return value into
this address.&lt;/p&gt;
&lt;p&gt;The following function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;make_pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Is compiled to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;;; Note that the WASM function has three parameters an no return values.&lt;/span&gt;
&lt;span class="c1"&gt;;; The first parameter (local 0) is the address where it should store its&lt;/span&gt;
&lt;span class="c1"&gt;;; return value.&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nv"&gt;$make_pair&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;i32.store&lt;/span&gt; &lt;span class="k"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;      &lt;span class="c1"&gt;;; ret_addr[4] = y&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nb"&gt;i32.store&lt;/span&gt;               &lt;span class="c1"&gt;;; ret_addr[0] = x&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here's a function that calls it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;do_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;make_pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the corresponding WASM:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nv"&gt;$do_work&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;;; local 2 to hold the address for make_pair&amp;#39;s return value.&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;local&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;global.get&lt;/span&gt; &lt;span class="nv"&gt;$__stack_pointer&lt;/span&gt;   &lt;span class="c1"&gt;;; sp &amp;lt;- __stack_pointer - 16&lt;/span&gt;
  &lt;span class="nb"&gt;i32.const&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
  &lt;span class="nb"&gt;i32.sub&lt;/span&gt;
  &lt;span class="nb"&gt;local.tee&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;                   &lt;span class="c1"&gt;;; save sp in local 2&lt;/span&gt;
  &lt;span class="nb"&gt;global.set&lt;/span&gt; &lt;span class="nv"&gt;$__stack_pointer&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;i32.const&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
  &lt;span class="nb"&gt;i32.add&lt;/span&gt;                       &lt;span class="c1"&gt;;; [ sp+8 ]&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                   &lt;span class="c1"&gt;;; [ sp+8  x ]&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                   &lt;span class="c1"&gt;;; [ sp+8  x  y ]&lt;/span&gt;

  &lt;span class="c1"&gt;;; make_pair is called with three parameters: the address for where to&lt;/span&gt;
  &lt;span class="c1"&gt;;; store its return pair, x and y.&lt;/span&gt;
  &lt;span class="nb"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;$make_pair&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;i32.load&lt;/span&gt; &lt;span class="k"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
  &lt;span class="nb"&gt;local.set&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                   &lt;span class="c1"&gt;;; local 1 = pp.x&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;i32.load&lt;/span&gt; &lt;span class="k"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;
  &lt;span class="nb"&gt;local.set&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                   &lt;span class="c1"&gt;;; local 0 = pp.y&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;i32.const&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
  &lt;span class="nb"&gt;i32.add&lt;/span&gt;
  &lt;span class="nb"&gt;global.set&lt;/span&gt; &lt;span class="nv"&gt;$__stack_pointer&lt;/span&gt;   &lt;span class="c1"&gt;;; __stack_pointer back to its original value&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nb"&gt;i32.const&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="nb"&gt;i32.mul&lt;/span&gt;                       &lt;span class="c1"&gt;;; [ 3*pp.y ]&lt;/span&gt;
  &lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nb"&gt;i32.const&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
  &lt;span class="nb"&gt;i32.mul&lt;/span&gt;                       &lt;span class="c1"&gt;;; [ 3*pp.y  7*pp.x ]&lt;/span&gt;
  &lt;span class="nb"&gt;i32.add&lt;/span&gt;                       &lt;span class="c1"&gt;;; [ 3*pp.y+7*pp.x ]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that this function only uses 8 bytes of its stack frame, but allocates 16;
this is because the ABI dictates 16-byte alignment for the stack pointer.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="advanced-topics"&gt;
&lt;h2&gt;Advanced topics&lt;/h2&gt;
&lt;p&gt;There are some advanced topics mentioned in the ABI that these notes don't cover
(at least for now), but I'll mention them here for completeness:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&amp;quot;Red zone&amp;quot; - leaf functions have access to 128 bytes of &lt;a class="reference external" href="https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64"&gt;red zone&lt;/a&gt;
below the
stack pointer. I found this difficult to observe in practice &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.
Since we don't issue system calls directly
in WASM, it's tricky to conjure a realistic leaf function that requires
the linear stack (instead of just using WASM locals).&lt;/li&gt;
&lt;li&gt;A separate frame pointer (global value) to be used for functions that require
dynamic stack allocation (such as using &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Variable-length_array"&gt;C's VLAs&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;A separate base pointer to be used for functions that require
alignment &amp;gt; 16 bytes on the stack.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p class="first"&gt;This is similar &lt;a class="reference external" href="https://eli.thegreenplace.net/2011/02/04/where-the-top-of-the-stack-is-on-x86/"&gt;to x86&lt;/a&gt;.
For the WASM C ABI, a good reason is provided for the direction: WASM
load and store instructions have an &lt;em&gt;unsigned&lt;/em&gt; constant called
&lt;tt class="docutils literal"&gt;offset&lt;/tt&gt; that can be used to add a positive offset to the address
parameter without extra instructions.&lt;/p&gt;
&lt;p class="last"&gt;Since &lt;tt class="docutils literal"&gt;$__stack_pointer&lt;/tt&gt; points to the lowest address in the frame,
these offsets can be used to efficiently access any value on the stack.&lt;/p&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;If you have a nice example showing it using Clang/LLVM, please drop me
a note!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="WebAssembly"></category><category term="C &amp; C++"></category><category term="Compilation"></category></entry><entry><title>Implementing Forth in Go and C</title><link href="https://eli.thegreenplace.net/2025/implementing-forth-in-go-and-c/" rel="alternate"></link><published>2025-08-26T20:38:00-07:00</published><updated>2025-08-27T03:39:03-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2025-08-26:/2025/implementing-forth-in-go-and-c/</id><summary type="html">&lt;p&gt;I first ran into Forth about 20 years ago when reading a book about
&lt;a class="reference external" href="https://www.oreilly.com/library/view/designing-embedded-hardware/0596007558/"&gt;designing embedded hardware&lt;/a&gt;.
The reason I got the book back then was to actually learn more about the HW
aspects, so having skimmed the Forth chapter I just registered an &amp;quot;oh, this is neat&amp;quot;
mental note …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I first ran into Forth about 20 years ago when reading a book about
&lt;a class="reference external" href="https://www.oreilly.com/library/view/designing-embedded-hardware/0596007558/"&gt;designing embedded hardware&lt;/a&gt;.
The reason I got the book back then was to actually learn more about the HW
aspects, so having skimmed the Forth chapter I just registered an &amp;quot;oh, this is neat&amp;quot;
mental note and moved on with my life. Over the last two decades I
heard about Forth a few more times here and there, such as that time when
&lt;a class="reference external" href="https://factorcode.org/"&gt;Factor&lt;/a&gt; was talked about for a brief period, maybe
10-12 years ago or so.&lt;/p&gt;
&lt;p&gt;It always occupied a slot in the &amp;quot;weird language&amp;quot; category inside my brain, and
I never paid it much attention. Until June this year, when a couple of factors
combined fortuitously:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;After spending much of the &lt;a class="reference external" href="https://eli.thegreenplace.net/archives/2025"&gt;earlier part of 2025&lt;/a&gt;
exploring the inner workings
of LLMs and digging in random mathy and algorithmic topics, I had an itch
to just write some code.&lt;/li&gt;
&lt;li&gt;I somehow found &lt;a class="reference external" href="https://ratfactor.com/forth/the_programming_language_that_writes_itself.html"&gt;Dave Gauer's page about Forth&lt;/a&gt;
and also the one on &lt;a class="reference external" href="https://ratfactor.com/forth/implementing"&gt;Implementing a Forth&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And something clicked. I'm going to implement a Forth, because... why not?&lt;/p&gt;
&lt;p&gt;So I spent much of my free hacking time over the past two months learning
about Forth and implementing &lt;em&gt;two&lt;/em&gt; of them.&lt;/p&gt;
&lt;div class="section" id="forth-the-user-level-and-the-hacker-level"&gt;
&lt;h2&gt;Forth: the user level and the hacker level&lt;/h2&gt;
&lt;p&gt;It's useful to think of Forth (at least &lt;a class="reference external" href="https://forth-standard.org/"&gt;standard Forth&lt;/a&gt;,
not offshoots like Factor) as having two different &amp;quot;levels&amp;quot;:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;strong&gt;User&lt;/strong&gt; level: you just want to use the language to write programs. Maybe
you're indeed bringing up new hardware, and find Forth a useful
calculator + REPL + script language. You don't care about Forth's
implementation or its soul, you just want to complete your task.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hacker&lt;/strong&gt; level: you're interested in the deeper soul of Forth. Isn't it
amazing that even control flow constructs like &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;IF...THEN&lt;/span&gt;&lt;/tt&gt; or loops like
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;BEGIN...UNTIL&lt;/span&gt;&lt;/tt&gt; are just Forth words, and if you wanted, you could implement
your own control flow constructs and have them be first-class citizens, as
seamless and efficient as the standard ones?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Another way to look at it (useful if you belong to a certain crowd) is that
user-level Forth is like Lisp without macros, and hacker-level Forth has macros
enabled. Lisp can still be great and useful without macros, but macros take
it to an entire new level and also unlock the deeper soul of the language.&lt;/p&gt;
&lt;p&gt;This distinction will be important when discussing my Forth implementations
below.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="goforth-and-ctil"&gt;
&lt;h2&gt;goforth and ctil&lt;/h2&gt;
&lt;img alt="Logo of goforth" class="align-center" src="https://eli.thegreenplace.net/images/pages/goforth-logo-sm.png" /&gt;
&lt;p&gt;There's a certain way Forth is supposed to be implemented; this is how it was
originally designed, and if you get closer to the hacker level, it
becomes apparent that you're pretty much required to implement it this way -
otherwise supporting all of the language's standard words will be very
difficult. I'm talking about the classical approach of a linked dictionary,
where a word is represented as a &amp;quot;threaded&amp;quot; list &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;, and this dictionary is
available for user code to augment and modify. Thus, much of the Forth
implementation can be written in Forth itself.&lt;/p&gt;
&lt;p&gt;The first implementation I tried is stubbornly different. Can we just make a
pure interpreter? This is what &lt;a class="reference external" href="https://github.com/eliben/goforth"&gt;goforth&lt;/a&gt;
is trying to explore (the Go implementation located in the root directory of
that repository). Many built-in words are supported - definitely enough to
write useful programs - and compilation
(the definition of new Forth words using &lt;tt class="docutils literal"&gt;: word ... ;&lt;/tt&gt;) is implemented by
storing the actual string following the word name in the dictionary, so it can
be interpreted when the word is invoked.&lt;/p&gt;
&lt;p&gt;This was an interesting approach and in some sense, it &amp;quot;works&amp;quot;. For the user
level of Forth, this is perfectly usable (albeit slow). However, it's
insufficient for the hacker level, because the host language interpreter (the
one in Go) has all the control, so it's impossible to implement &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;IF...THEN&lt;/span&gt;&lt;/tt&gt; in
Forth, for example (it has to be implemented in the host language).&lt;/p&gt;
&lt;p&gt;That was a fun way to get a deeper sense of what Forth is about, but I did want
to implement the hacker level as well, so the second implementation -
&lt;a class="reference external" href="https://github.com/eliben/goforth/tree/main/ctil"&gt;ctil&lt;/a&gt; - does just that.
It's inspired by the &lt;a class="reference external" href="http://git.annexia.org/?p=jonesforth.git"&gt;jonesforth&lt;/a&gt;
assembly implementation, but done in C instead &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ctil actually lets us implement major parts of Forth in Forth itself. For
example, &lt;tt class="docutils literal"&gt;variable&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;variable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;cells&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;allot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Conditionals:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;\ IF, ELSE, THEN work together to compile to lower-level branches.&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;\&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;\ IF ... THEN compiles to:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;\   0BRANCH OFFSET true-part rest&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;\ where OFFSET is the offset of rest&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;\&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;\ IF ... ELSE ... THEN compiles to :&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;\   0BRANCH OFFSET true-part BRANCH OFFSET2 false-part rest&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;\ where OFFSET is the offset of false-part and OFFSET2 is the offset of rest&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="kn"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;immediate&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nf"&gt;branch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;here&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kn"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;immediate&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;dup&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;here&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;swap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;swap&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kn"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;immediate&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;branch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;here&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;swap&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;dup&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;here&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;swap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;swap&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These are actual examples of ctil's &amp;quot;prelude&amp;quot; - a Forth file loaded before any
user code. If you understand Forth, this code is actually rather mind-blowing.
We compile &lt;tt class="docutils literal"&gt;IF&lt;/tt&gt; and the other words by directly laying our their low-level
representation in memory, and different words communicate with each other
using the data stack &lt;em&gt;during compilation&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="thoughts-on-forth-itself"&gt;
&lt;h2&gt;Thoughts on Forth itself&lt;/h2&gt;
&lt;p&gt;Forth made perfect sense in the historic context in which it was created in
the early 1970s. Imagine having some HW connected to your computer (a telescope
in the case of Forth's creator), and you have to interact with it. In terms
of languages at your disposal - you don't have much, even BASIC wasn't invented
yet. Perhaps your machine still didn't have a C compiler ported to it; C
compilers aren't simple, and C isn't very great for exploratory scripting
anyway. So you mostly just have your assembly language and whatever you build
on top.&lt;/p&gt;
&lt;p&gt;Forth is easy to implement in assembly and it gives you a much higher-level
language; you can use it as a calculator, as a REPL, and as a DSL for pretty
much anything due to its composable nature.&lt;/p&gt;
&lt;p&gt;Forth certainly has interesting aspects; it's a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Concatenative_programming_language"&gt;concatenative language&lt;/a&gt;,
and thus inherently &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Tacit_programming"&gt;point-free&lt;/a&gt;.
A classical example is that instead of writing the following in a more
traditional syntax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;eat(bake(prove(mix(ingredients))))
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You just write this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ingredients mix prove bake eat
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There is no need to explicitly pass parameters, or to explicitly return results.
Everything happens implicitly on the stack.&lt;/p&gt;
&lt;p&gt;This is useful for REPL-style programming where you use your language not
necessarily for writing large programs, but more for interactive instructions to
various HW devices. This dearth of syntax is also what makes Forth simple
to implement.&lt;/p&gt;
&lt;p&gt;All that said, in my mind Forth is firmly in the &amp;quot;weird language&amp;quot; category;
it's instructive to learn and to implement, but I wouldn't actually use it
for anything real these days. The stack-based programming model is cool for
very terse point-free programs, but it's not particularly readable and hard
to reason about without extensive comments, in my experience.&lt;/p&gt;
&lt;p&gt;Consider the implementation of a pretty standard Forth word: &lt;tt class="docutils literal"&gt;+!&lt;/tt&gt;. It expects
and address at the top of stack, and an addend below it. It adds the addend to
the value stored at that address. Here's a Forth implementation from
ctil's prelude:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;+!&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;( addend addr -- )&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;tuck&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;( addr addend addr )&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;@&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;( addr addend value-at-addr )&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;+&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;( addr updated-value )&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;swap&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;( updated-value addr )&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Look at that stack wrangling! It's really hard to follow what goes where without
the detailed comments showing the stack layout on the right of each instruction
(a common practice for Forth programs). Sure, we can create additional words
that would make this simpler, but that just increases the lexicon of words to
know.&lt;/p&gt;
&lt;p&gt;My point is, there's fundamental difficulty here. When you see this C code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Even without any documentation, you can immediately know several important
things:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;bar&lt;/tt&gt; has one parameter and one return value&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;foo&lt;/tt&gt; has two parameters and one return value&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;func&lt;/tt&gt; also has two parameters and one return value&lt;/li&gt;
&lt;li&gt;It's immediately obvious how the various values flow from one function call
to the next.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Written in Forth &lt;a class="footnote-reference" href="#footnote-3" id="footnote-reference-3"&gt;[3]&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;How can you know the arity of the functions without adding explicit comments?
Sure, if you have a handful of words like &lt;tt class="docutils literal"&gt;bar&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;foo&lt;/tt&gt; you know like the
back of your hand, this is easy. But imagine reading a large, unfamiliar code
base full of code like this and trying to comprehend it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="summary-and-links"&gt;
&lt;h2&gt;Summary and links&lt;/h2&gt;
&lt;p&gt;The source code of my &lt;a class="reference external" href="https://github.com/eliben/goforth"&gt;goforth project is on GitHub&lt;/a&gt;; both
implementations are there, with a comprehensive test harness that tests both.&lt;/p&gt;
&lt;p&gt;The learn Forth itself, I found these resources very useful:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://ratfactor.com/forth/the_programming_language_that_writes_itself.html"&gt;Dave Gauer's Forth page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.forth.com/starting-forth/"&gt;Starting Forth&lt;/a&gt; - a free online
book / tutorial on Forth for beginners&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To learn how to implement Forth:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://ratfactor.com/forth/implementing"&gt;Dave Gauer's page on Implementing a Forth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://git.annexia.org/?p=jonesforth.git"&gt;jonesforth&lt;/a&gt; implementation&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://archive.org/details/R.G.LoeligerThreadedInterpretiveLanguagesTheirDesignAndImplementationByteBooks1981"&gt;Threaded Interpretive Languages&lt;/a&gt; - an
old but nice book that explains how Forth implementations typically work&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Implementing Forth is a great self-improvement project for a coder; there's a
pleasantly challenging hump of understanding to overcome, and you gain valuable
insights into stack machines, interpretation vs. compilation and mixing these
levels of abstraction in cool ways.&lt;/p&gt;
&lt;p&gt;Also, implementing programming languages
from scratch is fun! It's hard to beat the feeling of getting to interact with
your implementation for the first time, and then iterating on improving it
and making it more featureful. &lt;a class="reference external" href="https://www.urbandictionary.com/define.php?term=One+More+Turn+Syndrome"&gt;Just one more word&lt;/a&gt;!&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This has nothing to do with threads in the sense of concurrency.
Rather, it's thread like in sewing, where the elements of the list
are all connected to each other as if with a thread. See
&lt;a class="reference external" href="https://wiki.c2.com/?ThreadedInterpretiveLanguage"&gt;this page&lt;/a&gt; for
more details.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p class="first"&gt;Which is another deviation from the norm. Forth is really supposed to
be implemented in assembly - this is what it was designed for, and it's
very clear from its structure that it must be so in order to achieve
peak performance.&lt;/p&gt;
&lt;p&gt;But where's the fun in doing things the way they were supposed to be
done? Besides, jonesforth is already a perfectly fine Forth implementation
in assembly, so I wouldn't have learned much by just copying it.&lt;/p&gt;
&lt;p class="last"&gt;I had a lot of fun coding in C for this one; it's been a while since I
last wrote non-trivial amounts of C, and I found it very enjoyable.&lt;/p&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Assuming the convention that multi-parameter functions have their
parameters pushed to the stack from left to right.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="C &amp; C++"></category><category term="Compilation"></category><category term="Go"></category></entry><entry><title>Plugins case study: Envoy WASM extensions</title><link href="https://eli.thegreenplace.net/2023/plugins-case-study-envoy-wasm-extensions/" rel="alternate"></link><published>2023-06-03T15:14:00-07:00</published><updated>2024-02-29T13:32:33-08:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2023-06-03:/2023/plugins-case-study-envoy-wasm-extensions/</id><summary type="html">&lt;p&gt;&lt;a class="reference external" href="https://www.envoyproxy.io/"&gt;Envoy&lt;/a&gt; is a configurable proxy that serves
a prominent role in modern cloud-native projects; for example, it's
used in many k8s deployments to provide inter-service communication (through
Istio). In this post, I'd like to describe Envoy's extension mechanism as a case
study of using WebAssembly for plugins.&lt;/p&gt;
&lt;div class="section" id="some-background"&gt;
&lt;h2&gt;Some background&lt;/h2&gt;
&lt;p&gt;Imagine …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="https://www.envoyproxy.io/"&gt;Envoy&lt;/a&gt; is a configurable proxy that serves
a prominent role in modern cloud-native projects; for example, it's
used in many k8s deployments to provide inter-service communication (through
Istio). In this post, I'd like to describe Envoy's extension mechanism as a case
study of using WebAssembly for plugins.&lt;/p&gt;
&lt;div class="section" id="some-background"&gt;
&lt;h2&gt;Some background&lt;/h2&gt;
&lt;p&gt;Imagine a complex service-based infrastructure, in which service A has to
communicate with service B. This is typically done via HTTP/REST or some RPC
mechanism, but there are a lot of complex networking details to deal with:
service discovery (I just want to send a message to an instance of service B,
but which address/port is it on?), load balancing, retries, etc. Instead of
having each service deal with this complexity, we can set up Envoy to run as a
sidecar, and ask it to handle all of it. Then the actual services can focus on
our business logic.&lt;/p&gt;
&lt;p&gt;Here's a handy diagram from &lt;a class="reference external" href="https://istio.io"&gt;https://istio.io&lt;/a&gt; that demonstrates this (Envoy is
the &lt;em&gt;Proxy&lt;/em&gt; boxes):&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/2023/envoyarch.svg" style="width: 600px;" type="image/svg+xml"&gt;System diagram of envoy proxies communicating for microservices&lt;/object&gt;
&lt;p&gt;As expected - for such a sophisticated piece of software - Envoy users
frequently need to customize it in various ways for their projects. For example,
we may want to define &lt;a class="reference external" href="https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/listeners/listener_filters#arch-overview-network-filters"&gt;custom filters&lt;/a&gt;;
this is a kind of middleware.&lt;/p&gt;
&lt;p&gt;Envoy's original approach to extensions was to support writing C++ to link
custom filters with Envoy itself. This, of course, is awkward for many reasons -
such as having to distribute your own Envoy binaries instead of using the
standard ones. Also, the filter API was not really designed to be stable so
keeping up with changes was an issue; and finally, few people like writing C++
these days.&lt;/p&gt;
&lt;p&gt;So the Envoy team came up with an alternative approach:
&lt;a class="reference external" href="https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter"&gt;Lua extensions&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="Lua programming language logo" class="align-center" src="https://eli.thegreenplace.net/images/2023/lua-logo.png" style="width: 200px;" /&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Lua_(programming_language)"&gt;Lua programming language&lt;/a&gt; was designed
for extensions and plugins; it's a small and simple language, and its
implementation is also small and simple - making it easy to embed. You can write
some Lua code either directly in your configuration file or a separate file it
points to, and there's an API exposed to Lua that the extension can interact
with.&lt;/p&gt;
&lt;p&gt;The Lua extension method is fully supported in Envoy and is currently in
a &lt;em&gt;stable&lt;/em&gt; state, but some folks weren't too keen on learning yet another
programming language just for the sake of writing filters for their proxy. Lua
is not particularly prominent in the Cloud world (which is mostly dominated
by Go, Python, Java and some other languages). Therefore, the Envoy maintainers
have created yet another way to extend it &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt; - with WebAssembly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="wasm-extensions"&gt;
&lt;h2&gt;WASM extensions&lt;/h2&gt;
&lt;p&gt;WASM extensions are still experimental in Envoy at the time of writing, but
it's an intriguing approach and the main subject of this post. WASM elegantly
solves the problems of the other extension methods as follows:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The WASM extension is compiled into a &lt;tt class="docutils literal"&gt;.wasm&lt;/tt&gt; file that the Envoy config
can point at, and is loaded dynamically at runtime. It doesn't require
recompiling and distributing a custom version of Envoy.&lt;/li&gt;
&lt;li&gt;The extension can use any programming language that compiles down to WASM,
and that covers &lt;em&gt;a lot&lt;/em&gt; of languages these days.
Your entire service infrastructure is written in Go and you don't want to
wrangle C++ or learn Lua just for the proxy filters? No problem - Go compiles
to WASM and there's even &lt;a class="reference external" href="https://github.com/tetratelabs/proxy-wasm-go-sdk"&gt;an SDK&lt;/a&gt;
to help writing Envoy filters in it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To this end, Envoy embeds &lt;a class="reference external" href="https://v8.dev/"&gt;v8&lt;/a&gt; as a WASM VM. All that
remains is to define the interface between these WASM extension modules and
Envoy itself.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-proxy-wasm-abi"&gt;
&lt;h2&gt;The Proxy-Wasm ABI&lt;/h2&gt;
&lt;p&gt;WebAssembly itself defines:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;A bytecode format (with an equivalent &lt;a class="reference external" href="https://eli.thegreenplace.net/2023/webassembly-text-code-samples/"&gt;text format&lt;/a&gt;)
and its execution semantics&lt;/li&gt;
&lt;li&gt;A way for WASM modules to export functions and data to the host environment&lt;/li&gt;
&lt;li&gt;A way for WASM modules to import functions and data from the host environment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that's about it. Everything else is left to the specific system implementer
to figure out. Moreover, the data types WASM supports are very limited -
essentially fixed-width integers and floats; users are expected to build
their own higher-level data structures on top of these using addresses into
WASM's linear heap memory, if needed.&lt;/p&gt;
&lt;p&gt;In a &lt;a class="reference external" href="https://eli.thegreenplace.net/2023/faas-in-go-with-wasm-wasi-and-rust/"&gt;previous post&lt;/a&gt; I've
talked about WASI - an API and ABI that enables OS-like functionality in WASM
code. While WASI is useful for exposing WASM modules to the outside world in
a vetted way, it's somewhat limited for complex host-wasm interactions, because
at the moment the only way for this to happen is via interfaces like
stdin/stdout &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Therefore, systems that require sophisticated interactions between the host
and WASM extensions are left to define their own interfaces. Which is exactly
what the Envoy developers ended up creating: the &lt;a class="reference external" href="https://github.com/proxy-wasm/spec"&gt;Proxy-Wasm
ABI&lt;/a&gt; &lt;a class="footnote-reference" href="#footnote-3" id="footnote-reference-3"&gt;[3]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The ABI is fairly low level, and it has two parts. One is
&lt;strong&gt;Functions implemented in the WASM module&lt;/strong&gt;. These are functions exported from
WASM (the custom extension) and imported by the host (Envoy or another proxy).
For example, &lt;tt class="docutils literal"&gt;proxy_on_request_headers&lt;/tt&gt; is exported by the WASM module as
a callback to handle headers for HTTP requests sailing through the proxy.&lt;/p&gt;
&lt;p&gt;This is the signature of &lt;tt class="docutils literal"&gt;proxy_on_request_headers&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;params:
    i32 (uint32_t) context_id
    i32 (size_t) num_headers
    i32 (bool) end_of_stream
returns:
    i32 (proxy_action_t) next_action
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The import is done in the &lt;a class="reference external" href="https://github.com/proxy-wasm/proxy-wasm-cpp-host/"&gt;proxy-wasm-cpp-host&lt;/a&gt; project
which is a dependency of Envoy. This project implements the host side of
Proxy-wasm for C++ hosts.&lt;/p&gt;
&lt;p&gt;What should the extension do within &lt;tt class="docutils literal"&gt;proxy_on_request_headers&lt;/tt&gt;, though? It
can do things like ask Envoy about the actual HTTP headers it sees with
&lt;tt class="docutils literal"&gt;proxy_get_header_map_value&lt;/tt&gt;. This is in the second part of the ABI,
&lt;strong&gt;Functions implemented in the host environment&lt;/strong&gt;. Its signature is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;params:
    i32 (proxy_map_type_t) map_type
    i32 (const char*) key_data
    i32 (size_t) key_size
    i32 (const char**) return_value_data
    i32 (size_t*) return_value_size
returns:
    i32 (proxy_result_t) call_result
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see this is &lt;em&gt;a very low level ABI&lt;/em&gt;; all parameters are either
pointers (addresses in WASM's linear memory) or constants of predefined types.
Since WASM severely restricts the types of function parameters and return
values, and both the WASM module and the host can be implemented in very diverse
programming languages, there's not much choice here. Writing the glue code on
the WASM-host interface is tedious and low-level.&lt;/p&gt;
&lt;p&gt;This is where the high-level SDKs come in.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-go-sdk-for-proxy-wasm"&gt;
&lt;h2&gt;The Go SDK for Proxy-wasm&lt;/h2&gt;
&lt;p&gt;Suppose we're writing our Envoy extension module in Go (a reasonable choice
given the dominance of Go in the Cloud Native / k8s / Istio ecosystem). It
seems like hooking up a simple extension to snoop on all the HTTP traffic
going through the proxy and logging the HTTP headers is quite a bit of work.&lt;/p&gt;
&lt;p&gt;Luckily, the good folks at Tetrate created the &lt;a class="reference external" href="https://github.com/tetratelabs/proxy-wasm-go-sdk/"&gt;Go SDK for Proxy-Wasm&lt;/a&gt;. This SDK handles all
the Proxy-Wasm ABI mechanics and presents a clean, pure Go API to extension
writers that won't have to worry about low level WASM details.&lt;/p&gt;
&lt;p&gt;Here's how the task of &amp;quot;snoop on HTTP traffic and log headers&amp;quot;
&lt;a class="reference external" href="https://github.com/tetratelabs/proxy-wasm-go-sdk/blob/main/examples/http_headers/main.go"&gt;looks using the Go SDK&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;httpHeaders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;OnHttpRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numHeaders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;endOfStream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;hs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;proxywasm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GetHttpRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;proxywasm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LogCriticalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;failed to get request headers: %v&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;proxywasm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LogInfof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;request header --&amp;gt; %s: %s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ActionContinue&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let's explore how both sides of the ABI (host-implemented and
module-implemented) are handled by the Go SDK. Starting with the WASM-calls-host
side, this is &lt;tt class="docutils literal"&gt;proxywasm.GetHttpRequestHeaders&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GetHttpRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([][&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MapTypeHttpRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It's just a wrapper around a more general &lt;tt class="docutils literal"&gt;getMap&lt;/tt&gt; function with a map type
that the ABI defines. The return type is a slice of 2-element arrays
(key/value).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mapType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MapType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([][&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rvs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;st&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProxyGetHeaderMapPairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mapType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;st&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusOK&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusToError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;st&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ErrorStatusNotFound&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RawBytePtrToByteSlice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeserializeMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;internal.ProxyGetHeaderMapPairs&lt;/tt&gt; is actually an ABI-defined function that's
imported from the host (as &lt;tt class="docutils literal"&gt;proxy_get_header_map_pairs&lt;/tt&gt;). It writes raw
pointers to its output parameters, so the rest of &lt;tt class="docutils literal"&gt;getMap&lt;/tt&gt; deals with
converting those into Go data types.&lt;/p&gt;
&lt;p&gt;On the host side, &lt;tt class="docutils literal"&gt;proxy_get_header_map_pairs&lt;/tt&gt; is mapped to a
C++ function &lt;a class="reference external" href="https://github.com/proxy-wasm/proxy-wasm-cpp-host/blob/e1fe5e99eedfb517bea92aee3f13f442d4bfa3b4/src/exports.cc#L1"&gt;in this file&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now the host-calls-WASM side. The Go SDK has the following function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;//export proxy_on_request_headers&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;proxyOnRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contextID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;numHeaders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;endOfStream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;recordTiming&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;logTiming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;proxyOnRequestHeaders&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpContexts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;contextID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;invalid context on proxy_on_request_headers&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setActiveContextID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contextID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnHttpRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;endOfStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note the &lt;tt class="docutils literal"&gt;//export&lt;/tt&gt; annotation that tells the compiler to export this function
from the WASM module. To be clear, the entire SDK - along with our custom code -
gets compiled into a &lt;tt class="docutils literal"&gt;.wasm&lt;/tt&gt; file that the host loads, and the &lt;tt class="docutils literal"&gt;//export&lt;/tt&gt;
tag makes the Go compiler place this function in the WASM function export
table that the host has access to.&lt;/p&gt;
&lt;p&gt;Once the host invokes it, it calls the &lt;tt class="docutils literal"&gt;OnHttpRequestHeaders&lt;/tt&gt; method on the
context, which is user-defined as shown above. Hopefully this example gives a
taste of what the SDK does for us - it provides a higher-level,
language-idiomatic API on top of a low-level, language-agnostic ABI.&lt;/p&gt;
&lt;p&gt;The Go SDK is just an example; there are other SDKs that exist for developing
WASM extensions for Envoy - for example in Rust or in C++.&lt;/p&gt;
&lt;p&gt;One small wrinkle in this story is that the Go SDK only supports the &lt;a class="reference external" href="https://tinygo.org/"&gt;TinyGo&lt;/a&gt; compiler at this time, not the default Go toolchain.
This is because the default toolchain doesn't have sufficient WASM support yet,
but this situation is changing; in Go 1.21 it has added WASI support and work
is ongoing on additional features that should make it possible to develop
Envoy extensions using the standard toolchain.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="fundamental-plugin-concepts-in-this-case-study"&gt;
&lt;h2&gt;Fundamental plugin concepts in this case study&lt;/h2&gt;
&lt;p&gt;Let's see how this case study of Envoy extensions with WASM measures against the
&lt;a class="reference external" href="https://eli.thegreenplace.net/2012/08/07/fundamental-concepts-of-plugin-infrastructures"&gt;Fundamental plugin concepts&lt;/a&gt;
that were covered &lt;a class="reference external" href="https://eli.thegreenplace.net/tag/plugins"&gt;several times on this blog&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="discovery"&gt;
&lt;h3&gt;Discovery&lt;/h3&gt;
&lt;p&gt;Envoy &amp;quot;discovers&amp;quot; available extensions trivially, because they have to be
explicitly specified in its configuration file. The config file lists the
extensions and where to find them; for WASM, this could be either a local
&lt;tt class="docutils literal"&gt;.wasm&lt;/tt&gt; file or a URL pointing to a file stored remotely (e.g. some
cloud storage bucket).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="registration"&gt;
&lt;h3&gt;Registration&lt;/h3&gt;
&lt;p&gt;The WASM extension registers functionality with Envoy by exporting certain
functions from the WASM module. When Envoy loads an extension, it scans the list
of exported functions for known names. For example, if the extension exports
&lt;tt class="docutils literal"&gt;proxy_on_request_headers&lt;/tt&gt;, Envoy will call it for HTTP headers. If the
extension doesn't export such a function, Envoy will assume it's not interested
in this particular callback.&lt;/p&gt;
&lt;p&gt;Another interesting example of how this functionality is used is the
&lt;tt class="docutils literal"&gt;proxy_abi_version_X_Y_Z&lt;/tt&gt; function. An extension will
export this function with an actual ABI version replacing X, Y an Z. Envoy
will look for a function with the &lt;tt class="docutils literal"&gt;proxy_abi_version_*&lt;/tt&gt; prefix, and from its
name will determine which version of the ABI the WASM module was compiled
against.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="hooks"&gt;
&lt;h3&gt;Hooks&lt;/h3&gt;
&lt;p&gt;This is mostly covered in the previous section. There are multiple callbacks
a WASM extension can register by exporting them from the WASM module;
&lt;tt class="docutils literal"&gt;proxy_on_request_headers&lt;/tt&gt; is one example out of many defined in the ABI.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="exposing-an-application-api-to-plugins"&gt;
&lt;h3&gt;Exposing an application API to plugins&lt;/h3&gt;
&lt;p&gt;This is the &lt;strong&gt;Functions implemented in the host environment&lt;/strong&gt; part of the
Proxy-Wasm ABI; we've seen an example of one of them -
&lt;tt class="docutils literal"&gt;proxy_get_header_map_pairs&lt;/tt&gt;. The ABI defines others, like &lt;tt class="docutils literal"&gt;proxy_log&lt;/tt&gt; for
emitting log messages to Envoy's log. These functions let extensions call
into Envoy.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;As you can see from the string of posts this year, I'm pretty excited about
the non-browser uses of WASM, particularly in the area of plugins.
The &lt;a class="reference external" href="https://eli.thegreenplace.net/2023/faas-in-go-with-wasm-wasi-and-rust/"&gt;FAAS post&lt;/a&gt; presented one interesting
possibility - using the current (limited but functional) WASI for the
host/plugin interface.&lt;/p&gt;
&lt;p&gt;What this post shows is a case study of a much more advanced extension system;
the capabilities and performance requirements of custom network filter plugins
are just way beyond what WASI can provide, so the Envoy developers ended up
creating their own ABI. It's fascinating to study how such an ABI affects plugin
development and what kind of ecosystem it spawns.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Note that I'm not trying to criticize the existing extension mechanisms
in Envoy in any way. Both work, and are used to solve real business
problems. As a project like Envoy grows in popularity and usage, it's
inevitable that it will spawn more options for different people to
accomplish their tasks with it. Such is the way of software.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The WASI folks are working on extensions to allow sockets and also
more complex data to be shared between WASM and hosts in an RPC-like
manner; this may enable greatly improved wasm-host interfaces in the
future.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p class="first"&gt;This all sounds great - the way things should be - until reality kicks
in. While doing research for this post I discovered that the Proxy-wasm
ABI, while clearly and carefully specified, is in-fact
&lt;a class="reference external" href="https://github.com/proxy-wasm/spec/issues/36"&gt;out of date&lt;/a&gt; and
the &amp;quot;real&amp;quot; definition lives within the Envoy source code. It's yet
another case of &amp;quot;the ABI is whatever its main implementation does&amp;quot;, even
though other proxies implement it already
(&lt;a class="reference external" href="https://github.com/mosn/mosn"&gt;MOSN&lt;/a&gt; for example).&lt;/p&gt;
&lt;p&gt;This is especially often the case in my favorite domain - systems
programming. &lt;em&gt;Sigh&lt;/em&gt;, such is life. The rest of the post talks about
the &lt;em&gt;de-facto&lt;/em&gt; specification, relying on the Envoy source code more
than the written down ABI. Hopefully at some future point the ABI is
updated and I can rewrite this footnote.&lt;/p&gt;
&lt;p class="last"&gt;A shout out to &lt;a class="reference external" href="https://github.com/codefromthecrypt"&gt;Adrian Cole&lt;/a&gt;
and &lt;a class="reference external" href="https://github.com/mathetake"&gt;Takeshi Yoneda&lt;/a&gt; for
confirming these findings, and the useful chats about all things WASM,
WASI and Go in general.&lt;/p&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="Go"></category><category term="Plugins"></category><category term="WebAssembly"></category><category term="C &amp; C++"></category></entry><entry><title>"Beating" C with 400 lines of unoptimized assembly</title><link href="https://eli.thegreenplace.net/2019/beating-c-with-400-lines-of-unoptimized-assembly/" rel="alternate"></link><published>2019-11-23T09:46:00-08:00</published><updated>2022-10-04T14:08:24-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2019-11-23:/2019/beating-c-with-400-lines-of-unoptimized-assembly/</id><summary type="html">&lt;p&gt;Earlier this week I ran into a fun quick blog post named
&lt;a class="reference external" href="https://ajeetdsouza.github.io/blog/posts/beating-c-with-70-lines-of-go/"&gt;Beating C with 70 lines of Go&lt;/a&gt;,
which reimplements the basic functionality of &lt;tt class="docutils literal"&gt;wc&lt;/tt&gt; in Go using various
approaches and compares their performance. Apparently it's inspired by an
earlier &lt;a class="reference external" href="https://chrispenner.ca/posts/wc"&gt;Haskell-based post&lt;/a&gt; and several
other offshoots.&lt;/p&gt;
&lt;p&gt;This reminded me …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Earlier this week I ran into a fun quick blog post named
&lt;a class="reference external" href="https://ajeetdsouza.github.io/blog/posts/beating-c-with-70-lines-of-go/"&gt;Beating C with 70 lines of Go&lt;/a&gt;,
which reimplements the basic functionality of &lt;tt class="docutils literal"&gt;wc&lt;/tt&gt; in Go using various
approaches and compares their performance. Apparently it's inspired by an
earlier &lt;a class="reference external" href="https://chrispenner.ca/posts/wc"&gt;Haskell-based post&lt;/a&gt; and several
other offshoots.&lt;/p&gt;
&lt;p&gt;This reminded me of my earlier post about &lt;a class="reference external" href="https://eli.thegreenplace.net/2016/wc-in-x64-assembly/"&gt;reimplementing wc in pure x64
assembly&lt;/a&gt;, where I
also measured the performance of my program against &lt;tt class="docutils literal"&gt;wc&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;The optimized approach taken in the Go implementation is very similar to what I
did in assembly, so it seemed like an interesting comparison. I started by
generating a ~580 MiB file using &lt;a class="reference external" href="https://github.com/eliben/xmlgen"&gt;xmlgen&lt;/a&gt;
and ran the various versions against each other:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;LC_TYPE=POSIX wc&lt;/tt&gt;: 2.13 sec&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wc-naive.go&lt;/span&gt;&lt;/tt&gt;: 3.53 sec&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wc-chunks.go&lt;/span&gt;&lt;/tt&gt;: 1.37 sec&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;wcx64&lt;/tt&gt;: 1.2 sec&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note the &lt;tt class="docutils literal"&gt;LC_TYPE&lt;/tt&gt; setting for the system's &lt;tt class="docutils literal"&gt;wc&lt;/tt&gt;. This is important for a
fair comparison, because without this &lt;tt class="docutils literal"&gt;wc&lt;/tt&gt; will attempt to do &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;utf-8&lt;/span&gt;&lt;/tt&gt;
decoding on all bytes in the file, which results in significant slowdowns. Since
the Go versions use byte-counts and so does my &lt;tt class="docutils literal"&gt;wcx64&lt;/tt&gt;, I force the comparison
to be fair. In fact, this isn't a bad result for Go - the straightforward
solution is almost as fast as the same approach direct-coded in assembly!&lt;/p&gt;
&lt;p&gt;The Go blog post follows with parallelized versions which are much faster than
the serial one, but I'm excluding it here because all the other competitors are
single-threaded. This is not a serious benchmark anyway. If you prefer to
be serious, &lt;a class="reference external" href="https://github.com/expr-fi/fastlwc/"&gt;this response using SIMD-optimized C&lt;/a&gt; blows everything out of the water:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;fastlwc&lt;/tt&gt;: 0.11 sec&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The conclusion? Well, there's no real conclusion here, beyond that coding
exercises like this are fun in any language :-)&lt;/p&gt;
</content><category term="misc"></category><category term="Assembly"></category><category term="C &amp; C++"></category><category term="Go"></category></entry><entry><title>Faster XML stream processing in Go</title><link href="https://eli.thegreenplace.net/2019/faster-xml-stream-processing-in-go/" rel="alternate"></link><published>2019-07-22T05:37:00-07:00</published><updated>2024-05-04T19:46:23-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2019-07-22:/2019/faster-xml-stream-processing-in-go/</id><summary type="html">&lt;p&gt;XML processing was all the rage 15 years ago; while it's less
prominent these days, it's still an important task in some application domains.
In this post I'm going to compare the speed of stream-processing huge XML files
in Go, Python and C and finish up with a new, minimal …&lt;/p&gt;</summary><content type="html">&lt;p&gt;XML processing was all the rage 15 years ago; while it's less
prominent these days, it's still an important task in some application domains.
In this post I'm going to compare the speed of stream-processing huge XML files
in Go, Python and C and finish up with a new, minimal module that uses C to
accelerate this task for Go. All the code shown throughout this post is
available &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2019/xml-stream"&gt;in this GitHub repository&lt;/a&gt; the new
Go module &lt;a class="reference external" href="https://github.com/eliben/gosax"&gt;is here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="what-does-xml-stream-processing-mean"&gt;
&lt;h2&gt;What does XML stream processing mean?&lt;/h2&gt;
&lt;p&gt;First, let's define the problem at hand in more detail. Roughly speaking, there
are two ways we can process data from a file:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Read the whole file into memory at once, and then proces the data in
memory.&lt;/li&gt;
&lt;li&gt;Read the file in chunks, process each chuck, without having the whole data
in memory at any given time.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In many ways, (1) is more convenient because we can easily go back to any part
of the file. However, in some situations (2) is essential; specifically, when
the file is very large. This is where &lt;em&gt;stream&lt;/em&gt; processing comes in. If our input
file is 500 GiB, we're unlikely to be able to read it into memory and have to
process it in parts. Even for smaller files that would theoretically fit into
RAM, it's not always a good idea to read them wholly; this dramatically
increases the active heap size and can cause performance issues in
garbage-collected languages.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-task"&gt;
&lt;h2&gt;The task&lt;/h2&gt;
&lt;p&gt;For this benchmark, I'm using &lt;a class="reference external" href="https://github.com/eliben/xmlgen"&gt;xmlgen&lt;/a&gt; to
create a 230 MiB XML file &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. A tiny fragment of the file may look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; standalone=&amp;quot;yes&amp;quot;?&amp;gt;
&amp;lt;site&amp;gt;
  &amp;lt;regions&amp;gt;
    &amp;lt;asia&amp;gt;
      &amp;lt;item id=&amp;quot;item0&amp;quot;&amp;gt;
        &amp;lt;location&amp;gt;United States&amp;lt;/location&amp;gt;
        &amp;lt;quantity&amp;gt;1&amp;lt;/quantity&amp;gt;
        &amp;lt;name&amp;gt;duteous nine eighteen &amp;lt;/name&amp;gt;
        &amp;lt;payment&amp;gt;Creditcard&amp;lt;/payment&amp;gt;
        ...
      &amp;lt;/item&amp;gt;
    &amp;lt;/asia&amp;gt;
  &amp;lt;/regions&amp;gt;
&amp;lt;/site&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The task is to find how many times &amp;quot;Africa&amp;quot; appears in the data of the
&lt;tt class="docutils literal"&gt;&amp;lt;location&amp;gt;&lt;/tt&gt; tag throughout the document.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="baseline-using-the-go-standard-library"&gt;
&lt;h2&gt;Baseline - using the Go standard library&lt;/h2&gt;
&lt;p&gt;Let's start with a baseline implementation - using the standard library's
&lt;tt class="docutils literal"&gt;encoding/xml&lt;/tt&gt; package. While the package's &lt;tt class="docutils literal"&gt;Unmarshal&lt;/tt&gt; mode will parse the
whole file in one go, it can also be used to process XML token by token and
selectively parse interesting elements. Here is the code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;encoding/xml&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;io&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;log&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;os&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;strings&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;Data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`xml:&amp;quot;,chardata&amp;quot;`&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EOF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// EOF means we&amp;#39;re done.&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Error decoding token: %s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StartElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// If this is a start element named &amp;quot;location&amp;quot;, parse this element&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// fully.&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DecodeElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;ty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Error decoding item: %s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Africa&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;count =&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I made sure to double check that the memory usage of this program stays bounded
and low while processing a large file - the maximum RSS was under 7 MiB while
processing our 230 MiB input file. I'm verifying this for all the programs
presented in this post using &lt;tt class="docutils literal"&gt;/usr/bin/time &lt;span class="pre"&gt;-v&lt;/span&gt;&lt;/tt&gt; on Linux.&lt;/p&gt;
&lt;p&gt;This program takes 6.24 seconds to process the whole file and print out the
result.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="python-implementation"&gt;
&lt;h2&gt;Python implementation&lt;/h2&gt;
&lt;p&gt;The first Python implementation uses the &lt;tt class="docutils literal"&gt;xml.etree.ElementTree&lt;/tt&gt; module from
the standard library:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;xml.etree.ElementTree&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;ET&lt;/span&gt;

&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ET&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iterparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;end&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,)):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;end&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;location&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Africa&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;count =&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The key here is the &lt;tt class="docutils literal"&gt;elem.clear()&lt;/tt&gt; call. It ensures that each element gets
discarded afer parsing it fully, so the memory usage won't grow linearly with
the size of the file (unless the file is pathological). This program takes 3.7
seconds to process the whole file - much faster than our Go program. Why is
that?&lt;/p&gt;
&lt;p&gt;While the Go program uses 100% Go code for the task (&lt;tt class="docutils literal"&gt;encoding/xml&lt;/tt&gt; is
implemented entirely in Go), the Python program is using a C extension (most of
&lt;tt class="docutils literal"&gt;ElementTree&lt;/tt&gt; is written in C) wrapping a fast XML parser in C - &lt;a class="reference external" href="https://github.com/libexpat/libexpat"&gt;libexpat&lt;/a&gt;. The bulk of the work here is done in
C, which is faster than Go.
The performance of &lt;tt class="docutils literal"&gt;encoding/xml&lt;/tt&gt; is further discussed in
&lt;a class="reference external" href="https://github.com/golang/go/issues/21823"&gt;this issue&lt;/a&gt;, though it's an
old one and the performance has been somewhat optimized since then.&lt;/p&gt;
&lt;p&gt;An alternative XML parsing library for Python is &lt;a class="reference external" href="https://github.com/lxml/lxml"&gt;lxml&lt;/a&gt;,
which uses &lt;a class="reference external" href="https://www.xmlsoft.org/"&gt;libxml&lt;/a&gt; underneath. Here's a Python
version using lxml:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;lxml&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;etree&lt;/span&gt;

&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;etree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iterparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;end&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,)):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;end&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;location&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Africa&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;count =&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This looks very similar to the previous version, and that's on purpose. lxml
has an &lt;tt class="docutils literal"&gt;etree&lt;/tt&gt;-compatible API to make transition from the standard library
smoother. This version also takes around 3.7 seconds for our 230 MiB file.&lt;/p&gt;
&lt;p&gt;The reason I'm including lxml here is that it will run faster than
&lt;tt class="docutils literal"&gt;xml.etree.ElementTree&lt;/tt&gt; when slurping the whole file, for our particular file
size. I want to highlight that this is outside of the scope for my experiment,
because I only care about streaming processing. The only way (that I'm aware
of!) to successfully process a 500 GiB file with lxml would be by using
&lt;tt class="docutils literal"&gt;iterparse&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="how-fast-can-it-run"&gt;
&lt;h2&gt;How fast can it run?&lt;/h2&gt;
&lt;p&gt;Based on the measurements presented here, Go is about 68% slower than Python for
parsing a large XML file in a streaming fashion. While Go usually compiles to
a much faster code than pure Python, the Python implementations have the backing
of efficient C libraries with which it's difficult to compete. I was curious
to know how fast it could be, in theory &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To answer this question, I implemented the same program using pure C with
libxml, which has a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Simple_API_for_XML"&gt;SAX API&lt;/a&gt;. I won't paste it wholly
here because it's longer, but you can find the &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2019/xml-stream/c-libxmlsax-count"&gt;full source code on GitHub&lt;/a&gt;.
It takes just 0.56 seconds to process our 230 MiB input file, which is very
impressive given the other results, but also not very surprising. This is C,
after all.&lt;/p&gt;
&lt;p&gt;You may wonder - if lxml uses libxml underneath, why is it so much slower than
the pure C version? The answer is Python call overhead. The lxml version calls
back into Python &lt;em&gt;for every parsed element&lt;/em&gt;, which incurs a significant
cost &lt;a class="footnote-reference" href="#footnote-3" id="footnote-reference-3"&gt;[3]&lt;/a&gt;. Another reason is that my C implementation doesn't actually parse an
element - it's just a simple event-based state machine, so there's less extra
work being done.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="using-libxml-from-go"&gt;
&lt;h2&gt;Using libxml from Go&lt;/h2&gt;
&lt;p&gt;To recap where we are so far:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Python libraries based on underlying C implementations are faster than
pure Go.&lt;/li&gt;
&lt;li&gt;Pure C is much faster still.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We have two options: we can either try to optimize Go's &lt;tt class="docutils literal"&gt;encoding/xml&lt;/tt&gt;
package, or we can try to wrap a fast C library with Go. While the former is a
worthy goal, it involves a large effort and should be a topic for a separate
post. Here, I'll go for the latter.&lt;/p&gt;
&lt;p&gt;Seaching around the web, I found a few wrappers around libxml. Two that seemed
moderately popular and maintained are &lt;a class="reference external" href="https://github.com/lestrrat-go/libxml2"&gt;https://github.com/lestrrat-go/libxml2&lt;/a&gt;
and &lt;a class="reference external" href="https://github.com/moovweb/gokogiri"&gt;https://github.com/moovweb/gokogiri&lt;/a&gt;. Unfortunately, neither of these (or
the other bindings I found) are exposing the SAX API of libxml; instead, they
focus on the DOM API, where the whole document is parsed by the underlying
library and a tree is returned. As mentioned above, we need the SAX interface
to process huge files.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="gosax"&gt;
&lt;h2&gt;gosax&lt;/h2&gt;
&lt;p&gt;It's time to roll our own :-) I wrote the &lt;a class="reference external" href="https://github.com/eliben/gosax"&gt;gosax&lt;/a&gt; module, which uses Cgo to call into libxml
and exposes a SAX interface &lt;a class="footnote-reference" href="#footnote-4" id="footnote-reference-4"&gt;[4]&lt;/a&gt;. Implementing it was an interesting exercise
in Cgo, because it requires some non-trivial concepts like
&lt;a class="reference external" href="https://eli.thegreenplace.net/2019/passing-callbacks-and-pointers-to-cgo/"&gt;registering Go callbacks with C&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here's a version of our program using gosax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;os&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;strings&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;github.com/eliben/gosax&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;inLocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;scb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gosax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SaxCallbacks&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;StartElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;attrs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;inLocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;inLocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;EndElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;inLocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Characters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;inLocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Africa&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gosax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParseFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;counter =&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, it implements a state machine that remembers being inside
a &lt;tt class="docutils literal"&gt;location&lt;/tt&gt; element, where the character data is checked. This program
takes 4.03 seconds to process our input file. Not bad! But we can do a bit
better, and &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/blob/main/2019/xml-stream/gosax-count/gosax-count.go"&gt;with a couple of optimizations&lt;/a&gt;
I managed to bring it down to 3.68 seconds - about the same speed as the Python
implementations!&lt;/p&gt;
&lt;p&gt;IMHO the roughly similar run times here are a coincidence, because the Python
programs are different from my approach in that they expose a higher-level API
than pure SAX. Recall that &lt;tt class="docutils literal"&gt;iterparse&lt;/tt&gt; returns a parsed element, and we
can access its &lt;tt class="docutils literal"&gt;text&lt;/tt&gt; attribute, etc. In gosax, we have to do this much more
manually. Since the the &lt;a class="reference external" href="https://about.sourcegraph.com/go/gophercon-2018-adventures-in-cgo-performance"&gt;cost of calls between Cgo and Go is rather high&lt;/a&gt;,
there is an optimization opportunity here for gosax. We could do more work in
C - parsing a full element, and returning it wholly to Go. This would move work
from the Go side to the C side, as well as reduce the number of cross-language
calls. But this is a task for another day.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Well, this was fun :-) There are 5 different implementations of the same simple
task described here, in 3 different programming languages. Here is a summary
of the speed measurements we got:&lt;/p&gt;
&lt;img alt="Performance comparison chart" class="align-center" src="https://eli.thegreenplace.net/images/2019/xml-sax-comparison.png" /&gt;
&lt;p&gt;Python's performance story has always been - &amp;quot;it's probably fast enough, and in
the rare cases where it isn't, use a C extension&amp;quot;. In Go the narrative is
somewhat different: in most cases, the Go compiler produces fairly fast code.
Pure Go code is significantly faster than Python and often faster than Java.
Even so, every once in a while it may be useful to dip into C or C++ for
performance, and in these cases Cgo is a good approach.&lt;/p&gt;
&lt;p&gt;It's obvious that &lt;tt class="docutils literal"&gt;encoding/xml&lt;/tt&gt; &lt;a class="reference external" href="https://github.com/golang/go/issues/21823"&gt;needs some work w.r.t. performance&lt;/a&gt;, but until that happens - there
are good alternatives! Leveraging the speed of libxml has been possible for the
DOM API, and now is possible for the SAX API as well. In the long run, I believe
that serious performance work on &lt;tt class="docutils literal"&gt;encoding/xml&lt;/tt&gt; can make it go faster than
the libxml wrappers because it would elimitate the high cost of C-to-Go calls.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This size will easily fit in RAM, but it's good enough to provide a
meaningful benchmarking duration.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;When working on optimizations, it's often useful to know &amp;quot;the speed
of light&amp;quot; of some computation. Say we want to optimize some function in
our program. It's worth asking - how much faster will the program be if
this function takes 0 time? If the overall change is tiny, the function
is not worth optimizing, most likely. This is just a practical
application of Amdahl's law.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;We can test this hypothesis by timing how long it takes the non-streaming
API in lxml to parse the same file. Since it parses the whole XML file
in C before returning the parsed structure to Python, we expect the
Python call overhead to be much smaller. Indeed, for files that fit into
memory this is faster. But once again, in this post we return our
attention to streaming APIs - assuming this is our only choice for
gigantic files.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;gosax is very minimal, only providing the most common SAX callbacks.
The decision to create a new module was just for convenience and speed;
the more correct thing would have likely been to contribute to one of
the existing libxml wrappers. I don't see gosax as production-quality
at this stage - I just hacked it together to be able to experiment for
this post.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="Go"></category><category term="C &amp; C++"></category><category term="Python"></category></entry></feed>