<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>pair of pared pears</title><link href="https://blog.trends.tf/" rel="alternate"></link><link href="https://blog.trends.tf/feeds/all.atom.xml" rel="self"></link><id>https://blog.trends.tf/</id><updated>2024-04-17T15:26:23-04:00</updated><entry><title>Memory management in mpmetrics</title><link href="https://blog.trends.tf/memory-management-in-mpmetrics.html" rel="alternate"></link><published>2024-04-17T15:26:23-04:00</published><updated>2024-04-17T15:26:23-04:00</updated><author><name>Sean Anderson</name></author><id>tag:blog.trends.tf,2024-04-17:/memory-management-in-mpmetrics.html</id><summary type="html">&lt;div id="preamble"&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This article is an extended, semi-literate overview of the memory management in
&lt;a href="https://github.com/Forty-Bot/mpmetrics"&gt;&lt;code&gt;mpmetrics&lt;/code&gt;&lt;/a&gt;. I think there are a lot of
neat little details in the design of this library that lend themselves better
to a more guided exposition than API documentation. I&amp;#8217;ve made a few
simplifications for didactic reasons …&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</summary><content type="html">&lt;div id="preamble"&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This article is an extended, semi-literate overview of the memory management in
&lt;a href="https://github.com/Forty-Bot/mpmetrics"&gt;&lt;code&gt;mpmetrics&lt;/code&gt;&lt;/a&gt;. I think there are a lot of
neat little details in the design of this library that lend themselves better
to a more guided exposition than API documentation. I&amp;#8217;ve made a few
simplifications for didactic reasons, but the code otherwise closely mirrors
the actual implementation.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;If you&amp;#8217;re not familiar with &lt;code&gt;mpmetrics&lt;/code&gt;, check out my
&lt;a href="../introducing-mpmetrics.html"&gt;introduction to mpmetrics&lt;/a&gt;. The problem
focused on in this post is dynamic allocation of variables backed by shared
memory&amp;#8230;&amp;#8203; in Python. It&amp;#8217;s a tough challenge since, in many ways, Python is the
wrong language for this kind of task. However its dynamic and flexible nature
allow unusual and satisfying solutions to many challenges.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_in_a_pickle"&gt;In a pickle&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In order to store data in a structured fashion, we are going to need some types
backed by shared memory. Let&amp;#8217;s start by wrapping &lt;code&gt;c_int64&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ctypes&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mem&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_int64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;and proxy all of our attributes (except &lt;code&gt;_value&lt;/code&gt;) onto &lt;code&gt;c_int64&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'_value'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__setattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;'_value'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'_value'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__delattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;delattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'_value'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Lets try it out:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; mem = bytearray(Int64.size)
&amp;gt;&amp;gt;&amp;gt; x = Int64(mem)
&amp;gt;&amp;gt;&amp;gt; x.value
0
&amp;gt;&amp;gt;&amp;gt; x.value += 1
&amp;gt;&amp;gt;&amp;gt; x.value
1
&amp;gt;&amp;gt;&amp;gt; mem
bytearray(b'\x01\x00\x00\x00\x00\x00\x00\x00')&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;As I&amp;#8217;m on a little-endian system, the least-significant byte comes first. We
can even pickle and unpickle it as long as we define some helpers:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mem&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__setstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mem&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_int64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Continuing our example from above,&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; import pickle
&amp;gt;&amp;gt;&amp;gt; pickle.loads(pickle.dumps(x))
&amp;gt;&amp;gt;&amp;gt; # No errors :)&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This is an important feature, since with the &lt;code&gt;forkserver&lt;/code&gt; and &lt;code&gt;spawn&lt;/code&gt; start
methods,
&lt;a href="https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods"&gt;all
objects are pickled when passing them to the subprocess&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;That went well, so lets try and tackle an array next. The size of the array
will depend on the element class, and the number of elements. In order to keep
the size as a class attribute (so we know how much memory we need to allocate),
we create a new class for each type of array:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mem&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;off&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_vals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We can also define some extra methods to make our class behave more like an
array:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getitem__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_vals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__iter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_vals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;as well as some helpers for pickling:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mem&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__setstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mem&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;off&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;
                &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__new__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__setstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mem&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_vals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let&amp;#8217;s try it out:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; IntArray5 = Array(Int64, 5)
&amp;gt;&amp;gt;&amp;gt; a = IntArray5(bytearray(IntArray5.size))
&amp;gt;&amp;gt;&amp;gt; a[0].value = 5
&amp;gt;&amp;gt;&amp;gt; a[0].value += 10
&amp;gt;&amp;gt;&amp;gt; a[0].value
15&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;But there&amp;#8217;s a problem when pickling:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; pickle.dumps(a)
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
AttributeError: Can't pickle local object 'Array.&amp;lt;locals&amp;gt;.Array'&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The problem is that &lt;code&gt;pickle&lt;/code&gt; uses the type&amp;#8217;s &lt;code&gt;__qualname__&lt;/code&gt; to identify the
class to use when unpickling. We can see this if we disassemble our pickle from
earlier:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; import pickletools
&amp;gt;&amp;gt;&amp;gt; pickletools.dis(pickletools.optimize(pickle.dumps(x)))
    0: \x80 PROTO      4
    2: \x95 FRAME      56
   11: \x8c SHORT_BINUNICODE '__main__'
   21: \x8c SHORT_BINUNICODE 'Int64'
   28: \x93 STACK_GLOBAL
   29: )    EMPTY_TUPLE
   30: \x81 NEWOBJ
   31: \x8c SHORT_BINUNICODE 'builtins'
   41: \x8c SHORT_BINUNICODE 'bytearray'
   52: \x93 STACK_GLOBAL
   53: C    SHORT_BINBYTES b'\x00\x00\x00\x00\x00\x00\x00\x00'
   63: \x85 TUPLE1
   64: R    REDUCE
   65: b    BUILD
   66: .    STOP&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;But since we create a new class every time we call &lt;code&gt;Array&lt;/code&gt;, we can&amp;#8217;t identify
the class we created this way, since &lt;code&gt;pickle&lt;/code&gt; has no way to tell what the
arguments were to &lt;code&gt;Array&lt;/code&gt;. We could rewrite &lt;code&gt;Array&lt;/code&gt; to take &lt;code&gt;cls&lt;/code&gt; and &lt;code&gt;n&lt;/code&gt; as
arguments to &lt;code&gt;__init__&lt;/code&gt;, but then we wouldn&amp;#8217;t know how much memory to
allocate.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;What we need is a way to record the arguments to &lt;code&gt;Array&lt;/code&gt; so that we can create
the correct class when unpickling. But the only thing we have to work with is
the &lt;code&gt;__qualname__&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;What if we store the arguments to &lt;code&gt;Array&lt;/code&gt; in the class name itself?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_the_trick"&gt;The trick&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Imagine for a moment that we just want to create &lt;code&gt;Int64&lt;/code&gt; &lt;code&gt;Array&lt;/code&gt;s, and we
only need to store the length. We could create an object like&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IntType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__qualname__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'.'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The usage is perhaps best-demonstrated by example:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; test = IntType('test', lambda *args: args)
&amp;gt;&amp;gt;&amp;gt; getattr(test, '5')
('test.5', 5)&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The first argument to the function is the path we used to access that
attribute, and the second is the value of the attribute. Now we can use this to
create a new &lt;code&gt;IntArray&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_IntArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Int64&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;

    &lt;span class="p"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IntType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'IntArray'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_IntArray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We need to call the three-argument &lt;code&gt;type&lt;/code&gt; instead of using the &lt;code&gt;class&lt;/code&gt; keyword,
