Skip to content

Commit

Permalink
Automated publish
Browse files Browse the repository at this point in the history
  • Loading branch information
Github Actions committed Dec 17, 2023
1 parent 6aa8631 commit fecbe37
Show file tree
Hide file tree
Showing 2 changed files with 4 additions and 3 deletions.
5 changes: 3 additions & 2 deletions blog/2023-12-17-module-loading/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@
<span class="line"><span style="color:#f8f8f2"> ([a &amp; args]</span></span>
<span class="line"><span style="color:#f8f8f2"> :variadic))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#f8f8f2">(</span><span style="color:#a6e22e">ambiguous</span><span style="color:#f8f8f2"> :a) </span><span style="color:#88846f">; =&gt; should be :fixed</span></span></code></pre><p>What jank was trying to do was call the variadic arity, with an empty seq for <code>args</code>, rather than to call the fixed arity. This is because both of them require one fixed argument first and the information I was storing for each function object was the required fixed args prior to variadic arg packing.<p>The equivalent function in Clojure JVM is <code>RestFn.getRequiredArity</code>, which returns the required fixed position arguments prior to the packed args. However, where Clojure JVM differs from jank is that Clojure uses dynamic dispatch to solve this ambiguity whereas jank does its own overload matching, for performance reasons.<p>To actually solve this problem, we need to know three things:<ol><li>Is the function variadic?<li>Is there an ambiguous fixed overload?<li>How many fixed arguments are required before the packed args?</ol><p>We cannot perform the correct call without <em>all</em> of this information. Also, function calls in a functional programming language like Clojure are on the hottest of hot code paths, so I can't exactly add two more virtual functions to jank's <code>callable</code> interface to get this data. In truth, even keeping one function but putting all of this data in a struct proved too much of an impact on the performance. Thus, we encode the data more compactly.<p>jank now packs all of this into a single byte. Questions 1 and 2 each get a high bit and question 3 gets the 6 remaining bits (it only uses 4) to store the fixed arg count. So, this byte for our <code>ambiguous</code> function above would look like this:<pre class="shiki"style="background-color:#272822"><code><span class="line"><span style="color:#ae81ff">1</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">1</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">0</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">0</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">0</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">0</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">0</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">1</span></span>
<span class="line"><span style="color:#f8f8f2">(</span><span style="color:#a6e22e">ambiguous</span><span style="color:#f8f8f2"> :a) </span><span style="color:#88846f">; =&gt; should be :fixed</span></span></code></pre><p>What jank was trying to do was call the variadic arity, with an empty seq for <code>args</code>, rather than to call the fixed arity. This is because both of them require one fixed argument first and the information I was storing for each function object was the required fixed args prior to variadic arg packing.<p>The equivalent function in Clojure JVM is <code>RestFn.getRequiredArity</code>, which returns the required fixed position arguments prior to the packed args. However, where Clojure JVM differs from jank is that Clojure uses dynamic dispatch to solve this ambiguity whereas jank does its own fixed vs variadic overload matching, for performance reasons.<p>To actually solve this problem, we need to know three things:<ol><li>Is the function variadic?<li>Is there an ambiguous fixed overload?<li>How many fixed arguments are required before the packed args?</ol><p>We cannot perform the correct call without <em>all</em> of this information. Also, function calls in a functional programming language like Clojure are on the hottest of hot code paths, so I can't exactly add two more virtual functions to jank's <code>callable</code> interface to get this data. In truth, even keeping one function but putting all of this data in a struct proved too much of an impact on the performance. Thus, we need to encode the data more compactly.<p>jank now packs all of this into a single byte. Questions 1 and 2 each get a high bit and question 3 gets the 6 remaining bits (of which it uses 4) to store the fixed arg count. So, this byte for our <code>ambiguous</code> function above would look like this:<pre class="shiki"style="background-color:#272822"><code><span class="line"><span style="color:#ae81ff">1</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">1</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">0</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">0</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">0</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">0</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">0</span><span style="color:#f8f8f2"> </span><span style="color:#ae81ff">1</span></span>
<span class="line"><span style="color:#f92672">^</span><span style="color:#f8f8f2"> </span><span style="color:#f92672">^</span><span style="color:#f8f8f2"> </span><span style="color:#f92672">^---------------</span></span>
<span class="line"><span style="color:#f92672">|</span><span style="color:#f8f8f2"> </span><span style="color:#f92672">|</span><span style="color:#f8f8f2"> </span><span style="color:#f92672">|</span></span>
<span class="line"><span style="color:#f92672">|</span><span style="color:#f8f8f2"> </span><span style="color:#f92672">|</span><span style="color:#88846f"> /* How many fixed arguments are required before the packed args? */</span></span>
<span class="line"><span style="color:#f92672">|</span><span style="color:#88846f"> /* Is there an ambiguous overload? */</span></span>
<span class="line"><span style="color:#88846f">/* Is the function variadic? */</span></span></code></pre><p>From there, when we use it, we disable the bit for question 2 and we <code>switch</code> on the rest. This allows us to do a O(1) jump on the combination of whether it's variadic and the required fixed args. Finally, we only need the question 2 bit to disambiguate one branch of each switch, which is the branch equal to however many arguments we received.<pre class="shiki"style="background-color:#272822"><code><span class="line"><span style="color:#a6e22e">object_ptr</span><span style="color:#f8f8f2"> </span><span style="color:#a6e22e">dynamic_call</span><span style="color:#f8f8f2">(</span><span style="color:#a6e22e">object_ptr</span><span style="color:#f8f8f2"> </span><span style="color:#f92672">const</span><span style="color:#f8f8f2"> </span><span style="color:#fd971f;font-style:italic">source</span><span style="color:#f8f8f2">, </span><span style="color:#a6e22e">object_ptr</span><span style="color:#f8f8f2"> </span><span style="color:#f92672">const</span><span style="color:#f8f8f2"> </span><span style="color:#fd971f;font-style:italic">a1</span><span style="color:#f8f8f2">)</span></span>
<span class="line"><span style="color:#88846f">/* Is the function variadic? */</span></span></code></pre><p>From there, when we use it, we disable the bit for question 2 and we <code>switch</code> on the rest. This allows us to do a <code>O(1)</code> jump on the combination of whether it's variadic and the required fixed args. Finally, we only need the question 2 bit to disambiguate one branch of each switch, which is the branch equal to however many arguments we received.<pre class="shiki"style="background-color:#272822"><code><span class="line"><span style="color:#a6e22e">object_ptr</span><span style="color:#f8f8f2"> </span><span style="color:#a6e22e">dynamic_call</span><span style="color:#f8f8f2">(</span><span style="color:#a6e22e">object_ptr</span><span style="color:#f8f8f2"> </span><span style="color:#f92672">const</span><span style="color:#f8f8f2"> </span><span style="color:#fd971f;font-style:italic">source</span><span style="color:#f8f8f2">, </span><span style="color:#a6e22e">object_ptr</span><span style="color:#f8f8f2"> </span><span style="color:#f92672">const</span><span style="color:#f8f8f2"> </span><span style="color:#fd971f;font-style:italic">a1</span><span style="color:#f8f8f2">)</span></span>
<span class="line"><span style="color:#f8f8f2">{</span></span>
<span class="line"><span style="color:#f8f8f2"> </span><span style="color:#f92672">return</span><span style="color:#f8f8f2"> visit_object</span></span>
<span class="line"><span style="color:#f8f8f2"> (</span></span>
Expand All @@ -79,6 +79,7 @@
<span class="line"><span style="color:#88846f"> check the extra bit in the flags. */</span></span>
<span class="line"><span style="color:#f8f8f2"> </span><span style="color:#f92672">if</span><span style="color:#f8f8f2">(</span><span style="color:#f92672">!</span><span style="color:#a6e22e">callable</span><span style="color:#f8f8f2">::</span><span style="color:#a6e22e">is_variadic_ambiguous</span><span style="color:#f8f8f2">(arity_flags))</span></span>
<span class="line"><span style="color:#f8f8f2"> { </span><span style="color:#f92672">return</span><span style="color:#f8f8f2"> typed_source-&gt;</span><span style="color:#a6e22e">call</span><span style="color:#f8f8f2">(a1, </span><span style="color:#a6e22e">obj</span><span style="color:#f8f8f2">::</span><span style="color:#a6e22e">nil</span><span style="color:#f8f8f2">::</span><span style="color:#a6e22e">nil_const</span><span style="color:#f8f8f2">()); }</span></span>
<span class="line"><span style="color:#88846f"> /* We're falling through! */</span></span>
<span class="line"><span style="color:#f8f8f2"> </span><span style="color:#f92672">default</span><span style="color:#f8f8f2">:</span></span>
<span class="line"><span style="color:#88846f"> /* The default case is not variadic. */</span></span>
<span class="line"><span style="color:#f8f8f2"> </span><span style="color:#f92672">return</span><span style="color:#f8f8f2"> typed_source-&gt;</span><span style="color:#a6e22e">call</span><span style="color:#f8f8f2">(a1);</span></span>
Expand Down
Loading

0 comments on commit fecbe37

Please sign in to comment.