since the name of the class we create will change based on &lt;code&gt;n&lt;/code&gt;. Let&amp;#8217;s try using
this class again&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; IntArray5 = getattr(IntArray, '5')
&amp;gt;&amp;gt;&amp;gt; a = IntArray5(bytearray(IntArray5.size))
&amp;gt;&amp;gt;&amp;gt; a[0].value = 5
&amp;gt;&amp;gt;&amp;gt; a[0].value += 10
&amp;gt;&amp;gt;&amp;gt; a[0].value
15&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Looking good so far. Let&amp;#8217;s try pickling it&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; pickle.dumps(a)
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
_pickle.PicklingError: Can't pickle &amp;lt;class '__main__.IntArray.5'&amp;gt;: it's not the same object as __main__.IntArray.5&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Whoops. The problem is that every time we call &lt;code&gt;_IntArray&lt;/code&gt; we create a new
class. This is pretty easy to solve by wrapping &lt;code&gt;__getattr__&lt;/code&gt; in a decorator
which saves its return value:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;saveattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapped&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Python won&amp;#8217;t bother calling &lt;code&gt;__getattr__&lt;/code&gt; if the relevant attribute is
already present in &lt;code&gt;__dict__&lt;/code&gt;. Lets try pickling again:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; pickle.loads(pickle.dumps(a))&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Success!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_further_objectives"&gt;Further objectives&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;That was a nice warm up. Let&amp;#8217;s try something more challenging. What if instead
of a known element type and an unknown length, we tried making a class with an
unknown object type and a fixed length:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_Array5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;

    &lt;span class="c1"&gt;# ...
&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="n"&gt;Array5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Array5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_Array5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So what should the name of &lt;code&gt;IntArray5&lt;/code&gt; be? Well, perhaps the most obvious thing would be&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;Array5.__main__.Int64&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;but with this kind of name we wouldn&amp;#8217;t know when the name of the object started
and when it ended. This would prevent us from nesting multiple &lt;code&gt;ObjectType&lt;/code&gt;s.
So let&amp;#8217;s use this name instead:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;Array5.&amp;lt;.__main__.Int64.&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The base &lt;code&gt;ObjectType&lt;/code&gt; just needs to have a &lt;code&gt;&amp;lt;&lt;/code&gt; attribute:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__qualname__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="nb"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'&amp;lt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'.&amp;lt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This attribute&amp;#8217;s job is to parse the module portion of the class&amp;#8217;s name. We do
this by repeatedly trying to import the next attribute as a module:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;

        &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;saveattr&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'.'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;importlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;import_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ModuleNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'.'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;For example, say that we have a file &lt;code&gt;a/b.py&lt;/code&gt; and inside that file we have a
class &lt;code&gt;C&lt;/code&gt;. When we access &lt;code&gt;Array5.&amp;lt;.a.b.C&lt;/code&gt;, we will have&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;Array5: ObjectType('Array5', _Array5)
  &amp;lt;: ObjectType.Module('Array5.&amp;lt;', _Array5, None)
    a: ObjectType.Module('Array5.&amp;lt;.a', _Array5, a)
      b: ObjectType.Module('Array5.&amp;lt;.a.b', _Array5, a.b)
        C: ObjectType.Attr('Array5.&amp;lt;.a.b.C', _Array5, a.b.C)&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;At this point, we&amp;#8217;ve gotten through the modules and finally made it to an
object. Now we need to walk its attributes:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Attr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nesting&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nesting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nesting&lt;/span&gt;

        &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;saveattr&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;nesting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nesting&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'&amp;lt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'&amp;gt;'&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;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'&amp;gt;'&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;nesting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'.'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__qualname__&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'.&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nesting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To continuing the above example, say that class &lt;code&gt;C&lt;/code&gt; has a nested class &lt;code&gt;D&lt;/code&gt;. When
we access &lt;code&gt;Array5.&amp;lt;.a.b.C.D.&amp;gt;&lt;/code&gt; we will have&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;Array5: ObjectType('Array5', _Array5)
  &amp;lt;: ObjectType.Module('Array5.&amp;lt;', _Array5, None)
    a: ObjectType.Module('Array5.&amp;lt;.a', _Array5, a)
      b: ObjectType.Module('Array5.&amp;lt;.a.b', _Array5, a.b)
        C: ObjectType.Attr('Array5.&amp;lt;.a.b.C', _Array5, a.b.C, 1)
          D: ObjectType.Attr('Array5.&amp;lt;.a.b.C.D', _Array5, a.b.C.D, 1)
            &amp;gt;: _Array5('Array5.&amp;lt;.a.b.C.D.&amp;gt;', a.b.C.D)&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The &lt;code&gt;nesting&lt;/code&gt; attribute helps us keep track of nested objects. For example say
we wanted to create an &lt;code&gt;Array5&lt;/code&gt; of an &lt;code&gt;Array5&lt;/code&gt; of &lt;code&gt;Int64&lt;/code&gt;s:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;Array5: ObjectType('Array5', _Array5)
  &amp;lt;: ObjectType.Module('Array5.&amp;lt;', _Array5, None)
    __main__: ObjectType.Module('Array5.&amp;lt;.__main__', _Array5, __main__)
      Array5: ObjectType.Attr('Array5.&amp;lt;.__main__.Array5', _Array5, __main__.Array5, 1)
       &amp;lt;: ObjectType.Attr('Array5.&amp;lt;.__main__.Array5.&amp;lt;', _Array5, __main__.Array5.&amp;lt;, 2)
         __main__: ObjectType.Attr('Array5.&amp;lt;.__main__.Array5.&amp;lt;.__main__', _Array5, __main__.Array5.&amp;lt;.__main__, 2)
           Int64: ObjectType.Attr('Array5.&amp;lt;.__main__.Array5.&amp;lt;.__main__.Int64', _Array5, __main__.Array5.&amp;lt;.__main__.Int64, 2)
             &amp;gt;: ObjectType.Attr('Array5.&amp;lt;.__main__.Array5.&amp;lt;.__main__.Int64.&amp;gt;', _Array5, __main__.Array5.&amp;lt;.__main__.Int64.&amp;gt;, 1)
               &amp;gt;: _Array5('Array5.&amp;lt;.__main__.Array5.&amp;lt;.__main__.Int64.&amp;gt;.&amp;gt;', __main__.Array5.&amp;lt;.__main__.Int64.&amp;gt;.&amp;gt;)&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Of course, this also means that &lt;code&gt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; are special, and you can&amp;#8217;t include
unmatched brackets in your class hierarchy (like a certain ticklish language).
A more robust system could prefix the type with the number attributes in the
type:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;Array5.6.__main__.Array5.2.__main__.Int64&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;but I like the aesthetics of angle brackets more. Speaking of which, to
actually access the above class name, we&amp;#8217;d have to type out something like&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; getattr(getattr(getattr(getattr(Array5, '&amp;lt;').__main__.Array5, '&amp;lt;').__main__.Int64, '&amp;gt;'), '&amp;gt;')
&amp;lt;class '__main__.Array5.&amp;lt;.__main__.Array5.&amp;lt;.__main__.Int64.&amp;gt;.&amp;gt;'&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This is a real pain. Let&amp;#8217;s add a helper to &lt;code&gt;ObjectType&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getitem__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'&amp;lt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;subpath&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__module__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__qualname__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subpath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now we can do&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; Array5[Array5[Int64]]
&amp;lt;class '__main__.Array5.&amp;lt;.__main__.Array5.&amp;lt;.__main__.Int64.&amp;gt;.&amp;gt;'&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Much better.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_a_product_of_necessity"&gt;A product of necessity&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;code&gt;IntArray&lt;/code&gt;s and &lt;code&gt;Array5&lt;/code&gt;s are all well and good, but what we really want is
an &lt;code&gt;Array&lt;/code&gt; where we can specify both the element type and the length. Since we
already have an &lt;code&gt;IntType&lt;/code&gt; and &lt;code&gt;ObjectType&lt;/code&gt;, we can combine them together with a
&lt;code&gt;ProductType&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argtypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__qualname__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argtype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argtypes&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="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_chain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argtypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argtypes&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="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argtypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argtypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;saveattr&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argtype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# __getitem__ omitted for brevity&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Instead of constructing the class immediately, as before, we instead &lt;code&gt;_chain&lt;/code&gt;
into the next &lt;code&gt;argtype&lt;/code&gt;. With this, we can now redefine &lt;code&gt;Array&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;

    &lt;span class="c1"&gt;# ...
&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="n"&gt;Array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProductType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Array'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_Array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IntType&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;When we access something like &lt;code&gt;Array[Int64, 5]&lt;/code&gt;, the attributes will look like:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;Array: ProductType('Array', _Array, (ObjectType, IntType))
  &amp;lt;: ObjectType.Module('Array.&amp;lt;', Array._chain, None)
    __main__: ObjectType.Module('Array5.&amp;lt;.__main__', Array._chain, __main__)
      Int64: ObjectType.Attr('Array5.&amp;lt;.__main__.Int64', Array._chain, __main__.Int64)
        &amp;gt;: IntType('Array5.&amp;lt;.__main__.Int64.&amp;gt;', Array[Int64]._chain)
          5: _Array('Array5.&amp;lt;.__main__.Int64.&amp;gt;.5', Int64, 5)&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;And we can finally pickle and unpickle:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; IntArray5 = Array[Int64, 5]
&amp;gt;&amp;gt;&amp;gt; a = IntArray5(bytearray(IntArray5.size))
&amp;gt;&amp;gt;&amp;gt; pickle.loads(pickle.dumps(a))&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;There&amp;#8217;s a problem though:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; a[0].value = 15
&amp;gt;&amp;gt;&amp;gt; pickle.loads(pickle.dumps(a))[0].value
0&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This is because when we slice a &lt;code&gt;bytearray&lt;/code&gt;, we get a new &lt;code&gt;bytearray&lt;/code&gt; with a
copy of the original &lt;code&gt;bytearray&lt;/code&gt;'s contents. We can get around this by using
a &lt;code&gt;memoryview&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; a = IntArray5(memoryview(bytearray(IntArray5.size)))
&amp;gt;&amp;gt;&amp;gt; a[0].value = 15
&amp;gt;&amp;gt;&amp;gt; bytearray(a[0]._mem)
bytearray(b'\x0f\x00\x00\x00\x00\x00\x00\x00')
&amp;gt;&amp;gt;&amp;gt; bytearray(a._mem)[0:8]
bytearray(b'\x0f\x00\x00\x00\x00\x00\x00\x00')&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;But we can&amp;#8217;t pickle it:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; pickle.dumps(a)
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
TypeError: cannot pickle 'memoryview' object&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;It&amp;#8217;s time to actually start working with shared memory.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_malloc_madness"&gt;Malloc madness&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The first thing we need need is a bulk source of shared memory. Unfortunately,
we cannot use &lt;code&gt;multiprocessing.shared_memory&lt;/code&gt; because we can&amp;#8217;t expand its
memory later. Metrics can be created at any point in the application&amp;#8217;s
lifetime, and we don&amp;#8217;t necessarily know how many we will need when we have to
create the first metric. For example, adding a label creates a new copy of a
metric for that label, and it&amp;#8217;s common to generate labels dynamically based on
endpoints or status codes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Instead, we open a &lt;code&gt;TemporaryFile&lt;/code&gt; and truncate it as necessary to extend it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;mmap&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;tempfile&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TemporaryFile&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# File backing our shared memory
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TemporaryFile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fileno&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="c1"&gt;# Allocate a page to start with
&lt;/span&gt;        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Keep track of the memory we've mapped
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_maps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;map_size&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="c1"&gt;# Initialize a base pointer with the memory we just mapped
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;memoryview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_maps&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="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="c1"&gt;# And make sure we don't reuse that memory later
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We&amp;#8217;re going to be making a basic "bump" style allocator. The algorithm is
really simple; in pseudocode it&amp;#8217;s just:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Although it&amp;#8217;s a little more complex than that: we need to ensure we have enough
space in the file and take care when crossing page boundaries.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;code&gt;mmap&lt;/code&gt; doesn&amp;#8217;t have to return contiguous memory when extending an
existing mapping. For example, if we made two allocations of size 2048 and
4096, and we tried to allocate the first one at offset 0 and the second one at
offset 2048, the second allocation would span two pages (ending at byte 6143).
If the first page was mapped at address 16384, the second page would have to
be mapped at address 20480 to ensure a contiguous mapping. But we can&amp;#8217;t
guarantee that with the &lt;code&gt;mmap&lt;/code&gt; API. So instead, we round up to the next page
boundary if we would otherwise cross it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Allocations larger than a single page always cross page boundaries no matter
how we align things. To solve this issue, we map all the pages for these
allocations in one call to &lt;code&gt;mmap&lt;/code&gt;, ensuring that we get a contiguous mapping.
Then, we bump the base address to the next page boundary, ensuring that no
other allocations will need those pages.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In detail, if the allocation spans multiple pages, we page-align the size.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_align_mask&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="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;align&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="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_align_mask&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="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&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;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;align&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;If we need to allocate a new page, enlarge the file and update the base:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ftruncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;align&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;align&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;And finally, we can bump the base pointer and return a new &lt;code&gt;Block&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;        start = self._base.value
        self._base.value += size
        return Block(self, start, size)&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;code&gt;Block&lt;/code&gt; is like a pointer, except it keeps track of how big it is and where it
was allocated from.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;itertools&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heap&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;There&amp;#8217;s only one major method, &lt;code&gt;deref&lt;/code&gt;, which creates a &lt;code&gt;memoryview&lt;/code&gt;. The first
half of this function determines the page(s) we need to access, and what
their offsets are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;deref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;heap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;
        &lt;span class="n"&gt;first_page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;last_page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;nr_pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;last_page&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;first_page&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;page_off&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first_page&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;
        &lt;span class="n"&gt;off&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;page_off&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We store our mapped pages in list. Each element is a &lt;code&gt;memoryview&lt;/code&gt; of the page,
or &lt;code&gt;None&lt;/code&gt; if we haven&amp;#8217;t mapped it yet. To start, we extend the length of our
list if it&amp;#8217;s not big enough.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_maps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;last_page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
           &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_maps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itertools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_page&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_maps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Then, we create a map at the location of the first page. &lt;code&gt;malloc&lt;/code&gt; ensures we
never have &lt;code&gt;Block&lt;/code&gt;s which cross page boundaries unless they are larger than a
single page. Since multi-page allocations are the only allocations in the pages
they use, we will never try to access the &lt;code&gt;None&lt;/code&gt;s occupying the later indices
in the list.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_maps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;first_page&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
           &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_maps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;first_page&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nr_pages&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                              &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;page_off&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Finally, we can create a memory view out of the mapped page:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;memoryview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_maps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;first_page&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let&amp;#8217;s try it out:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; h = Heap()
&amp;gt;&amp;gt;&amp;gt; block = h.malloc(InteArray5.size)
&amp;gt;&amp;gt;&amp;gt; a = IntArray5(block.deref())
&amp;gt;&amp;gt;&amp;gt; a[0].value = 15
&amp;gt;&amp;gt;&amp;gt; bytearray(a[0]._mem)
bytearray(b'\x0f\x00\x00\x00\x00\x00\x00\x00')
&amp;gt;&amp;gt;&amp;gt; bytearray(a._mem)[0:8]
bytearray(b'\x0f\x00\x00\x00\x00\x00\x00\x00')&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Good so far, but we still can&amp;#8217;t pickle the &lt;code&gt;memoryview&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; pickle.dumps(a)
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
TypeError: cannot pickle 'memoryview' object&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;What about pickling the &lt;code&gt;Block&lt;/code&gt;, which can create the &lt;code&gt;memoryview&lt;/code&gt; from the &lt;code&gt;Heap&lt;/code&gt;?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; pickle.dumps(block)
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
TypeError: cannot pickle '_io.BufferedRandom' object&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now the problem is that we can&amp;#8217;t &lt;code&gt;pickle&lt;/code&gt; the open file backing the &lt;code&gt;Heap&lt;/code&gt;. And
in general, there&amp;#8217;s no way to pickle an open file since it might not be around
whenever another python process gets around to unpickling it. But we just need
to make pickling work when spawning new processes. As it turns out, the
&lt;code&gt;multiprocessing&lt;/code&gt; authors had the same problem, and came up with &lt;code&gt;DupFd&lt;/code&gt;. We
can use it to implement &lt;code&gt;Heap&lt;/code&gt;'s &lt;code&gt;pickle&lt;/code&gt; helpers:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;multiprocessing.reduction&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DupFd&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;DupFd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__setstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detach&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'a+b'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_maps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;memoryview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_maps&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="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Under the hood, &lt;code&gt;DupFd&lt;/code&gt; sets up a UNIX domain server which duplicates the file
descriptor, and then sends it to the client when requested. The pickle data is
just the address of the server:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; pickletools.dis(pickletools.optimize(pickle.dumps(h)))
    0: \x80 PROTO      4
    2: \x95 FRAME      113
   11: \x8c SHORT_BINUNICODE '__main__'
   21: \x8c SHORT_BINUNICODE 'Heap'
   27: \x93 STACK_GLOBAL
   28: )    EMPTY_TUPLE
   29: \x81 NEWOBJ
   30: \x8c SHORT_BINUNICODE 'multiprocessing.resource_sharer'
   63: \x8c SHORT_BINUNICODE 'DupFd'
   70: \x93 STACK_GLOBAL
   71: )    EMPTY_TUPLE
   72: \x81 NEWOBJ
   73: }    EMPTY_DICT
   74: \x8c SHORT_BINUNICODE '_id'
   79: \x8c SHORT_BINUNICODE '/tmp/pymp-i7ih27es/listener-a61oo9mt'
  117: K    BININT1    2
  119: \x86 TUPLE2
  120: s    SETITEM
  121: b    BUILD
  122: b    BUILD
  123: .    STOP&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The server shuts down after sending the file descriptor, so we can only
unpickle the heap once. Lets try it out:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; block = h.malloc(IntArray5.size)
&amp;gt;&amp;gt;&amp;gt; a = IntArray5(block.deref())
&amp;gt;&amp;gt;&amp;gt; b = IntArray5(pickle.loads(pickle.dumps(block)).deref())
&amp;gt;&amp;gt;&amp;gt; a[0].value = 85
&amp;gt;&amp;gt;&amp;gt; b[0].value
85&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Success! But wouldn&amp;#8217;t it be nice if we could just pickle the array directly?&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_shipping_and_receiving"&gt;Shipping and Receiving&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Going back to &lt;code&gt;Int64&lt;/code&gt;, we could rewrite it to take a &lt;code&gt;Heap&lt;/code&gt; instead of raw
memory:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_int64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deref&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&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;def&lt;/span&gt; &lt;span class="nf"&gt;__getstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_block&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__setstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_int64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deref&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="p"&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;But this breaks &lt;code&gt;Array&lt;/code&gt; and &lt;code&gt;Heap&lt;/code&gt;, since now we no longer have a way to create
an &lt;code&gt;Int64&lt;/code&gt; from memory. What we really want is a second &lt;code&gt;BoxedInt64&lt;/code&gt; which
takes a &lt;code&gt;Heap&lt;/code&gt; while the regular &lt;code&gt;Int64&lt;/code&gt; still uses memory directly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BoxedInt64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	&lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deref&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_block&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__setstate__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;
	&lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;_setstate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deref&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Where we implement &lt;code&gt;_setstate&lt;/code&gt; in &lt;code&gt;Int64&lt;/code&gt; like&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_setstate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
	&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_int64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Examining &lt;code&gt;BoxedInt64&lt;/code&gt;, you may notice that aside from inheriting from &lt;code&gt;Int64&lt;/code&gt;,
it is otherwise completely generic. In fact, we can create boxed types on the
fly by creating new subclasses with &lt;code&gt;ObjectType&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_Box&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Same as BoxedInt64
&lt;/span&gt;
&lt;span class="n"&gt;Box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Box'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_Box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{}))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Which we can now use like&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; a = Box[Array[Int64, 5]](h)
&amp;gt;&amp;gt;&amp;gt; b = pickle.loads(pickle.dumps(a))
&amp;gt;&amp;gt;&amp;gt; a[0].value = 33
&amp;gt;&amp;gt;&amp;gt; b[0].value
33&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&amp;#x220e;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_epilogue"&gt;Epilogue&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Hopefully this has been an interesting journey through the heart of
&lt;code&gt;mpmetrics&lt;/code&gt;. For expository purposes, I left out or skipped over many details,
such as the many other types, locking, and of course this doesn&amp;#8217;t even cover
the metrics themselves. If you are interested in more details of how this
library works, check out the
&lt;a href="https://mpmetrics.trends.tf/internals.html"&gt;&lt;code&gt;mpmetrics&lt;/code&gt; internals
documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</content><category term="mpmetrics"></category><category term="python"></category><category term="openmetrics"></category></entry><entry><title>Introducing mpmetrics</title><link href="https://blog.trends.tf/introducing-mpmetrics.html" rel="alternate"></link><published>2023-12-09T18:38:42-05:00</published><updated>2023-12-09T18:38:42-05:00</updated><author><name>Sean Anderson</name></author><id>tag:blog.trends.tf,2023-12-09:/introducing-mpmetrics.html</id><summary type="html">&lt;div class="sect1"&gt;
&lt;h2 id="_a_brief_introduction_to_metrics"&gt;A brief introduction to metrics&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Metrics are measurements we make on a program. For example, say we wanted to
know how long a function takes to call. We can wrap that function in a metric:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;prometheus_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generate_latest&lt;/span&gt;

&lt;span class="c1"&gt;# Create a metric to track time spent and …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</summary><content type="html">&lt;div class="sect1"&gt;
&lt;h2 id="_a_brief_introduction_to_metrics"&gt;A brief introduction to metrics&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Metrics are measurements we make on a program. For example, say we wanted to
know how long a function takes to call. We can wrap that function in a metric:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;prometheus_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generate_latest&lt;/span&gt;

&lt;span class="c1"&gt;# Create a metric to track time spent and requests made.
&lt;/span&gt;&lt;span class="n"&gt;REQUEST_TIME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'request_processing_seconds'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Time spent processing request'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Decorate function with metric.
&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;REQUEST_TIME&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;"""A dummy function that takes some time."""&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;After calling &lt;code&gt;process_requests&lt;/code&gt; a few times, we might see something like the
following when rendering the metrics:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; for i in range(25)
...  process_request(i/100)
...
&amp;gt;&amp;gt;&amp;gt; print(generate_latest().decode())
&amp;lt;snip&amp;gt;
# HELP request_processing_seconds Time spent processing request
# TYPE request_processing_seconds summary
request_processing_seconds_count 25.0
request_processing_seconds_sum 3.0034301709383726&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;From this output, it is possible to calculate the mean request time (120 ms).
We can also add labels to our metrics:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="n"&gt;REQUEST_TIME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'request_processing_seconds'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Time spent processing request'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="n"&gt;labelnames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'function'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;REQUEST_TIME&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'process_request'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;REQUEST_TIME&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'execute_query'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;which are enclosed in brackets when exposed:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="literalblock"&gt;
&lt;div class="content"&gt;
&lt;pre&gt;# HELP request_processing_seconds Time spent processing request
# TYPE request_processing_seconds summary
request_processing_seconds_count{function="process_requests"} 31.0
request_processing_seconds_sum{function="process_requests"} 15.573771364055574
request_processing_seconds_count{function="execute_query"} 30.0
request_processing_seconds_sum{function="execute_query"} 14.991517985239625&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;There are a variety of standard names and formats for metrics, as standardized
by
&lt;a href="https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md"&gt;OpenMetrics&lt;/a&gt;,
but they are all based on giving names to floating point numbers.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_prometheus_python_client"&gt;Prometheus Python Client&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Python has a library for working with metrics called
&lt;a href="https://github.com/prometheus/client_python"&gt;&lt;code&gt;prometheus_client&lt;/code&gt;&lt;/a&gt;, which is
shown in all of the above examples. This library works great for single-process
applications. Python&amp;#8217;s GIL makes it difficult to achieve good concurrency with
just threads, so it&amp;#8217;s common to run applications as multiple processes. To
ensure we have coherent metrics (which don&amp;#8217;t jump around based on which process
served the request) we need to synchronize metrics across different processes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;code&gt;prometheus_client&lt;/code&gt; does this by using the environmental variable
&lt;code&gt;PROMETHEUS_MULTIPROC_DIR&lt;/code&gt; to store several metrics files, one per process.
Each file contains a series of length-prefixed key-value pairs. Each key is a
string, and each value is a pair of doubles (the actual value and a timestamp).
The files themselves are memory-mapped, and updating or reading them just
involves a memcpy.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Unfortunately, this approach has several drawbacks:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Not all metrics are supported, and some features (such as exemplars) are not
supported either. In threaded mode, &lt;code&gt;prometheus_client&lt;/code&gt; also supports
grouping different metrics in different registries, allowing them to be
collected, filtered, and reported independently. In multiprocessing mode
there is just one, global registry.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The environmental variable enabling multiprocess mode must be set outside of
python. That is, it cannot be set programatically. This is inconvenient
because the directory should change each run to avoid inadvertently using
stale data from a previous run.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There is no synchronization between processes, nor is there any atomicity. To
use the above example, it would be possible to read an old value of
&lt;code&gt;request_processing_seconds_count&lt;/code&gt; and a new value of
&lt;code&gt;request_processing_seconds_sum&lt;/code&gt;. This is especially problematic for
infrequent events. On some architectures (although I don&amp;#8217;t believe x86 is
affected), torn reads/writes may result in completely bogus values being
read.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_mpmetrics"&gt;mpmetrics&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I found these restrictions to be limiting and unweildy, so I wrote a
&lt;a href="https://github.com/Forty-Bot/mpmetrics"&gt;multiprocess-safe metrics library&lt;/a&gt;. It
uses atomic integers (and doubles) in shared memory to ensure that we always get a
consistent view of the metrics. All of the above restrictions are lifted.
Although there&amp;#8217;s a lot going on under the hood, this is all hidden behind the
same API as &lt;code&gt;prometheus_client&lt;/code&gt;. Let&amp;#8217;s revisit the above example, but this time
using &lt;code&gt;mpmetrics&lt;/code&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mpmetrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Summary&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;prometheus_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;start_http_server&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;multiprocessing&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="c1"&gt;# Create a metric to track time spent and requests made.
&lt;/span&gt;&lt;span class="n"&gt;REQUEST_TIME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'request_processing_seconds'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Time spent processing request'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Decorate function with metric.
&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;REQUEST_TIME&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;"""A dummy function that takes some time."""&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This time we&amp;#8217;ll generate requests from multiple processes:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="rouge highlight"&gt;&lt;code data-lang="python"&gt;&lt;span class="c1"&gt;# Function to generate requests
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_requests&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;process_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Start up the server to expose the metrics.
&lt;/span&gt;    &lt;span class="n"&gt;start_http_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Generate some requests from two processes
&lt;/span&gt;    &lt;span class="n"&gt;multiprocessing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;generate_requests&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;generate_requests&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;You can navigate to &lt;a href="http://localhost:8000/metrics" class="bare"&gt;http://localhost:8000/metrics&lt;/a&gt; to view the metrics. If
you&amp;#8217;re interested, check out some
&lt;a href="https://github.com/Forty-Bot/mpmetrics/tree/master/examples"&gt;other examples&lt;/a&gt;, or
head over to the &lt;a href="https://mpmetrics.trends.tf/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</content><category term="mpmetrics"></category><category term="python"></category><category term="openmetrics"></category></entry><entry><title>Three pitfalls in I2C</title><link href="https://blog.trends.tf/three-pitfalls-in-i2c.html" rel="alternate"></link><published>2023-06-30T05:13:49-04:00</published><updated>2023-06-30T12:53:39-04:00</updated><author><name>Sean Anderson</name></author><id>tag:blog.trends.tf,2023-06-30:/three-pitfalls-in-i2c.html</id><summary type="html">&lt;div id="preamble"&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I recently implemented an I&lt;sup&gt;2&lt;/sup&gt;C slave, and came across a few interesting corner
cases in the specification.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_i2c_basics"&gt;I&lt;sup&gt;2&lt;/sup&gt;C basics&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I&lt;sup&gt;2&lt;/sup&gt;C is a multi-master, low-speed, bidirectional bus specified in
&lt;a href="https://www.nxp.com/docs/en/user-guide/UM10204.pdf"&gt;NXP UM10204&lt;/a&gt;. There are only
two signals: SCL (the clock) and SDA (the data). Each of …&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</summary><content type="html">&lt;div id="preamble"&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I recently implemented an I&lt;sup&gt;2&lt;/sup&gt;C slave, and came across a few interesting corner
cases in the specification.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_i2c_basics"&gt;I&lt;sup&gt;2&lt;/sup&gt;C basics&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I&lt;sup&gt;2&lt;/sup&gt;C is a multi-master, low-speed, bidirectional bus specified in
&lt;a href="https://www.nxp.com/docs/en/user-guide/UM10204.pdf"&gt;NXP UM10204&lt;/a&gt;. There are only
two signals: SCL (the clock) and SDA (the data). Each of the signals is
open-drain, with resistors pulling the signals high. This property is used
throughout the protocol. For example, by defining an acknowledgement (ack) as
holding SDA low, there is an implicit negative acknowledgement (nack) when no
device responds to a transaction.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The general format of transactions is&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;a start condition&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a 7-bit address&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a read/&lt;span class="overline"&gt;write&lt;/span&gt; bit&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;an acknowledgement&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;any number of number of data bytes, each followed by an acknowledgement&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a stop condition&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;For example, in the following diagram shows a single-byte read:&lt;/p&gt;
&lt;/div&gt;
&lt;script type="WaveDrom"&gt;
{ signal: [
  { name: "SDA", wave: "10=.|=.1.0.=.|=.1.0.1" },
  { name: "SCL", wave: "1.01|01010101|010101." },
],
  foot: {
    tock: '​ start ​ addr[6] … ​ addr[0] ​ R/W ​ ack ​ data[7] … ​ data[0] ​ nack ​ ​ stop '
  }
}
&lt;/script&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;SDA is valid on the rising edge of SCL, and SDA changes on the falling edge of
SCL. To signal the start and end of the transaction, SDA transitions with SCL
high. This framing violation makes it easy to re-synchronize the master/slave
state machines.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;An important aspect of I&lt;sup&gt;2&lt;/sup&gt;C that is not visible in the above diagram is who is
sending data. Because the signals are open-drain, both the master and slave can
drive the bus at the same time. The following diagram shows what the internal
drivers of SDA in the above transaction might look like:&lt;/p&gt;
&lt;/div&gt;
&lt;script type="WaveDrom"&gt;
{ signal: [
  { name: "SDA (actual)", wave: "10=.|=.1.0.=.|=.1.0.1" },
  { name: "SDA (master)", wave: "10=.|=.1.....|....0.1" },
  { name: "SDA (slave)",  wave: "1...|....0.=.|=.1...." },
  { name: "SCL",          wave: "1.01|01010101|010101." },
],
  foot: {
    tock: '​ start ​ addr[6] … ​ addr[0] ​ R/W ​ ack ​ data[7] … ​ data[0] ​ nack ​ ​ stop '
  }
}
&lt;/script&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;At the beginning of the transaction the master sends data on the bus, while
the slave leaves its SDA high. Then the slave acknowledges the request and
sends a byte of data. Since this is the last byte the master wants to read, the
master doesn&amp;#8217;t acknowledge the data and sends a stop condition.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_quick_reads"&gt;Quick reads&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;One of the shortest types of I&lt;sup&gt;2&lt;/sup&gt;C transactions is the quick read/write (so
named by &lt;a href="http://www.smbus.org/"&gt;SMBus&lt;/a&gt;). These transfer one bit of data in the
read/&lt;span class="overline"&gt;write&lt;/span&gt; bit following the address. Once the master receives an
ack, it sends the stop condition to end the transaction. In addition to
transfering a bit of data, these transactions can also be used as a heuristic
way of detecting available slaves (such as with
&lt;a href="https://git.kernel.org/pub/scm/utils/i2c-tools/i2c-tools.git/about/"&gt;i2cdetect&lt;/a&gt;).
The following diagram shows a successful quick read:&lt;/p&gt;
&lt;/div&gt;
&lt;script type="WaveDrom"&gt;
{ signal: [
  { name: "SDA", wave: "10=.|=.1.0.1" },
  { name: "SCL", wave: "1.01|010101." },
],
  foot: {
    tock: '​ start ​ addr[6] … ​ addr[0] ​ R/W ​ ack stop '
  }
}
&lt;/script&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;From the slave&amp;#8217;s point of view, a quick read looks just like a regular read
transaction. This can prevent the master from sending the stop condition if the
first bit of the byte is a 0, since the slave will hold SDA low. If the read
byte is all 0s, the slave won&amp;#8217;t release SDA until the ack bit:&lt;/p&gt;
&lt;/div&gt;
&lt;script type="WaveDrom"&gt;
{ signal: [
  { name: "SDA", wave: "10=.|=.1.0...|..1.0.1" },
  { name: "SCL", wave: "1.01|01010101|010101." },
],
  foot: {
    tock: '​ start ​ addr[6] … ​ addr[0] ​ R/W ​ ack ​ data[7] … ​ data[0] ​ nack ​ ​ stop '
  }
}
&lt;/script&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;When designing a slave, this can be avoided by ensuring that the first bit of
any read transaction is 1. If the slave has a &amp;#8220;command&amp;#8221; or sub-address
register which needs to be written as the first byte of a transaction, the
default data before the command register is written can be all 1s for the same
effect.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;From the master&amp;#8217;s perspective, all that is needed is to continue reading out
the byte until there is a high bit. This is guaranteed to happen when the slave
waits for an ack.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_sda_hold_time_ambiguity"&gt;SDA hold time ambiguity&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;While using coding violations for framing is a common technique, it creates a
conflict on the falling edge of SCL. If a slave sees SDA fall before SCL, it
can detect a spurious start/stop condition.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.trends.tf/hardware/images/i2c_timing_upper.png" alt="i2c timing upper"&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;SMBus versions before 3.0 specified a 300 ns minimum hold time (t&lt;sub&gt;HD;DAT&lt;/sub&gt;).
This ensures that other devices on the bus see SCL transition before SDA.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I&lt;sup&gt;2&lt;/sup&gt;C, on the other hand, has a minimum hold time of 0 seconds. Versions 6 and
earlier of UM10204 suggested the following solution:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="quoteblock"&gt;
&lt;blockquote&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;A device must internally provide a hold time of at least 300 ns for the SDA
signal (with respect to the V&lt;sub&gt;IH(min)&lt;/sub&gt; of the SCL signal) to bridge the
undefined region of the falling edge of SCL.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;That is, if a device detects a start/stop condition it must wait 300 ns before
doing anything. If SCL is still high, it was a real start/stop. Otherwise it
was just a data transition. The 300 ns value in both I&lt;sup&gt;2&lt;/sup&gt;C and SMBus is
t&lt;sub&gt;f&lt;/sub&gt;, or the maximum fall time. Waiting this long ensures that SCL has
transitioned before we sample SDA.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To allow for greater compatibility between SMBus and I&lt;sup&gt;2&lt;/sup&gt;C devices, SMBus
versions 3.0 and later reduce t&lt;sub&gt;HD;DAT&lt;/sub&gt; to 0 seconds. In a lengthy appendix,
they suggest using the same strategy as I&lt;sup&gt;2&lt;/sup&gt;C.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Despite this, version 7 of UM10204 seems to suggest that neither a 300 ns hold
time nor an internal delay are necessary to resolve this issue. Looking closely
at the timing diagram, t&lt;sub&gt;HD;DAT&lt;/sub&gt; is defined as the time between when SCL falls
to 30% V&lt;sub&gt;DD&lt;/sub&gt; (logical 0), and when SDA rises above 30% V&lt;sub&gt;DD&lt;/sub&gt; or falls below 70%
V&lt;sub&gt;DD&lt;/sub&gt;. Therefore, it suggests that devices&lt;/p&gt;
&lt;/div&gt;
&lt;div class="quoteblock"&gt;
&lt;blockquote&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Ensure SCL drops below 0.3 V&lt;sub&gt;DD&lt;/sub&gt; on falling edge before SDA crosses into the
indeterminate range of 0.3 V&lt;sub&gt;DD&lt;/sub&gt; to 0.7 V&lt;sub&gt;DD&lt;/sub&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Regarding masters which don&amp;#8217;t support clock stretching and don&amp;#8217;t have inputs on
SCL, UM10204 continues:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="quoteblock"&gt;
&lt;blockquote&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;For controllers that cannot observe the SCL falling edge then independent
measurement of the time for the SCL transition from static high (V&lt;sub&gt;DD&lt;/sub&gt;) to
0.3 V&lt;sub&gt;DD&lt;/sub&gt; should be used to insert a delay of the SDA transition with respect
to SCL&lt;/p&gt;
&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;effectively mandating a 300 ns hold time&amp;#8230;&amp;#8203; which is what SMBus switched away
from.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;However, even masters supporting clock stretching should still use a delay for
two reasons: First, it is difficult to detect when SCL falls below 30% V&lt;sub&gt;DD&lt;/sub&gt;,
since in typical implementations the entire region from 30–70% V&lt;sub&gt;DD&lt;/sub&gt; is
indeterminate. And second, devices with series protection resistors might not
see the same value on SDA as the transmitter, since there will be a voltage
difference across the resistor.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;For maximum compatibility, devices should implement both an output hold time
and an internal hold time when detecting start/stop conditions.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_implementation_support"&gt;Implementation support&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Unfortunately, despite much vacillation in SMBus and I&lt;sup&gt;2&lt;/sup&gt;C, this issue does not
seem to be known to some implementors. A quick survey of open-source
implementations reveals fairly patchy handling:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/w/index.php?title=I%C2%B2C&amp;amp;oldid=1153206437#Example_of_bit-banging_the_I2C_protocol"&gt;Wikipedia&amp;#8217;s
bitbang implementation&lt;/a&gt;, doesn&amp;#8217;t wait between &lt;code&gt;clear_SCL&lt;/code&gt; and &lt;code&gt;set_SDA&lt;/code&gt; in
&lt;code&gt;i2c_write_bit&lt;/code&gt;. That said, it doesn&amp;#8217;t seem to support multi-master busses,
so it may be assuming slaves with an internal hold time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/i2c/algos/i2c-algo-bit.c?id=6995e2de6891c724bfeb2db33d7b87775f913ad1"&gt;Linux&lt;/a&gt;
doesn&amp;#8217;t wait between &lt;code&gt;scllo&lt;/code&gt; and &lt;code&gt;setsda&lt;/code&gt; in &lt;code&gt;i2c_outb&lt;/code&gt;, but it doesn&amp;#8217;t seem
to support multi-master busses either. Some of the hardware-accelerated
drivers seem to be aware of this issue, and support configurable hold times.
This allows using the SMBus pre-3.0 solution, as long as all slaves also
support it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Neither the master nor slaves in
&lt;a href="https://github.com/alexforencich/verilog-i2c/tree/4a41d477568646617776681cea53ba8a6391c14f"&gt;Alex
Forencich&amp;#8217;s I&lt;sup&gt;2&lt;/sup&gt;C project&lt;/a&gt; seem to delay for a hold time or use an internal hold time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/freecores/i2c/tree/3b067f00ccced753b0502024766a51f58f3e04bc"&gt;Freecores&amp;#8217;
master&lt;/a&gt; doesn&amp;#8217;t add an internal hold time or use an internal hold time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;It&amp;#8217;s often unclear whether commercial implementations correctly handle this
ambiguity. For example, this
&lt;a href="https://ww1.microchip.com/downloads/en/DeviceDoc/doc0336.pdf"&gt;AT24 EEPROM
datasheet&lt;/a&gt; specifies a 0 second hold time, but doesn&amp;#8217;t mention any internal
hold time. Many vendors support
&lt;a href="https://microchip.my.site.com/s/article/SERCOM-I2C-SDA-hold-time-for-SAM-D-series"&gt;configurable
hold times&lt;/a&gt;, which shows they are aware of the issue. Occasionally, there are
&lt;a href="https://ams.com/documents/20143/36005/TMD2772_AN000257_1-00.pdf/b7b5fe57-00f0-115b-f855-3a75378ccb0e"&gt;errata
regarding it&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I suspect that for most hardware this ambiguity becomes an issue when the input
threshold voltage is on the low end. This could cause a rising SDA to be
detected before a falling SCL. This is exacerbated by high bus capacitance,
but many busses have low (a few dozen pF) capacitance. As with many timing
violations, mean time between failure can be quite long, and incorrect
implementations may not be noticed.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_fast_mode_plus_compatibility"&gt;Fast-mode Plus compatibility&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The original (Standard-mode) I&lt;sup&gt;2&lt;/sup&gt;C runs at 100 KHz, but UM10204 also includes a
backwards-compatible &amp;#8220;Fast-mode&amp;#8221; which runs at 400 KHz. There are also
&amp;#8220;High-speed mode&amp;#8221; and &amp;#8220;Ultra Fast-mode&amp;#8221; varients which are not backwards
compatible. In 2007, NXP introduced a &amp;#8220;Fast-mode Plus&amp;#8221; which runs at 1 Mhz
and was designed to be backwards-compatible. SMBus also incorporated this mode
into version 3.0.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To determine what a Fast-mode Plus slave needs to do to be backwards
compatible, let&amp;#8217;s first examine Fast-mode backwards-compatibility. For a
Fast-mode slave to be backwards compatible with Standard-mode, its input and
output timings must be compatible with both Standard-mode and Fast-mode.
Generally, output timings are the same as Fast-mode. Standard-mode only
requires a longer setup time, which will be met as long as the slave doesn&amp;#8217;t
stretch the clock. Similarly, input timings are mostly the same as Fast-mode.
One issue could be the internal hold time necessary for the SDA ambiguity
detailed above. However, both Standard- and Fast-mode specify a 300 ns fall
time (t&lt;sub&gt;f&lt;/sub&gt;), which is less than Fast-mode&amp;#8217;s 600 ns start condition setup time
(t&lt;sub&gt;SU;STA&lt;/sub&gt;). Therefore, the same 300 ns hold time can be used for both modes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.trends.tf/hardware/images/i2c_timing_lower.png" alt="i2c timing lower"&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Unfortunately, Fast-mode Plus reduced t&lt;sub&gt;SU;STA&lt;/sub&gt; to 260 ns in order to achieve
a higher clock rate. This means that every Fast-mode Plus start condition is
within the SDA hold time ambiguity in Fast- and Standard-mode. A slave which
implements the 300 ns internal delay required by Fast- and Standard-mode will
not be able to detect Fast-mode Plus start conditions with minimum-specified
delay.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;There are some ways to mitigate this at the system level:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;All bust masters could be configured to run at 960 kHz, which (if t&lt;sub&gt;SU;STA&lt;/sub&gt;
is scaled as well) will provide enough of a delay to ensure start times will
be detected correctly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Components with higher slew rates could be selected to ensure t&lt;sub&gt;f&lt;/sub&gt; remains
below 260 ns. Alternatively, bus line capacitance could be reduced below the
maximum.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;As well as some ways to mitigate this at the device level:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A configuration bit (such as a register or a pin) could configure the device
to be either Fast-mode or Fast-mode Plus compatible. This could even be
automatically detected, although this would need to be done carefully since
masters can switch speed at any time. For example, a master might run at one
speed when accessing a certain device, and another speed when accessing a
different device.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The input drivers could be engineered to have a lower V&lt;sub&gt;IH&lt;/sub&gt; and a higher
V&lt;sub&gt;IL&lt;/sub&gt;, reducing the time of ambiguity (assuming monotonic transitions).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;But, as-written, the Fast-mode Plus timings are incompatible with Fast- and
Standard-mode. Pre-3.0 SMBus and post-v7 I&lt;sup&gt;2&lt;/sup&gt;C are not affected because they do
not require an internal hold time.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</content><category term="hardware"></category><category term="i2c"></category><category term="smbus"></category><category term="pedantry"></category></entry><entry><title>The smallest inter-frame gap in Fast Ethernet</title><link href="https://blog.trends.tf/the-smallest-inter-frame-gap-in-fast-ethernet.html" rel="alternate"></link><published>2022-12-24T05:13:46-05:00</published><updated>2023-06-30T05:10:54-04:00</updated><author><name>Sean Anderson</name></author><id>tag:blog.trends.tf,2022-12-24:/the-smallest-inter-frame-gap-in-fast-ethernet.html</id><summary type="html">&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Fast_Ethernet"&gt;Fast Ethernet&lt;/a&gt; in the form of
100BASE-TX is a very mature technology, although there were
&lt;a href="http://www.sigcon.com/Pubs/news/5_7.htm"&gt;some hiccups in getting there&lt;/a&gt;. So it
was surprising to me to find a way for the PCS receiver to accept an
inter-frame gap shorter than the end-of-stream delimeter itself.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The 100BASE-TX Ethernet physical layer …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Fast_Ethernet"&gt;Fast Ethernet&lt;/a&gt; in the form of
100BASE-TX is a very mature technology, although there were
&lt;a href="http://www.sigcon.com/Pubs/news/5_7.htm"&gt;some hiccups in getting there&lt;/a&gt;. So it
was surprising to me to find a way for the PCS receiver to accept an
inter-frame gap shorter than the end-of-stream delimeter itself.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The 100BASE-TX Ethernet physical layer is broken up into several sub-layers:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.trends.tf/hardware/images/100basex_layers.png" alt="100basex layers"&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Working from the bottom up, the PMD converts analog signals from the medium to
&lt;a href="https://en.wikipedia.org/wiki/MLT-3_encoding"&gt;MLT-3 symbols&lt;/a&gt; (correcting for
channel loss and baseline wander), decodes those symbols to bits, descrambles
the bits, and then encodes the bits with NRZI. The PMA converts from NRZI back
to bits&lt;sup class="footnote"&gt;[&lt;a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote."&gt;1&lt;/a&gt;]&lt;/sup&gt;
and performs some other optional tasks (such as far-end fault detection when
using a medium without autonegotiation). The PCS performs
&lt;a href="https://en.wikipedia.org/wiki/4B5B"&gt;4B5B&lt;/a&gt;&lt;sup class="footnote"&gt;[&lt;a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote."&gt;2&lt;/a&gt;]&lt;/sup&gt;
alignment and decoding and generates data on the MII.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The PMA provides the PCS with a raw stream of bits. There is no indication of
where one nibble begins and another ends. To recover this information, each
nibble (4 bits) is encoded using 5 bits. Half of this encoding is used for
data, but the other half is either invalid or used for control code groups.
When no data is being transferred, the /I/ control code group (0b11111) is
continuously transmitted. The control code groups /J/ and /K/ (0b11000 and
0b10001) form the Start-of-Stream Delimiter (SSD), indicating the start of a
new frame:&lt;/p&gt;
&lt;/div&gt;
&lt;script type="WaveDrom"&gt;
{ signal : [
  { name: "clk",  wave: "p.............." },
  { name: "code", wave: "3....5....5....",   data: "/I/ /J/ /K/" },
  { name: "bits", wave: "1......0..10..1" },
]}
&lt;/script&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To ensure that this sequence can always be recognized, all other code groups
which (in combination with any other code group) could contain the sequence
0b1100010001 are invalid. The last control code groups to cover are /T/ and /R/
(0b01101 and 0b00111), which form the End-of-Stream Delimiter (ESD), indicating
the end of a frame. By convention, /V/ is used for invalid code groups.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;IEEE 802.3 specifies the PCS receiver through a state diagram. Each state
performs its actions continuously, and has several conditions leading to other
states. Conditions are formed of several comparisons or events, with &lt;code&gt;*&lt;/code&gt;
expressing &amp;#8220;and&amp;#8221; (conjunction). All conditions are evaluated continuously
(more on this later), and sometimes there are uncontrolled transitions (UCTs),
immediately transitioning from one state to another.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.trends.tf/hardware/images/100basex_pcs_rx.png" alt="100basex pcs rx"&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The variables used in the above diagram are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="dlist"&gt;
&lt;dl&gt;
&lt;dt class="hdlist1"&gt;link_status&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;Whether the link is up. For our purposes, we can assume that
this is always true.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="hdlist1"&gt;rx_bits&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;The last 10 bits we have received. The least significant bit (0) is
the most recent bit. These are not aligned, except when
gotCodeGroup.indicate occurs. In that case, rx_bits[4:0] is the most
recent code group, and rx_bits[9:5] is the next most recent.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="hdlist1"&gt;gotCodeGroup.indicate&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;This event occurs whenever there is a complete code
group in rx_bits. It is automatically generated every
five bits by the receive bits process (not shown). The
initial alignment is determined by when RX_DV goes
high.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="hdlist1"&gt;RX_DV&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;An MII signal indicating whether RXD is valid or not. This also
determines the alignment of gotCodeGroup.indicate.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="hdlist1"&gt;RXD&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;An MII signal indicating the recieved data.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="hdlist1"&gt;RX_ER&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;An MII signal indicating there was an error.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="hdlist1"&gt;receiving&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;Whether we are currently receiving a packet.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt class="hdlist1"&gt;rx_lpi&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;This is only used for Energy-Efficient Ethernet (EEE).&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let&amp;#8217;s add some of these signals to the SSD diagram from before:&lt;/p&gt;
&lt;/div&gt;
&lt;script type="WaveDrom"&gt;
{ signal : [
  { name: "clk",   wave: "p...................." },
  { name: "code",  wave: "3....5....5....7....7",   data: "/I/ /J/ /K/ DATA" },
  { name: "bits",  wave: "1......0..10..1x....." },
  { name: "state", wave: "=.........=....=....=",
    data: ["IDLE", "IDENTIFY JK", "START OF STREAM J"] },
  { name: "receiving",
                   wave: "0.........1.........." },
  { name: "gotCodeGroup.indicate",
                   wave: "0..................10" },
  { name: "RX_DV", wave: "0..............1....." },
  { name: "RXD",   wave: "x..............=....=", data: "0101" },
]}
&lt;/script&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;There are a few important things to note here. First, note that there is an
instant transition from CARRIER DETECT to IDENTIFY JK. This is because one of
the conditions for exiting CARRIER DETECT will always be true, and both will be
evaluated immediately. Second, although state and gotCodeGroup.indicate are
shown as having transition times, they are really instant from the state
machine&amp;#8217;s point of view. In particular, gotCodeGroup.indicate is only true for
one instant. If a state and its successor both depend on gotCodeGroup.indicate
to transition, they can&amp;#8217;t both transition off of the same gotCodeGroup.indicate
event. If implemented in hardware, states like CARRIER DETECT should be viewed
more as &amp;#8220;superstates&amp;#8221; (a la superclass) rather than states proper. The last
thing to note is that there&amp;#8217;s a delay of two code groups between when the first
bit of a code enters the PCS and when the data gets signalled on the MII.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now, lets&amp;#8217;s look at the end of a packet:&lt;/p&gt;
&lt;/div&gt;
&lt;script type="WaveDrom"&gt;
{ signal : [
  { name: "clk",   wave: "p..................." },
  { name: "code",  wave: "7....4....4....3....",   data: "DATA /T/ /R/ /I/" },
  { name: "bits",  wave: "x....01.010.1......." },
  { name: "state", wave: "=..............=....",
    data: ["RECEIVE", "IDLE"] },
  { name: "receiving",
                   wave: "1..............0...." },
  { name: "gotCodeGroup.indicate",
                   wave: "0...10...10...10...." },
  { name: "RX_DV", wave: "1..............0...." },
  { name: "RXD",   wave: "=....=....=....x....", data: "DATA DATA DATA" },
]}
&lt;/script&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Note that like CARRIER DETECT, END OF STREAM instantly transitions to IDLE, but
not before setting rx_bits to all 1s. This is as if we had already been sending
/I/I/ instead of /T/R/. We can abuse this to create (through non-standard
coding) the shortest possible inter-packet gap. The following diagram shows
the contents of rx_bits after END OF STREAM as &amp;#8220;virtual&amp;#8221; bits. It also shows
the actual received bits as &amp;#8220;real&amp;#8221; bits:&lt;/p&gt;
&lt;/div&gt;
&lt;script type="WaveDrom"&gt;
{ signal : [
  { name: "clk",   wave: "p..................." },
  { name: "real code",
                   wave: "4....4....9....9....",   data: "/T/ /R/ /V/ /V/" },
  { name: "real bits",
                   wave: "01.010.1..0..10..1x." },
  { name: "virtual code",
                   wave: "3..3....5....5....7.",   data: "/I/ /I/ /J/ /K/ DATA" },
  { name: "virtual bits",
                   wave: "1.........0..10..1x." },
  { name: "state", wave: "=.........=..=....=.",
    data: ["RECEIVE", "IDLE", "IDENTIFY JK", "START..."] },
  { name: "receiving",
                   wave: "1.........0..1......" },
  { name: "gotCodeGroup.indicate",
                   wave: "0...10...10........." },
  { name: "RX_DV", wave: "1.........0.......1." },
  { name: "RXD",   wave: "=....=....x.......=.", data: "DATA DATA 0101" },
]}
&lt;/script&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;A fully-compliant 100BASE-X PCS will exactly follow the state transitions in
the diagram above, allowing an inter-frame gap of just 0.8 octets.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;It appears that the designers of the PCS state machine wished to allow
back-to-back frames (e.g. /T/R/J/K/)&lt;sup class="footnote"&gt;[&lt;a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote."&gt;3&lt;/a&gt;]&lt;/sup&gt;.
If this is indeed the case, we need to be careful when fixing the state
machine. We could omit the clearing of rx_bits, but this would cause us to
erroneously transition to BAD SSD (as we would transition to CARRIER DETECT
when rx_bits[9:0] = /R/J/). Adding a condition on gotCodeGroup.indicate would
not work for similar reasons.  Instead, we should add an intermediate PRE IDLE
state after END OF STREAM. This state would set receiving, RX_ER, and RX_DV to
FALSE. It would transition to IDLE on gotCodeGroup.indicate.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In practice, this &amp;#8220;bug&amp;#8221; has no consequences. This is for three reasons.
First, conforming PCSs can never generate this bit pattern. Second, conforming
MACs use an inter-frame gap of at least 12 octets. And, third, most PCSs do not
implement the specification exactly. This is because an exact implementation
imposes difficult-to-meet timing requirements due to the tight coupling between
the receive bits process and the recieve process. A typical implementation may
register the outputs of the receive bits process, which does not allow for the
instant feedback necessary to re-align the receive bits process and trigger the
bug.&lt;/p&gt;
&lt;/div&gt;
&lt;div id="footnotes"&gt;
&lt;hr&gt;
&lt;div class="footnote" id="_footnotedef_1"&gt;
&lt;a href="#_footnoteref_1"&gt;1&lt;/a&gt;. Converting from binary to NRZI and back again is not terribly efficient (and I suspect that almost no integrated implementations of 100BASE-TX include it). So why is it specified in the standard? The 100BASE-TX PMD is largely based on the CDDI (Copper Distributed Data Interface) PMD. That PMD was designed to be a drop-in replacement for the FDDI (Fiber Distributed Data Interface) PMD, so that the PCS and PMA could be reused. The FDDI PMD just performed some filtering and conversion from optical to electrical signalling (much like modern optical SFP modules). The NRZI conversion itself was done in the PMA. To keep compatibility, the PMD decodes MLT-3 symbols and then re-encode them as NRZI. This is also why descrambling happens in the PMD, even though it probably should happen in the PMA.
&lt;/div&gt;
&lt;div class="footnote" id="_footnotedef_2"&gt;
&lt;a href="#_footnoteref_2"&gt;2&lt;/a&gt;. Also inherited from FDDI
&lt;/div&gt;
&lt;div class="footnote" id="_footnotedef_3"&gt;
&lt;a href="#_footnoteref_3"&gt;3&lt;/a&gt;. I don&amp;#8217;t know whether back-to-back frame support was inherited from FDDI or was introduced with Fast Ethernet. It&amp;#8217;s likely that someone produced PCSs which allowed this, and lobbied to ensure that such behavior remained standard
&lt;/div&gt;
&lt;/div&gt;</content><category term="hardware"></category><category term="ethernet"></category><category term="pedantry"></category></entry><entry><title>Hello World</title><link href="https://blog.trends.tf/hello-world.html" rel="alternate"></link><published>2022-12-24T05:11:36-05:00</published><updated>2022-12-24T05:11:36-05:00</updated><author><name>Sean Anderson</name></author><id>tag:blog.trends.tf,2022-12-24:/hello-world.html</id><content type="html">&lt;div class="paragraph"&gt;
&lt;p&gt;A blog for my musings. No schedule. Updated when I feel like it.&lt;/p&gt;
&lt;/div&gt;</content><category term="misc"></category></entry></feed>