<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Algorithms-and-Data-Structures | AkitaOnRails.com</title><link>https://akitaonrails.github.io/en/tags/algorithms-and-data-structures/</link><description>Fabio Akita's blog — tech, career, and assorted geek topics. English edition.</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 19 Oct 2026 23:20:51 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/en/tags/algorithms-and-data-structures/index.xml" rel="self" type="application/rss+xml"/><item><title>How does Bitcoin force consensus among Byzantine generals?</title><link>https://akitaonrails.github.io/en/2017/11/01/how-does-bitcoin-force-consensus-among-byzantine-generals/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2017/11/01/how-does-bitcoin-force-consensus-among-byzantine-generals/</guid><pubDate>Wed, 01 Nov 2017 14:02:00 -0200</pubDate><description>&lt;p&gt;&lt;em&gt;&amp;ldquo;Is it possible to break the blockchain?&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now, this is a fair question. If you know anything about the blockchain architecture, you instinctively conclude that &amp;ldquo;no&amp;rdquo;, it&amp;rsquo;s quite improbable that anyone will break it. In practice, it&amp;rsquo;s basically impossible.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s quite amazing that most of my peer programmers have a very difficult time overcoming the prejudice against cryptocurrencies. I have no idea where this prejudice comes from, but I know very smart people that can solve the most difficult web scalability problems, but that never once glanced over Satoshi Nakamoto&amp;rsquo;s extremely short original paper describing the blockchain.&lt;/p&gt;</description><content:encoded><![CDATA[<p><em>&ldquo;Is it possible to break the blockchain?&rdquo;</em></p>
<p>Now, this is a fair question. If you know anything about the blockchain architecture, you instinctively conclude that &ldquo;no&rdquo;, it&rsquo;s quite improbable that anyone will break it. In practice, it&rsquo;s basically impossible.</p>
<p>It&rsquo;s quite amazing that most of my peer programmers have a very difficult time overcoming the prejudice against cryptocurrencies. I have no idea where this prejudice comes from, but I know very smart people that can solve the most difficult web scalability problems, but that never once glanced over Satoshi Nakamoto&rsquo;s extremely short original paper describing the blockchain.</p>
<p>Seriously, the <a href="https://bitcoin.org/bitcoin.pdf"target="_blank" rel="noopener">Bitcoin: A Peer-to-Peer Electronic Cash System</a> paper is so ridiculously small and easy to understand that most computer science students should be able to understand it. So all the smart programmers I know should be able to grasp it in a coffee break. Any average programmer should be able to read and understand this paper in 30 minutes or so.</p>
<p>You can simplify a mental model of it as a Linked List, each node of the List is what we call a Block. Each Block is a stupid struct with the usual previous/next pointers and a body comprised of a tree-like structure (a <a href="https://brilliant.org/wiki/merkle-tree/"target="_blank" rel="noopener">Merkle Tree</a>, to be more exact).</p>
<p>The catch is that each block has the hash signature of the previous block, thus creating a secure &ldquo;chain&rdquo;. Hence &ldquo;block-chain&rdquo;.</p>
<p>Yes, in computer science terms, we&rsquo;re dealing with undergraduate levels of data structures here. If you understand a Linked List and a stupid Binary Tree, plus the easiest crypto thing to understand, a simple digest hash such as SHA256, and boom, you understand the basic backbone of the blockchain database.</p>
<p>Yes, it is just a database. A distributed-database to be more exact. Or a very crude and simple distributed database for that matter. It is not very efficient, and it pales in comparison to more serious NoSQL distributed databases such as Redis or Cassandra. So the query-abilities are basically non-existent beyond finding a block by its identity.</p>
<p>Of course, the Bitcoin source-code is more sophisticated than that but the basics are really so ridiculous that you don&rsquo;t need more than 20 lines of Ruby code to replicate it. Check out this example implementation from <a href="https://github.com/openblockchains/awesome-blockchains/tree/master/blockchain.rb"target="_blank" rel="noopener">Gerald Bauer</a>.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;digest&#34;</span>    <span class="c1"># for hash checksum digest function SHA256</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Block</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:index</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:timestamp</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:data</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:previous_hash</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:hash</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">previous_hash</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@index</span>         <span class="o">=</span> <span class="n">index</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@timestamp</span>     <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@data</span>          <span class="o">=</span> <span class="n">data</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@previous_hash</span> <span class="o">=</span> <span class="n">previous_hash</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@hash</span>          <span class="o">=</span> <span class="n">calc_hash</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">calc_hash</span>
</span></span><span class="line"><span class="cl">    <span class="n">sha</span> <span class="o">=</span> <span class="no">Digest</span><span class="o">::</span><span class="no">SHA256</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="n">sha</span><span class="o">.</span><span class="n">update</span><span class="p">(</span> <span class="vi">@index</span><span class="o">.</span><span class="n">to_s</span> <span class="o">+</span> <span class="vi">@timestamp</span><span class="o">.</span><span class="n">to_s</span> <span class="o">+</span> <span class="vi">@data</span> <span class="o">+</span> <span class="vi">@previous_hash</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sha</span><span class="o">.</span><span class="n">hexdigest</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">first</span><span class="p">(</span> <span class="n">data</span><span class="o">=</span><span class="s2">&#34;Genesis&#34;</span> <span class="p">)</span>    <span class="c1"># create genesis (big bang! first) block</span>
</span></span><span class="line"><span class="cl">    <span class="c1">## uses index zero (0) and arbitrary previous_hash (&#34;0&#34;)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Block</span><span class="o">.</span><span class="n">new</span><span class="p">(</span> <span class="mi">0</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="s2">&#34;0&#34;</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">next</span><span class="p">(</span> <span class="n">previous</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="s2">&#34;Transaction Data...&#34;</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Block</span><span class="o">.</span><span class="n">new</span><span class="p">(</span> <span class="n">previous</span><span class="o">.</span><span class="n">index</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">previous</span><span class="o">.</span><span class="n">hash</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I know, right!?</p>
<p>One question remains: how does this simple structure become a &ldquo;distributed&rdquo; database?</p>
<p>Now, either you need to have a centralized &ldquo;master-copy&rdquo; out of which all other copies replicate from. Or you need some form of &ldquo;consensus&rdquo; between the different copies.</p>
<p>How do you reach consensus between rogue, random nodes spread across the globe? This is the problem that is called <a href="http://pmg.csail.mit.edu/papers/osdi99.pdf"target="_blank" rel="noopener">&ldquo;Byzantine Fault Tolerance&rdquo;</a>, masterfully explained and solved by Barbara Liskov and Miguel Castro, from MIT, in 1999.</p>
<p>In a nutshell, imagine that you have Byzantine generals, each with their own armies, surrounding a hostile city. Now, you can either attack or retreat. But all generals must either do one or the other, in consensus. How do you reach consensus when you don&rsquo;t have direct communication with all the generals and, worse, when some of the generals may be traitors or double-agents?</p>
<p>That&rsquo;s the kind of problem we face here. Anyone on the internet can download a copy of the blockchain, and they can check that the blocks are valid and unadulterated by recomputing the digest hashes for each block.</p>
<p>But how do you add new blocks and make the other nodes accept your new block?</p>
<p>That&rsquo;s why Satoshi added the so-called &ldquo;Proof of Work&rdquo; to the equation. Remember that I said that each block is chained together to the previous by containing the hash of the previous block? Computing a digest hash is quite trivial these days.</p>
<p>In Ruby if you do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Digest</span><span class="o">::</span><span class="no">SHA256</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(</span><span class="s2">&#34;abcd&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; &#34;88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">Digest</span><span class="o">::</span><span class="no">SHA256</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(</span><span class="s2">&#34;123&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; &#34;a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It takes a <strong>fraction of a millisecond</strong> to run.</p>
<p>Now, what if I ask you to find the hash that starts with a certain amount of &ldquo;zeroes&rdquo; in the beginning of the hash?</p>
<p>For example:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># I want to find 4 zeros (&#34;0000&#34;) in the hash:</span>
</span></span><span class="line"><span class="cl"><span class="no">Digest</span><span class="o">::</span><span class="no">SHA256</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(</span><span class="s2">&#34;79026&#34;</span> <span class="o">+</span> <span class="s2">&#34;123&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; &#34;0000559fb4a55f135c7db3d83405b86b4b63cd035993873a5b676bae08b64334&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>How do I know that I had to prepend &ldquo;79026&rdquo;? I don&rsquo;t, I have to start from 0 and incrementing one by one until I find the hash with the format I want.</p>
<p>If we check <a href="https://github.com/openblockchains/awesome-blockchains/blob/master/blockchain.rb/blockchain_with_proof_of_work.rb#L29-L45"target="_blank" rel="noopener">Gerald&rsquo;s example</a> we would implement this lookup like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">compute_hash_with_proof_of_work</span><span class="p">(</span> <span class="n">difficulty</span><span class="o">=</span><span class="s2">&#34;00&#34;</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">nonce</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">  <span class="kp">loop</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nb">hash</span> <span class="o">=</span> <span class="n">calc_hash_with_nonce</span><span class="p">(</span> <span class="n">nonce</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">hash</span><span class="o">.</span><span class="n">start_with?</span><span class="p">(</span> <span class="n">difficulty</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="o">[</span><span class="n">nonce</span><span class="p">,</span><span class="nb">hash</span><span class="o">]</span>    <span class="c1">## bingo! proof of work if hash starts with leading zeros (00)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">nonce</span> <span class="o">+=</span> <span class="mi">1</span>             <span class="c1">## keep trying (and trying and trying)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">calc_hash_with_nonce</span><span class="p">(</span> <span class="n">nonce</span><span class="o">=</span><span class="mi">0</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">sha</span> <span class="o">=</span> <span class="no">Digest</span><span class="o">::</span><span class="no">SHA256</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="n">sha</span><span class="o">.</span><span class="n">update</span><span class="p">(</span> <span class="n">nonce</span><span class="o">.</span><span class="n">to_s</span> <span class="o">+</span> <span class="s2">&#34;123&#34;</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">sha</span><span class="o">.</span><span class="n">hexdigest</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Just a simple SHA256 takes somewhere between 0.000010 to 0.000020 seconds (remember: fractions of milliseconds). Now how long does it take to find that &ldquo;79026&rdquo; (which we call a &ldquo;nonce&rdquo;)?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>&gt; puts Benchmark.measure { compute_hash_with_proof_of_work(&#34;0000&#34;) }
  0.190000   0.000000   0.190000 (  0.189615)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Yep, considerably more, now it takes 0.18 seconds instead of 0.000020. We can increase the &ldquo;difficult&rdquo; variable to make it even more laborious to find the nonce. And that&rsquo;s exactly how Bitcoin is implemented: each block adjusts the difficulty so that the fastest time to find the hash for the next block is around 10 minutes.</p>
<p>And this, my friends, is what we call <strong>&ldquo;MINING&rdquo;</strong>. What a miner does is compute a loop, incrementing nonces, over the block digest to find the correct nonce.</p>
<p>Once a nonce is found, the miner can add the block to the blockchain and broadcast it to other nodes. The other nodes can then double-check (and now it&rsquo;s just the 0.000020 seconds procedure again, super fast).</p>
<p>When the nodes double-check and confirm the nonce, they all add the block to the top of the blockchain. And usually, when the other miners keep adding other blocks on top of that, that block becomes &ldquo;solidified&rdquo;. The most recent block on the top of the blockchain is usually unstable, but once you have more blocks on top of it, it is said to be more &ldquo;guaranteed&rdquo;. Which is why most exchanges and other services that accept bitcoin wait for the so-called &ldquo;6 blocks&rdquo; confirmation.</p>
<p>And because the difficulty is such that the fastest node takes around &ldquo;10 minutes&rdquo; to find that nonce, a block is said to be &ldquo;secure&rdquo; when around 1 hour passes and 6 blocks are added after it.</p>
<h2>Can we break this?<span class="hx:absolute hx:-mt-20" id="can-we-break-this"></span>
    <a href="#can-we-break-this" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now, you will understand why we talk about &ldquo;hash power&rdquo; when we talk about mining.</p>
<p>Mining is the act of signing and confirming blocks to the blockchain. It&rsquo;s a maintenance service, which is why you reward miners with &ldquo;transaction fees&rdquo; and a couple of &ldquo;satoshis&rdquo; (fractions of 1 Bitcoin), for their work. And also why you call this &ldquo;Proof of Work&rdquo; because when someone finds a nonce, we know it had to go through a lot of hash computation to reach it.</p>
<p>And also why we talk about CPUs or GPUs that miners use to have &ldquo;hash rates&rdquo;. You need to have an absurd capacity to be able to mine Bitcoins nowadays. No one will use a home-built rig to do it. One must build special hardware, such as the famous <a href="https://www.cryptocompare.com/mining/bitmain/antminer-s9-miner/"target="_blank" rel="noopener">AntMiners</a>. A USD 1,500 AntMiner S9 is able to compute around 14 TH.</p>
<p>Each crypto-currency different from Bitcoin calculates hashes differently so the hashrate differs from coin to coin.</p>
<p>The current Hash Power of the entire Bitcoin consensus network is almost reaching 14 EH (exa-hashes or millions of tera-hashes).</p>
<p>So, let&rsquo;s say that I am a billionaire and I want to troll the Bitcoin community by adding enough hash power to surpass the entire hash power of the network. I&rsquo;d have to buy 1 million AntMiner s9, or an investment of around USD 1.5 billion! And this is without adding the energy required to boot and run those machines, of course.</p>
<p>But even then, do you know what happens? Remember that difficulty variable I mentioned above? It will adjust again, to make sure the next block takes 10 minutes to compute again!</p>
<p>Then, no, even if you&rsquo;re willing to put USD 1.5 billion to waste, you won&rsquo;t break it. And that&rsquo;s how Bitcoin deals with Byzantine generals in this consensus network.</p>
]]></content:encoded><category>bitcoin-and-cryptocurrency</category><category>computer-science</category><category>algorithms-and-data-structures</category></item><item><title>Matches, Rankings, The Social Network, League of Legends, and Ruby?</title><link>https://akitaonrails.github.io/en/2016/11/01/matches-rankings-the-social-network-league-of-legends-and-ruby/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/11/01/matches-rankings-the-social-network-league-of-legends-and-ruby/</guid><pubDate>Tue, 01 Nov 2016 10:20:00 -0200</pubDate><description>&lt;p&gt;There is a segment in a series of talks that I have been presenting in Brazil for the last 3 years or so that I never blogged about. Yesterday I just &lt;a href="http://www.akitaonrails.com/en/2016/10/31/implementacao-ruby-on-rails-sistema-ranking-popularidade-correto"target="_blank" rel="noopener"&gt;posted about the proper way to rank content by &amp;ldquo;popularity&amp;rdquo;&lt;/a&gt; so I thought I should revisit the theme.&lt;/p&gt;
&lt;p&gt;To begin, I believe by now most people already watched the movie &amp;ldquo;The Social Network&amp;rdquo;. It&amp;rsquo;s interesting that I heard many people saying how this movie influenced them to begin their own startups. So I was thinking, &amp;ldquo;what does this movie actually teach?&amp;rdquo;&lt;/p&gt;</description><content:encoded><![CDATA[<p>There is a segment in a series of talks that I have been presenting in Brazil for the last 3 years or so that I never blogged about. Yesterday I just <a href="http://www.akitaonrails.com/en/2016/10/31/implementacao-ruby-on-rails-sistema-ranking-popularidade-correto"target="_blank" rel="noopener">posted about the proper way to rank content by &ldquo;popularity&rdquo;</a> so I thought I should revisit the theme.</p>
<p>To begin, I believe by now most people already watched the movie &ldquo;The Social Network&rdquo;. It&rsquo;s interesting that I heard many people saying how this movie influenced them to begin their own startups. So I was thinking, &ldquo;what does this movie actually teach?&rdquo;</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/567/pasted-image-765.jpg" alt="The Social Network Casting"  loading="lazy" /></p>
<p>Well, from the movie we learn that David Fincher is a fantastic director, that Aaron Sorkin writes very sharp dialogue that is very compelling, that Justin Timberlake does a better Sean Parker than the real deal, that Andrew Garfield does an ok Eduardo Saverin, and that Jesse Eisenberg will forever be Mark Zuckerberg.</p>
<p>And that&rsquo;s it, we can&rsquo;t learn anything else from the movie.</p>
<p>Or can we?</p>
<h3>Facemesh<span class="hx:absolute hx:-mt-20" id="facemesh"></span>
    <a href="#facemesh" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>One of my favorite scenes from the movie is when Zuckerberg/Eisenberg is pissed by the Erica Albright split up and he starts scraping photos of women from Harvard websites and organizing them into a troll website called &ldquo;Facemesh&rdquo; where he puts them to compete. People can vote on which photo they prefer and it sorts them out in a ranking of popularity.</p>
<p>When Eduardo Saverin/Andrew Garfield shows up, Zuckerberg asks him:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/BzZRr4KV59I"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<blockquote>
  <p>&ldquo;Wardo, I need you (&hellip;) I need the algorithm used to rank chess players&rdquo;</p>

</blockquote>
<p>And he goes ahead and writes the following on the dorm room window:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/569/pasted-image-759.jpg" alt="Through the Looking Glass"  loading="lazy" /></p>
<p>Here most people would just think:</p>
<blockquote>
  <p>&ldquo;pff, another gibberish formula just to show off that they are little geniuses, but most certainly this formula doesn&rsquo;t even exist&rdquo;</p>

</blockquote>
<p>Except it does. And this is the one scene in the movie that stuck in my head as I had seen it before. To help you out, let&rsquo;s un-mirror the mirrored image:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/568/pasted-image-761.jpg" alt="The Algorithm"  loading="lazy" /></p>
<p>And this is the &ldquo;algorithm&rdquo;.</p>
<p>As I said in my previous article, most developers would create a Facemesh-like website adding integer fields in the table of contestants with the count of upvotes, downvotes and they would do something silly such as:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">score</span> <span class="o">=</span> <span class="n">upvotes</span> <span class="o">-</span> <span class="n">downvotes</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Or even sillier:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">score</span> <span class="o">=</span> <span class="n">upvotes</span> <span class="o">/</span> <span class="p">(</span><span class="n">upvotes</span> <span class="o">+</span> <span class="n">downvotes</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And it doesn&rsquo;t work that way, you will get very wrong rankings.</p>
<h3>The Demonstration<span class="hx:absolute hx:-mt-20" id="the-demonstration"></span>
    <a href="#the-demonstration" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>To show you how wrong, I created a simple demonstration project called <a href="https://github.com/akitaonrails/elo_demo"target="_blank" rel="noopener">elo_demo</a> which you can git clone and run yourself.</p>
<p>It will create 2,000 random matches against 10 players. This will be the sorted results if we use the wrong methods of subtracting losses from wins and order through that result:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>   Name        Games  Wins  Losses Points (wins - losses)
 1 Kong          217   117    100     17
 2 Samus         211   110    101      9
 3 Wario         197   102     95      7
 4 Luigi         186    95     91      4
 5 Zelda         160    81     79      2
 6 Pikachu       209   105    104      1
 7 Yoshi         223   112    111      1
 8 Mario         203   101    102     -1
 9 Fox           208    95    113    -18
10 Bowser        186    82    104    -22</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, let&rsquo;s make the 2nd place Samus win 10 times in a row against the 3rd place Wario. This is the new ranking:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>   Name        Games  Wins  Losses Points
 1 Samus         221   120    101     19
 2 Kong          217   117    100     17
 3 Luigi         186    95     91      4
 4 Zelda         160    81     79      2
 5 Pikachu       209   105    104      1
 6 Yoshi         223   112    111      1
 7 Mario         203   101    102     -1
 8 Wario         207   102    105     -3
 9 Fox           208    95    113    -18
10 Bowser        186    82    104    -22</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Sounds fair, Samus jumps to 1st place and Wario goes down to 8th place.</p>
<p>Now, what if we make the weaker 10th place Bowser win 10 times against the current 2nd place Kong?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>   Name        Games  Wins  Losses Points
 1 Samus         221   120    101     19
 2 Kong          227   117    110      7
 3 Luigi         186    95     91      4
 4 Zelda         160    81     79      2
 5 Pikachu       209   105    104      1
 6 Yoshi         223   112    111      1
 7 Mario         203   101    102     -1
 8 Wario         207   102    105     -3
 9 Bowser        196    92    104    -12
10 Fox           208    95    113    -18</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is where you see how <strong>wrong</strong> this method is. Even though he lost 10 times against the weakest player, Kong still reigns supreme at 2nd place. And poor Bowser, in spite of all his hard work and effort, levels up just 1 meager position from 10th to 9th.</p>
<p>This is very frustrating and if it feels unfair, it&rsquo;s because it is. This kind of calculation is <strong>wrong</strong>.</p>
<h3>From Chess to League of Legends<span class="hx:absolute hx:-mt-20" id="from-chess-to-league-of-legends"></span>
    <a href="#from-chess-to-league-of-legends" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If you ever played League of Legends you are probably familiar with something called &ldquo;ELO Boosts&rdquo;.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/570/Screen_Shot_2016-11-01_at_10.19.42.png" alt="Elo Boost"  loading="lazy" /></p>
<p>This is a way to level up your account for money. I&rsquo;d strongly recommend against it if you intend to compete professionally as it&rsquo;s against the rules stipulated by Riot.</p>
<p>Anyway, have you ever wondered why it&rsquo;s called &ldquo;ELO&rdquo;?</p>
<p>This is for Austro-Hungarian professor <strong>Arpad Emmerich Elo</strong>. He is best known for his system of rating chess players. Quoting from Wikipedia:</p>
<blockquote>
  <p>The original chess rating system was developed in 1950 by Kenneth Harkness (&hellip;). By 1960, using the data developed through the Harkness Rating System, Elo developed his own formula which had a <strong>sound statistical basis</strong> and constituted an improvement on the Harkness System. The new rating system was approved and passed at a meeting of the United States Chess Federation in St. Louis in 1960.</p>

</blockquote>
<blockquote>
  <p>In 1970, FIDE, the World Chess Federation, agreed to adopt the Elo Rating System. From then on until the mid-1980s, Elo himself made the rating calculations. At the time, the computational task was relatively easy because fewer than 2000 players were rated by FIDE.</p>

</blockquote>
<p>His system has been refined and evolved to make tournament leaderboards actually fair and competitive. One such evolution is in the form of Microsoft&rsquo;s <a href="https://www.microsoft.com/en-us/research/project/trueskill-ranking-system/"target="_blank" rel="noopener">TrueSkill Ranking System</a> used in all Xbox Live games.</p>
<p>That &ldquo;algorithm&rdquo; that Eduardo Saverin writes on the window of Harvard&rsquo;s dorm room? It&rsquo;s the <strong>ELO Rating System</strong>!!</p>
<p>I don&rsquo;t know if Zuckerberg actually implemented the ELO rating system equations. If he did, it was the <strong>correct</strong> choice. But the whole Eduardo writing the equations on the window probably didn&rsquo;t happen that way as it would be way easier to Google for it :-)</p>
<h3>ELO Rating System Demonstration<span class="hx:absolute hx:-mt-20" id="elo-rating-system-demonstration"></span>
    <a href="#elo-rating-system-demonstration" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>My pet demonstration project also calculates that exact ELO score. The calculations are done by the <a href="https://github.com/iain/elo"target="_blank" rel="noopener">elo</a> rubygem.</p>
<p>The idea is to calculate the probability that one player has to win over the other player. So if a strong player plays against a weak player, he is expected to win, and if this is the outcome, he will not score a lot and the losing player will not fall a lot either. But if the unexpected happens and the strong one loses then it&rsquo;s expected for him to fall down a lot and for the &ldquo;weaker&rdquo; player to jump up a lot.</p>
<p>That will make the tournaments more competitive and make the new players more motivated to play against the strongest and also make the strongest play harder to hold their positions.</p>
<p>From the elo gem documentation, this is how you use it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">kong</span>  <span class="o">=</span> <span class="no">Elo</span><span class="o">::</span><span class="no">Player</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">bowser</span> <span class="o">=</span> <span class="no">Elo</span><span class="o">::</span><span class="no">Player</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:rating</span> <span class="o">=&gt;</span> <span class="mi">1500</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">game1</span> <span class="o">=</span> <span class="n">kong</span><span class="o">.</span><span class="n">wins_from</span><span class="p">(</span><span class="n">bowser</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">game2</span> <span class="o">=</span> <span class="n">kong</span><span class="o">.</span><span class="n">loses_from</span><span class="p">(</span><span class="n">bowser</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">game3</span> <span class="o">=</span> <span class="n">kong</span><span class="o">.</span><span class="n">plays_draw</span><span class="p">(</span><span class="n">bowser</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">game4</span> <span class="o">=</span> <span class="n">kong</span><span class="o">.</span><span class="n">versus</span><span class="p">(</span><span class="n">bowser</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">game4</span><span class="o">.</span><span class="n">winner</span> <span class="o">=</span> <span class="n">bowser</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">game5</span> <span class="o">=</span> <span class="n">kong</span><span class="o">.</span><span class="n">versus</span><span class="p">(</span><span class="n">bowser</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">game5</span><span class="o">.</span><span class="n">loser</span> <span class="o">=</span> <span class="n">bowser</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">game6</span> <span class="o">=</span> <span class="n">kong</span><span class="o">.</span><span class="n">versus</span><span class="p">(</span><span class="n">bowser</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">game6</span><span class="o">.</span><span class="n">draw</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">game7</span> <span class="o">=</span> <span class="n">kong</span><span class="o">.</span><span class="n">versus</span><span class="p">(</span><span class="n">bowser</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">game7</span><span class="o">.</span><span class="n">result</span> <span class="o">=</span> <span class="mi">1</span> <span class="c1"># result is in perspective of kong, so kong wins</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">game8</span> <span class="o">=</span> <span class="n">kong</span><span class="o">.</span><span class="n">versus</span><span class="p">(</span><span class="n">bowser</span><span class="p">,</span> <span class="ss">:result</span> <span class="o">=&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="c1"># bowser wins</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is how you assess the results:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">kong</span><span class="o">.</span><span class="n">rating</span>       <span class="c1"># =&gt; 1080</span>
</span></span><span class="line"><span class="cl"><span class="n">kong</span><span class="o">.</span><span class="n">pro?</span>         <span class="c1"># =&gt; false</span>
</span></span><span class="line"><span class="cl"><span class="n">kong</span><span class="o">.</span><span class="n">starter?</span>     <span class="c1"># =&gt; true</span>
</span></span><span class="line"><span class="cl"><span class="n">kong</span><span class="o">.</span><span class="n">games_played</span> <span class="c1"># =&gt; 8</span>
</span></span><span class="line"><span class="cl"><span class="n">kong</span><span class="o">.</span><span class="n">games</span>        <span class="c1"># =&gt; [ game1, game2, ... game8 ]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The gem has more tuning besides that original algorithm, such as the K-factor to reward new players. Those kinds of tunings are what makes matches more competitive today and how you evolve it to TrueSkill levels, but it&rsquo;s beside the point of this article.</p>
<p>Let&rsquo;s see the wrong ranking again:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>   Name        Games  Wins  Losses Points (wins - losses)
 1 Kong          217   117    100     17
 2 Samus         211   110    101      9
 3 Wario         197   102     95      7
 4 Luigi         186    95     91      4
 5 Zelda         160    81     79      2
 6 Pikachu       209   105    104      1
 7 Yoshi         223   112    111      1
 8 Mario         203   101    102     -1
 9 Fox           208    95    113    -18
10 Bowser        186    82    104    -22</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now let&rsquo;s see the <strong>correct</strong> ranking by calculating the Elo score using the exact same 2,000 matches:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>   Name        Games  Wins  Losses Points  Elo Rating
 1 Pikachu       209   105    104      1         851
 2 Zelda         160    81     79      2         847
 3 Samus         211   110    101      9         842
 4 Luigi         186    95     91      4         841
 5 Wario         197   102     95      7         824
 6 Mario         203   101    102     -1         820
 7 Yoshi         223   112    111      1         803
 8 Kong          217   117    100     17         802
 9 Bowser        186    82    104    -22         785
10 Fox           208    95    113    -18         754</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>See how different it is? In the wrong ranking, Kong is considered the strongest, but in the Elo ranking he is just 8th place. And the reason is that even though he is the one that won most matches (217) he also lost a heck of a lot (117). Someone with fewer wins such as Zelda in 2nd place (160 wins) lost a heck of a lot less (81), which is why she is higher in the ranking.</p>
<p>Now, if we make her win 10 matches in a row against 3rd place Samus, this is the new ranking:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>   Name        Games  Wins  Loses  Points  Elo Rating
 1 Zelda         170    91     79     12         904
 2 Pikachu       209   105    104      1         851
 3 Luigi         186    95     91      4         841
 4 Wario         197   102     95      7         824
 5 Mario         203   101    102     -1         820
 6 Yoshi         223   112    111      1         803
 7 Kong          217   117    100     17         802
 8 Bowser        186    82    104    -22         785
 9 Samus         221   110    111     -1         775
10 Fox           208    95    113    -18         754</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, Zelda jumps up from 2nd to 1st place and Samus falls down from 3rd to 9th. So far so good. But what about the scenario where we make strong 2nd place Pikachu play against a much weaker 10th place Fox McCloud?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>   Name        Games  Wins  Loses  Points  Elo Rating
 1 Zelda         170    91     79     12         904
 2 Luigi         186    95     91      4         841
 3 Fox           218   105    113     -8         829
 4 Wario         197   102     95      7         824
 5 Mario         203   101    102     -1         820
 6 Yoshi         223   112    111      1         803
 7 Kong          217   117    100     17         802
 8 Bowser        186    82    104    -22         785
 9 Samus         221   110    111     -1         775
10 Pikachu       219   105    114     -9         766</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, this is fairness: Pikachu should have won, but losing 10 times in a row against someone considered much weaker makes him fall down from 2nd place all the way to the last place. And noobie Fox, having won 10 times against a much stronger opponent, deserves jumping up all the way to 3rd place.</p>
<p>This is the kind of dynamic that can make matches and games competitive, which is exactly why every online leaderboard and professional tournament uses those kinds of algorithms.</p>
<p>And it all began in chess, using math that has been known since the late 40&rsquo;s!!</p>
<blockquote>
  <p>This is the point of this post and my previous one: <strong>the math is not new</strong>.</p>

</blockquote>
<p>Developers waste a great amount of time in stupid pissing contests over which language or tool is &ldquo;shinier&rdquo;, but they ignore the proper math and deliver wrong results. But the math has been around for decades, in some cases more than a full century already!</p>
<p>We should strive to earn the title of Computer <em>Scientists</em>. There is a lot of <strong>science</strong> lacking from computing nowadays. Pissing contests do not make a programmer any good.</p>
]]></content:encoded><category>algorithms-and-data-structures</category><category>gaming</category></item><item><title>Ruby on Rails implementation of a (proper) Ranking/Popularity system</title><link>https://akitaonrails.github.io/en/2016/10/31/ruby-on-rails-implementation-of-a-proper-ranking-popularity-system/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/10/31/ruby-on-rails-implementation-of-a-proper-ranking-popularity-system/</guid><pubDate>Mon, 31 Oct 2016 16:51:00 -0200</pubDate><description>&lt;p&gt;I was reading a blog post published recently titled &lt;a href="http://naturaily.com/blog/post/ruby-on-rails-implementation-of-a-ranking-system-using-postgresql-window-functions"target="_blank" rel="noopener"&gt;&amp;ldquo;Ruby on Rails implementation of a ranking system using PostgreSQL window functions&amp;rdquo;&lt;/a&gt; and to be fair the purpose of the post was to introduce &lt;a href="https://www.postgresql.org/docs/8.4/static/functions-window.html"target="_blank" rel="noopener"&gt;PostgreSQL&amp;rsquo;s &amp;ldquo;ntile&amp;rdquo; window function&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But in the process, the author made the same mistake I&amp;rsquo;ve seen time and time again.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s assume you have a project with resources that you want to list by &amp;ldquo;popularity&amp;rdquo;. It can be a Reddit-like site where people like or dislike posts or comments. It can be an e-commerce where people like or dislike products.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I was reading a blog post published recently titled <a href="http://naturaily.com/blog/post/ruby-on-rails-implementation-of-a-ranking-system-using-postgresql-window-functions"target="_blank" rel="noopener">&ldquo;Ruby on Rails implementation of a ranking system using PostgreSQL window functions&rdquo;</a> and to be fair the purpose of the post was to introduce <a href="https://www.postgresql.org/docs/8.4/static/functions-window.html"target="_blank" rel="noopener">PostgreSQL&rsquo;s &ldquo;ntile&rdquo; window function</a>.</p>
<p>But in the process, the author made the same mistake I&rsquo;ve seen time and time again.</p>
<p>Let&rsquo;s assume you have a project with resources that you want to list by &ldquo;popularity&rdquo;. It can be a Reddit-like site where people like or dislike posts or comments. It can be an e-commerce where people like or dislike products.</p>
<p>It can be anything where people like or dislike something.</p>
<p>The biggest error people make is to consider a simple score like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">popularity</span> <span class="o">=</span> <span class="n">positive_votes</span> <span class="o">-</span> <span class="n">negative_votes</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>There is an old article titled <a href="http://www.evanmiller.org/how-not-to-sort-by-average-rating.html"target="_blank" rel="noopener">&ldquo;How Not To Sort By Average Rating&rdquo;</a> and I quote:</p>
<blockquote>
  <p>&ldquo;<em>Why it is wrong:</em> Suppose one item has 600 positive ratings and 400 negative ratings: 60% positive. Suppose item two has 5,500 positive ratings and 4,500 negative ratings: 55% positive. This algorithm puts item two (score = 1000, but only 55% positive) above item one (score = 200, and 60% positive). WRONG.&rdquo;</p>

</blockquote>
<p>Then you may think, I know how to fix it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Score</span> <span class="o">=</span> <span class="n">average_rating</span> <span class="o">=</span> <span class="n">positive_votes</span> <span class="o">/</span> <span class="n">total_votes</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, this is wrong, and again I quote:</p>
<blockquote>
  <p>&ldquo;<em>Why it is wrong:</em> Average rating works fine if you always have a ton of ratings, but suppose item 1 has 2 positive ratings and 0 negative ratings. Suppose item 2 has 100 positive ratings and 1 negative rating. This algorithm puts item two (tons of positive ratings) below item one (very few positive ratings). WRONG.&rdquo;</p>

</blockquote>
<h3>Correct Solution: Lower Bound of Wilson Score Confidence Interval for a Bernoulli<span class="hx:absolute hx:-mt-20" id="correct-solution-lower-bound-of-wilson-score-confidence-interval-for-a-bernoulli"></span>
    <a href="#correct-solution-lower-bound-of-wilson-score-confidence-interval-for-a-bernoulli" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>And I quote again:</p>
<blockquote>
  <p>&ldquo;<em>Say what:</em> We need to balance the proportion of positive ratings with the uncertainty of a small number of observations. Fortunately, the math for this was worked out in 1927 by Edwin B. Wilson. What we want to ask is: Given the ratings I have, there is a 95% chance that the &ldquo;real&rdquo; fraction of positive ratings is at least what? Wilson gives the answer. Considering only positive and negative ratings (i.e. not a 5-star scale), the lower bound on the proportion of positive ratings is given by:&rdquo;</p>

</blockquote>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/565/rating-equation.png" alt="Lower Bound Bernoulli equation"  loading="lazy" /></p>
<p>I recommend you read the <a href="http://www.evanmiller.org/how-not-to-sort-by-average-rating.html"target="_blank" rel="noopener">original article</a> but let&rsquo;s cut to the chase. If we follow the original post I linked in the beginning, I have a simple blog post Rails app, but instead of a <code>visits_count</code> field I need to add a <code>positive:integer</code> and <code>negative:integer</code> fields and user interface to post votes.</p>
<p>And I will replace the <code>PostWithPopularityQuery</code> class with the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PostWithPopularityQuery</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">call</span>
</span></span><span class="line"><span class="cl">    <span class="no">Post</span><span class="o">.</span><span class="n">find_by_sql</span> <span class="o">[</span><span class="s1">&#39;SELECT id, title, body, positive, negative,
</span></span></span><span class="line"><span class="cl"><span class="s1">        ((positive + 1.9208) / (positive + negative) -
</span></span></span><span class="line"><span class="cl"><span class="s1">        1.96 * SQRT((positive * negative) / (positive + negative) + 0.9604) /
</span></span></span><span class="line"><span class="cl"><span class="s1">        (positive + negative)) / (1 + 3.8416 / (positive + negative))
</span></span></span><span class="line"><span class="cl"><span class="s1">        AS ci_lower_bound
</span></span></span><span class="line"><span class="cl"><span class="s1">      FROM posts 
</span></span></span><span class="line"><span class="cl"><span class="s1">      WHERE positive + negative &gt; 0
</span></span></span><span class="line"><span class="cl"><span class="s1">      ORDER BY ci_lower_bound DESC&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is what I expect to see in a simple scaffold <code>index.html.erb</code>:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/564/Screen_Shot_2016-10-31_at_16.43.50.png" alt="Post index page"  loading="lazy" /></p>
<p>I would even go as far as recommending the <code>ci_lower_bound</code> to be a float field in the table and to have an asynchronous ActiveJob to update it in some larger interval of time (every 5 minutes, for example) and then the <code>PostsController#index</code> action would perform a straightforward <code>SELECT</code> query ordering directly against a real indexed field <code>ci_lower_bound DESC</code> without performing the calculations on <strong>every</strong> query.</p>
<p>Now <strong>THIS</strong> is the correct way to implement a simple, naive popularity ranking system that actually works correctly.</p>
<p>And this is not the only way to do it. There are dozens of good discussions of algorithms online. Every service that depends on content popularity has been refining algorithms like this for years. Facebook used to have an algorithm called &ldquo;EdgeRank&rdquo; which relied on variables such as Affinity, Weight, Time Decay, and it seems to have evolved so much that it now calculates popularity against more than <a href="http://marketingland.com/edgerank-is-dead-facebooks-news-feed-algorithm-now-has-close-to-100k-weight-factors-55908"target="_blank" rel="noopener">100 thousand variables</a>!!</p>
<p>But regardless of the online service, I can assure you that <strong>none</strong> sorts by simple visits count or simple average votes count. That would be downright wrong.</p>
]]></content:encoded><category>algorithms-and-data-structures</category><category>rails</category><category>ruby</category></item><item><title>Trying to match C-based Fast Blank with Crystal</title><link>https://akitaonrails.github.io/en/2016/07/06/trying-to-match-c-based-fast-blank-with-crystal/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/07/06/trying-to-match-c-based-fast-blank-with-crystal/</guid><pubDate>Wed, 06 Jul 2016 17:10:00 -0300</pubDate><description>&lt;p&gt;In my eyes, Crystal may become the ideal solution to make our beloved Ruby gems faster. Up until now we have been using C-based extensions to accelerate CPU-bound code in Ruby. Nokogiri, for example, is a wrapper to provide a nice API on top of libxml, which is a huge library in C.&lt;/p&gt;
&lt;p&gt;But there are many opportunities to accelerate Rails applications as well. For example, we just saw the release of the &lt;a href="https://github.com/danielpclark/faster_path"target="_blank" rel="noopener"&gt;gem &amp;ldquo;faster_path&amp;rdquo;&lt;/a&gt;, this time written in Rust and bridged through FFI (Foreign Function Interface). The author&amp;rsquo;s claim is that Sprockets has to compute lots of paths and making this library, natively compiled and optimized with Rust, added a huge improvement in the asset pipeline task.&lt;/p&gt;</description><content:encoded><![CDATA[<p>In my eyes, Crystal may become the ideal solution to make our beloved Ruby gems faster. Up until now we have been using C-based extensions to accelerate CPU-bound code in Ruby. Nokogiri, for example, is a wrapper to provide a nice API on top of libxml, which is a huge library in C.</p>
<p>But there are many opportunities to accelerate Rails applications as well. For example, we just saw the release of the <a href="https://github.com/danielpclark/faster_path"target="_blank" rel="noopener">gem &ldquo;faster_path&rdquo;</a>, this time written in Rust and bridged through FFI (Foreign Function Interface). The author&rsquo;s claim is that Sprockets has to compute lots of paths and making this library, natively compiled and optimized with Rust, added a huge improvement in the asset pipeline task.</p>
<p>Sam Saffron, from Discourse, also built a very small gem called <a href="https://github.com/SamSaffron/fast_blank"target="_blank" rel="noopener">&ldquo;fast_blank&rdquo;</a> which is a tiny library written in C that reimplements ActiveSupport&rsquo;s <code>String#blank?</code> method to be up to 9x faster. Because Rails digests volumes of strings, checking if they are blank every time, this adds some performance (depends on your app, of course).</p>
<p>The Holy Grail to native-level performance is to be able to write close-to-Ruby code instead of having to hack low-level C or having the high learning curve of a language such as Rust. More than that, I&rsquo;d like to avoid having to use FFI. I am not an expert in FFI but I remember understanding that it adds overhead to make the bindings.</p>
<p>By the way, it&rsquo;s important to disclose right now: I am not a C expert by any means of the imagination, far from that. So I have very little experience dealing with hardcore C development. Which is again, why this possibility of writing in Crystal is even more appealing to me. So if you are a C expert and you spot something silly I am saying about it, please let me know in the comments section below.</p>
<p>My exercise is to rewrite the C-based Fast Blank gem in Crystal, add it to the same Gem to compile under Crystal if it&rsquo;s available or fall back to C, and make the specs pass so it&rsquo;s a seamless transition for the user.</p>
<p>To achieve that I had to:</p>
<ul>
<li>Extend the Gem&rsquo;s extconf.rb to generate different Makefiles (for C and Crystal) that are able to compile under OS X or Linux (Ubuntu at least) - OK</li>
<li>Make the specs pass in the Crystal version - Almost (it&rsquo;s ok for all intents and purposes but an edge case)</li>
<li>Make the performance be faster than Ruby and close to C - Not so much yet (under OS X the performance is quite good, but under Ubuntu it doesn&rsquo;t scale so well for large strings)</li>
</ul>
<p>You can check out the results so far on <a href="https://github.com/akitaonrails/fast_blank/tree/crystal_version"target="_blank" rel="noopener">my fork over Github</a> and follow the <a href="https://github.com/SamSaffron/fast_blank/pull/20"target="_blank" rel="noopener">Pull Request discussion as well</a>.</p>
<h3>Comparing C and Crystal<span class="hx:absolute hx:-mt-20" id="comparing-c-and-crystal"></span>
    <a href="#comparing-c-and-crystal" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Just to get us started, let&rsquo;s check out a snippet of Sam&rsquo;s original C version:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">VALUE</span>
</span></span><span class="line"><span class="cl"><span class="nf">rb_str_blank</span><span class="p">(</span><span class="n">VALUE</span> <span class="n">str</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">rb_encoding</span> <span class="o">*</span><span class="n">enc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">char</span> <span class="o">*</span><span class="n">s</span><span class="p">,</span> <span class="o">*</span><span class="n">e</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">enc</span> <span class="o">=</span> <span class="nf">STR_ENC_GET</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">s</span> <span class="o">=</span> <span class="nf">RSTRING_PTR</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">s</span> <span class="o">||</span> <span class="nf">RSTRING_LEN</span><span class="p">(</span><span class="n">str</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">Qtrue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">e</span> <span class="o">=</span> <span class="nf">RSTRING_END</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="p">(</span><span class="n">s</span> <span class="o">&lt;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">n</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">cc</span> <span class="o">=</span> <span class="nf">rb_enc_codepoint_len</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n</span><span class="p">,</span> <span class="n">enc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">rb_isspace</span><span class="p">(</span><span class="n">cc</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">cc</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">Qfalse</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">s</span> <span class="o">+=</span> <span class="n">n</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">Qtrue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Yeah, quite scary, I know. Now let&rsquo;s see the Crystal version:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">struct</span> <span class="no">Char</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># same way C Ruby implements it</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">is_blank</span>
</span></span><span class="line"><span class="cl">    <span class="nb">self</span> <span class="o">==</span> <span class="s1">&#39; &#39;</span> <span class="o">||</span> <span class="p">(</span><span class="s1">&#39;\t&#39;</span> <span class="o">&lt;=</span> <span class="nb">self</span> <span class="o">&lt;=</span> <span class="s1">&#39;\r&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">String</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">blank?</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kp">true</span> <span class="k">if</span> <span class="nb">self</span><span class="o">.</span><span class="n">nil?</span> <span class="o">||</span> <span class="nb">self</span><span class="o">.</span><span class="n">size</span> <span class="o">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="n">each_char</span> <span class="p">{</span> <span class="o">|</span><span class="n">char</span><span class="o">|</span> <span class="k">return</span> <span class="kp">false</span> <span class="k">if</span> <span class="o">!</span><span class="n">char</span><span class="o">.</span><span class="n">is_blank</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Hell yeah! If you&rsquo;re a rubyist I bet you can understand 100% of the snippet above. It&rsquo;s not &ldquo;exactly&rdquo; the same thing (as the specs are not fully passing yet), but it&rsquo;s damn close.</p>
<h3>The Quest for a Makefile to Crystal<span class="hx:absolute hx:-mt-20" id="the-quest-for-a-makefile-to-crystal"></span>
    <a href="#the-quest-for-a-makefile-to-crystal" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I&rsquo;ve researched many <a href="https://github.com/akitaonrails/ruby_ext_in_crystal_math"target="_blank" rel="noopener">experimental Github repos</a> and Gists out there. But I didn&rsquo;t find one that has it all so I decided to tweak what I found until I got to this version:</p>
<p>Obs: again, I am not a C expert. If you have experience with Makefiles I know this one can be refactored to something nicer than this. Let me know in the comments below.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="n">ifeq</span> <span class="s">&#34;$(PLATFORM)&#34;</span> <span class="s">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nl">PLATFORM</span> <span class="p">:</span><span class="o">=</span> <span class="err">$</span><span class="p">(</span><span class="n">shell</span> <span class="n">uname</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">endif</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ifeq</span> <span class="s">&#34;$(PLATFORM)&#34;</span> <span class="s">&#34;Linux&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">UNAME</span> <span class="o">=</span> <span class="s">&#34;$(shell llvm-config --host-target)&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">CRYSTAL_BIN</span> <span class="o">=</span> <span class="err">$</span><span class="p">(</span><span class="n">shell</span> <span class="n">readlink</span> <span class="o">-</span><span class="n">f</span> <span class="err">`</span><span class="n">which</span> <span class="n">crystal</span><span class="err">`</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">LIBRARY_PATH</span> <span class="o">=</span> <span class="err">$</span><span class="p">(</span><span class="n">shell</span> <span class="n">dirname</span> <span class="err">$</span><span class="p">(</span><span class="n">CRYSTAL_BIN</span><span class="p">))</span><span class="o">/</span><span class="p">..</span><span class="o">/</span><span class="n">embedded</span><span class="o">/</span><span class="n">lib</span>
</span></span><span class="line"><span class="cl"><span class="n">LIBCRYSTAL</span> <span class="o">=</span> <span class="err">$</span><span class="p">(</span><span class="n">shell</span> <span class="n">dirname</span> <span class="err">$</span><span class="p">(</span><span class="n">CRYSTAL_BIN</span><span class="p">)</span> <span class="p">)</span><span class="o">/</span><span class="p">..</span><span class="o">/</span><span class="n">src</span><span class="o">/</span><span class="n">ext</span><span class="o">/</span><span class="n">libcrystal</span><span class="p">.</span><span class="n">a</span>
</span></span><span class="line"><span class="cl"><span class="n">LIBRUBY</span> <span class="o">=</span> <span class="err">$</span><span class="p">(</span><span class="n">shell</span> <span class="n">ruby</span> <span class="o">-</span><span class="n">e</span> <span class="s">&#34;puts RbConfig::CONFIG[&#39;libdir&#39;]&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">LIBS</span> <span class="o">=</span> <span class="o">-</span><span class="n">lpcre</span> <span class="o">-</span><span class="n">lgc</span> <span class="o">-</span><span class="n">lpthread</span> <span class="o">-</span><span class="n">levent</span> <span class="o">-</span><span class="n">lrt</span> <span class="o">-</span><span class="n">ldl</span>
</span></span><span class="line"><span class="cl"><span class="n">LDFLAGS</span> <span class="o">=</span> <span class="o">-</span><span class="n">rdynamic</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nl">install</span><span class="p">:</span> <span class="n">all</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nl">all</span><span class="p">:</span> <span class="n">fast_blank</span><span class="p">.</span><span class="n">so</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">fast_blank</span><span class="p">.</span><span class="nl">so</span><span class="p">:</span> <span class="n">fast_blank</span><span class="p">.</span><span class="n">o</span>
</span></span><span class="line"><span class="cl">  <span class="err">$</span><span class="p">(</span><span class="n">CC</span><span class="p">)</span> <span class="o">-</span><span class="n">shared</span> <span class="err">$</span><span class="o">^</span> <span class="o">-</span><span class="n">o</span> <span class="err">$@</span> <span class="err">$</span><span class="p">(</span><span class="n">LIBCRYSTAL</span><span class="p">)</span> <span class="err">$</span><span class="p">(</span><span class="n">LDFLAGS</span><span class="p">)</span> <span class="err">$</span><span class="p">(</span><span class="n">LIBS</span><span class="p">)</span> <span class="o">-</span><span class="n">L</span><span class="err">$</span><span class="p">(</span><span class="n">LIBRARY_PATH</span><span class="p">)</span> <span class="o">-</span><span class="n">L</span><span class="err">$</span><span class="p">(</span><span class="n">LIBRUBY</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">fast_blank</span><span class="p">.</span><span class="nl">o</span><span class="p">:</span> <span class="p">..</span><span class="o">/</span><span class="p">..</span><span class="o">/</span><span class="p">..</span><span class="o">/</span><span class="p">..</span><span class="o">/</span><span class="n">ext</span><span class="o">/</span><span class="n">src</span><span class="o">/</span><span class="n">fast_blank</span><span class="p">.</span><span class="n">cr</span>
</span></span><span class="line"><span class="cl">  <span class="n">crystal</span> <span class="n">build</span> <span class="o">--</span><span class="n">cross</span><span class="o">-</span><span class="n">compile</span> <span class="o">--</span><span class="n">release</span> <span class="o">--</span><span class="n">target</span> <span class="err">$</span><span class="p">(</span><span class="n">UNAME</span><span class="p">)</span> <span class="err">$</span><span class="o">&lt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nl">PHONY</span><span class="p">:</span> <span class="n">clean</span>
</span></span><span class="line"><span class="cl"><span class="nl">clean</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">rm</span> <span class="o">-</span><span class="n">f</span> <span class="n">bc_flags</span>
</span></span><span class="line"><span class="cl">  <span class="n">rm</span> <span class="o">-</span><span class="n">f</span> <span class="o">*</span><span class="p">.</span><span class="n">o</span>
</span></span><span class="line"><span class="cl">  <span class="n">rm</span> <span class="o">-</span><span class="n">f</span> <span class="o">*</span><span class="p">.</span><span class="n">so</span>
</span></span><span class="line"><span class="cl"><span class="n">endif</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ifeq</span> <span class="s">&#34;$(PLATFORM)&#34;</span> <span class="s">&#34;Darwin&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">CRYSTAL_FLAGS</span> <span class="o">=</span> <span class="o">-</span><span class="n">dynamic</span> <span class="o">-</span><span class="n">bundle</span> <span class="o">-</span><span class="n">Wl</span><span class="p">,</span><span class="o">-</span><span class="n">undefined</span><span class="p">,</span><span class="n">dynamic_lookup</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nl">install</span><span class="p">:</span> <span class="n">all</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nl">all</span><span class="p">:</span> <span class="n">fast_blank</span><span class="p">.</span><span class="n">bundle</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">fast_blank</span><span class="p">.</span><span class="nl">bundle</span><span class="p">:</span> <span class="p">..</span><span class="o">/</span><span class="p">..</span><span class="o">/</span><span class="p">..</span><span class="o">/</span><span class="p">..</span><span class="o">/</span><span class="n">ext</span><span class="o">/</span><span class="n">src</span><span class="o">/</span><span class="n">fast_blank</span><span class="p">.</span><span class="n">cr</span>
</span></span><span class="line"><span class="cl">  <span class="n">crystal</span> <span class="err">$</span><span class="o">^</span> <span class="o">--</span><span class="n">release</span> <span class="o">--</span><span class="n">link</span><span class="o">-</span><span class="n">flags</span> <span class="s">&#34;$(CRYSTAL_FLAGS)&#34;</span> <span class="o">-</span><span class="n">o</span> <span class="err">$@</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nl">clean</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">rm</span> <span class="o">-</span><span class="n">f</span> <span class="o">*</span><span class="p">.</span><span class="n">log</span>
</span></span><span class="line"><span class="cl">  <span class="n">rm</span> <span class="o">-</span><span class="n">f</span> <span class="o">*</span><span class="p">.</span><span class="n">o</span>
</span></span><span class="line"><span class="cl">  <span class="n">rm</span> <span class="o">-</span><span class="n">f</span> <span class="o">*</span><span class="p">.</span><span class="n">bundle</span>
</span></span><span class="line"><span class="cl"><span class="n">endif</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Most people using Crystal are on OS X, including the creators of Crystal. LLVM is under Apple&rsquo;s umbrella and their entire ecosystem relies heavily on LLVM. They spent many years migrating the C front-end first, then the C back-end away from GNU&rsquo;s standard GCC to Clang. And they were able to make both Objective-C and Swift compile down to LLVM&rsquo;s IR and that&rsquo;s how both can interact back and forth natively.</p>
<p>Then, they improved the ARM backend support and that&rsquo;s how they can have an entire iOS &ldquo;Simulator&rdquo; (not a dog slow emulator like Android) where the iOS apps are natively compiled to run over Intel&rsquo;s x86_64 processor while in development and then quickly recompile to ARM when ready to package to the App Store.</p>
<p>This way you can run natively, test quickly, without the slowness of an emulated environment. By the way, I will say this once: Google&rsquo;s biggest mistake is not supporting LLVM as they should, and reinventing the wheel instead. If they had, Go could already be used to implement for Android and Chromebooks as well as x86 based servers and they could put away all the Java/Oracle debacle.</p>
<p>But I digress.</p>
<p>In OS X you can pass a &ldquo;<code>-bundle</code>&rdquo; link-flag to crystal and it will probably use clang underneath to generate the binary bundle.</p>
<p>On Ubuntu crystal just compiles down to an object file (.o) and you have to manually invoke GCC with the &ldquo;<code>-shared</code>&rdquo; option to create a shared-object. To do that we have to use the <a href="https://crystal-lang.org/docs/syntax_and_semantics/cross-compilation.html"target="_blank" rel="noopener">&quot;&ndash;cross-compile&quot;</a> and pass an LLVM target triplet so it generates the .o (this requires the <code>llvm-config</code> tool).</p>
<p>Shared Libraries (.so) and Loadable Modules (.bundle) are different beasts, check <a href="http://docstore.mik.ua/orelly/unix3/mac/ch05_03.htm"target="_blank" rel="noopener">this documentation</a> out for more details.</p>
<p>Keep in mind that benchmarking binaries built with different compilers can make a difference. I am not an expert but out of pure anecdote I believe Ruby under RVM on OS X is compiled using OS X&rsquo;s default Clang. On Ubuntu it&rsquo;s compiled under GCC. This seems to make Ruby on OS X &ldquo;so slightly&rdquo; inefficient in synthetic benchmarks.</p>
<p>On the other hand, Crystal binaries linked with GCC feel &ldquo;so slightly&rdquo; inefficient on Ubuntu, while Ruby on Ubuntu feels a bit faster, having been compiled and linked with GCC.</p>
<p>So when we compare Fast Blank/OS X/bit faster with Ruby/OS X/slower against Fast Blank/Ubuntu/bit slower with Ruby/Ubuntu/bit faster, it seems to give a wider advantage to the OS X benchmark comparison against the Ubuntu benchmark, even though individual computation times are not so far from each other.</p>
<p>I will come back to this point in the benchmarks section.</p>
<p>Finally, every time you have a rubygem with a native extension, you will find this bit in their gemspec files:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Gem</span><span class="o">::</span><span class="no">Specification</span><span class="o">.</span><span class="n">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">s</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;fast_blank&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">s</span><span class="o">.</span><span class="n">extensions</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;ext/fast_blank/extconf.rb&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>When the gem is installed through <code>gem install</code> or <code>bundle install</code> it will run this script to generate a proper <code>Makefile</code>. In a pure C extension it will use the built-in &ldquo;mkmf&rdquo; library to generate it.</p>
<p>In our case, if we have Crystal installed, we want to use the Crystal version, so I tweaked the <code>extconf.rb</code> to be like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;mkmf&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;VERSION&#39;</span><span class="o">]</span> <span class="o">!=</span> <span class="s2">&#34;C&#34;</span> <span class="o">&amp;&amp;</span> <span class="n">find_executable</span><span class="p">(</span><span class="s1">&#39;crystal&#39;</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">find_executable</span><span class="p">(</span><span class="s1">&#39;llvm-config&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Very dirty patching</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create_makefile</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="n">srcprefix</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">mfile</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;Makefile&#34;</span><span class="p">,</span> <span class="s2">&#34;wb&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cr_makefile</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">),</span> <span class="s2">&#34;../src/Makefile&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">mfile</span><span class="o">.</span><span class="n">print</span> <span class="no">File</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">cr_makefile</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">ensure</span>
</span></span><span class="line"><span class="cl">    <span class="n">mfile</span><span class="o">.</span><span class="n">close</span> <span class="k">if</span> <span class="n">mfile</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Crystal version of the Makefile copied&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">create_makefile</span> <span class="s1">&#39;fast_blank&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So, if it finds <code>crystal</code> and <code>llvm-config</code> (which in OS X you have to add the proper path like this: <code>export PATH=$(brew --prefix llvm)/bin:$PATH</code>).</p>
<p>The <code>Rakefile</code> in this project declares the standard <code>:compile</code> task as the first one to run, and it will execute the <code>extconf.rb</code>, which will generate the proper <code>Makefile</code> and run the <code>make</code> command to compile and link the proper library in the proper <code>lib/</code> path.</p>
<p>So we will end up with <code>lib/fast_blank.bundle</code> on OS X and <code>lib/fast_blank.so</code> on Ubuntu. From there we can just have <code>require &quot;fast_blank&quot;</code> from any Ruby file in the gem and it will have access to the publicly exported C function mappings from the Crystal library.</p>
<h3>Mapping C-Ruby to Crystal<span class="hx:absolute hx:-mt-20" id="mapping-c-ruby-to-crystal"></span>
    <a href="#mapping-c-ruby-to-crystal" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now, any direct C extension - without FFI, fiddle or other &ldquo;bridges&rdquo; - will ALWAYS have a much better advantage.</p>
<p>The reason is that you literally have to &ldquo;copy&rdquo; data from C-Ruby to Crystal/Rust/Go or whatever other language you&rsquo;re binding. While with a C-based extension you can operate directly in the memory space with the data without having to move it or copy it away.</p>
<p>For example. First, you have to bind the C functions from C-Ruby to Crystal. And we accomplish that with <a href="https://github.com/phoffer/crystalized_ruby/blob/master/src/lib_ruby.cr"target="_blank" rel="noopener">Paul Hoffer&rsquo;s Crystalized Ruby</a> mappings. It&rsquo;s an experimental repository that I helped a bit to clean up in order for him to later extract this mapping library into its own Shard (shards are the same as gems for Crystal). For now I had to simply copy the file over to my Fast Blank.</p>
<p>Some of the relevant bits are like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">lib</span> <span class="no">LibRuby</span>
</span></span><span class="line"><span class="cl">  <span class="n">type</span> <span class="no">VALUE</span> <span class="o">=</span> <span class="no">Void</span><span class="o">*</span>
</span></span><span class="line"><span class="cl">  <span class="n">type</span> <span class="no">METHOD_FUNC</span> <span class="o">=</span> <span class="no">VALUE</span> <span class="o">-&gt;</span> <span class="no">VALUE</span>
</span></span><span class="line"><span class="cl">  <span class="n">type</span> <span class="no">ID</span> <span class="o">=</span> <span class="no">Void</span><span class="o">*</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># strings</span>
</span></span><span class="line"><span class="cl">  <span class="n">fun</span> <span class="n">rb_str_to_str</span><span class="p">(</span><span class="n">value</span> <span class="p">:</span> <span class="no">VALUE</span><span class="p">)</span> <span class="p">:</span> <span class="no">VALUE</span>
</span></span><span class="line"><span class="cl">  <span class="n">fun</span> <span class="n">rb_string_value_cstr</span><span class="p">(</span><span class="n">value_ptr</span> <span class="p">:</span> <span class="no">VALUE</span><span class="o">*</span><span class="p">)</span> <span class="p">:</span> <span class="no">UInt8</span><span class="o">*</span>
</span></span><span class="line"><span class="cl">  <span class="n">fun</span> <span class="n">rb_str_new_cstr</span><span class="p">(</span><span class="n">str</span> <span class="p">:</span> <span class="no">UInt8</span><span class="o">*</span><span class="p">)</span> <span class="p">:</span> <span class="no">VALUE</span>
</span></span><span class="line"><span class="cl">  <span class="n">fun</span> <span class="n">rb_utf8_encoding</span><span class="p">()</span> <span class="p">:</span> <span class="no">VALUE</span>
</span></span><span class="line"><span class="cl">  <span class="n">fun</span> <span class="n">rb_enc_str_new_cstr</span><span class="p">(</span><span class="n">str</span> <span class="p">:</span> <span class="no">UInt8</span><span class="o">*</span><span class="p">,</span> <span class="n">enc</span> <span class="p">:</span> <span class="no">VALUE</span><span class="p">)</span> <span class="p">:</span> <span class="no">VALUE</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># exception handling</span>
</span></span><span class="line"><span class="cl">  <span class="n">fun</span> <span class="n">rb_rescue</span><span class="p">(</span><span class="n">func</span> <span class="p">:</span> <span class="no">VALUE</span> <span class="o">-&gt;</span> <span class="no">UInt8</span><span class="o">*</span><span class="p">,</span> <span class="n">args</span> <span class="p">:</span> <span class="no">VALUE</span><span class="p">,</span> <span class="ss">callback</span><span class="p">:</span> <span class="no">VALUE</span> <span class="o">-&gt;</span> <span class="no">UInt8</span><span class="o">*</span><span class="p">,</span> <span class="ss">value</span><span class="p">:</span> <span class="no">VALUE</span><span class="p">)</span> <span class="p">:</span> <span class="no">UInt8</span><span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">String</span>
</span></span><span class="line"><span class="cl">  <span class="no">RUBY_UTF</span> <span class="o">=</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_utf8_encoding</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">to_ruby</span>
</span></span><span class="line"><span class="cl">    <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_enc_str_new_cstr</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="no">RUBY_UTF</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">from_ruby</span><span class="p">(</span><span class="n">str</span> <span class="p">:</span> <span class="no">LibRuby</span><span class="o">::</span><span class="no">VALUE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">c_str</span> <span class="o">=</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_rescue</span><span class="p">(</span><span class="o">-&gt;</span><span class="nb">String</span><span class="o">.</span><span class="n">cr_str_from_rb_cstr</span><span class="p">,</span> <span class="n">str</span><span class="p">,</span> <span class="o">-&gt;</span><span class="nb">String</span><span class="o">.</span><span class="n">return_empty_string</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="n">to_ruby</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># FIXME there is still an unhandled problem: then we receive \u0000 from Ruby it raises &#34;string contains null bytes&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># so we catch it with rb_rescue, but then we can&#39;t generate a Pointer(UInt8) that represents the unicode 0, instead we return a plain blank string</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># but then the specs fail</span>
</span></span><span class="line"><span class="cl">    <span class="kp">new</span><span class="p">(</span><span class="n">c_str</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">ensure</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">cr_str_from_rb_cstr</span><span class="p">(</span><span class="n">str</span> <span class="p">:</span> <span class="no">LibRuby</span><span class="o">::</span><span class="no">VALUE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">rb_str</span> <span class="o">=</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_str_to_str</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">c_str</span>  <span class="o">=</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_string_value_cstr</span><span class="p">(</span><span class="n">pointerof</span><span class="p">(</span><span class="n">rb_str</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">return_empty_string</span><span class="p">(</span><span class="n">arg</span> <span class="p">:</span> <span class="no">LibRuby</span><span class="o">::</span><span class="no">VALUE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span><span class="n">_u8</span>
</span></span><span class="line"><span class="cl">    <span class="n">pointerof</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then I can use these mappings and helpers to build a &ldquo;Wrapper&rdquo; class in Crystal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;./lib_ruby&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;./string_extension&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">StringExtensionWrapper</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">blank?</span><span class="p">(</span><span class="nb">self</span> <span class="p">:</span> <span class="no">LibRuby</span><span class="o">::</span><span class="no">VALUE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kp">true</span><span class="o">.</span><span class="n">to_ruby</span> <span class="k">if</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_str_length</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="n">str</span> <span class="o">=</span> <span class="nb">String</span><span class="o">.</span><span class="n">from_ruby</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">str</span><span class="o">.</span><span class="n">blank?</span><span class="o">.</span><span class="n">to_ruby</span>
</span></span><span class="line"><span class="cl">  <span class="k">rescue</span>
</span></span><span class="line"><span class="cl">    <span class="kp">true</span><span class="o">.</span><span class="n">to_ruby</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">blank_as?</span><span class="p">(</span><span class="nb">self</span> <span class="p">:</span> <span class="no">LibRuby</span><span class="o">::</span><span class="no">VALUE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kp">true</span><span class="o">.</span><span class="n">to_ruby</span> <span class="k">if</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_str_length</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="n">str</span> <span class="o">=</span> <span class="nb">String</span><span class="o">.</span><span class="n">from_ruby</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">str</span><span class="o">.</span><span class="n">blank_as?</span><span class="o">.</span><span class="n">to_ruby</span>
</span></span><span class="line"><span class="cl">  <span class="k">rescue</span>
</span></span><span class="line"><span class="cl">    <span class="kp">true</span><span class="o">.</span><span class="n">to_ruby</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">crystal_value</span><span class="p">(</span><span class="nb">self</span> <span class="p">:</span> <span class="no">LibRuby</span><span class="o">::</span><span class="no">VALUE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">str</span> <span class="o">=</span> <span class="nb">String</span><span class="o">.</span><span class="n">from_ruby</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">str</span><span class="o">.</span><span class="n">to_ruby</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this &ldquo;Wrapper&rdquo; depends on the &ldquo;pure&rdquo; Crystal library itself like with the snippets for the Char struct and String class extensions I showed in the first section of the article above.</p>
<p>Finally, I have a main &ldquo;fast_blank.cr&rdquo; file that externs those Wrapper functions so C-Ruby can see them as plain String methods:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;./string_extension_wrapper.cr&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">fun</span> <span class="n">init</span> <span class="o">=</span> <span class="no">Init_fast_blank</span>
</span></span><span class="line"><span class="cl">  <span class="no">GC</span><span class="o">.</span><span class="n">init</span>
</span></span><span class="line"><span class="cl">  <span class="no">LibCrystalMain</span><span class="o">.</span><span class="n">__crystal_main</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="no">Pointer</span><span class="p">(</span><span class="no">Pointer</span><span class="p">(</span><span class="no">UInt8</span><span class="p">))</span><span class="o">.</span><span class="n">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">string</span> <span class="o">=</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_define_class</span><span class="p">(</span><span class="s2">&#34;String&#34;</span><span class="p">,</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_cObject</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_define_method</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="s2">&#34;blank?&#34;</span><span class="p">,</span> <span class="o">-&gt;</span><span class="no">StringExtensionWrapper</span><span class="o">.</span><span class="n">blank?</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_define_method</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="s2">&#34;blank_as?&#34;</span><span class="p">,</span> <span class="o">-&gt;</span><span class="no">StringExtensionWrapper</span><span class="o">.</span><span class="n">blank_as?</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is mostly boilerplate. But now check out what I am having to do in the wrapper, in this particular snippet:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">blank?</span><span class="p">(</span><span class="nb">self</span> <span class="p">:</span> <span class="no">LibRuby</span><span class="o">::</span><span class="no">VALUE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="kp">true</span><span class="o">.</span><span class="n">to_ruby</span> <span class="k">if</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_str_length</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">  <span class="n">str</span> <span class="o">=</span> <span class="nb">String</span><span class="o">.</span><span class="n">from_ruby</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">str</span><span class="o">.</span><span class="n">blank?</span><span class="o">.</span><span class="n">to_ruby</span>
</span></span><span class="line"><span class="cl"><span class="k">rescue</span>
</span></span><span class="line"><span class="cl">  <span class="kp">true</span><span class="o">.</span><span class="n">to_ruby</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I am receiving a C-Ruby String casted as a pointer (VALUE) then I go through the lib_ruby.cr mappings to get the C-Ruby string data and copy it over into a new instance of Crystal&rsquo;s internal String representation. So at any given time I have 2 copies of the same string, one in the C-Ruby memory space and another in the Crystal memory space.</p>
<p>This happens with all FFI-like extensions but it doesn&rsquo;t happen to the pure C implementation. In Sam Saffron&rsquo;s C implementation it directly works with the same address in C-Ruby&rsquo;s memory space:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">VALUE</span>
</span></span><span class="line"><span class="cl"><span class="nf">rb_str_blank</span><span class="p">(</span><span class="n">VALUE</span> <span class="n">str</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">rb_encoding</span> <span class="o">*</span><span class="n">enc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">char</span> <span class="o">*</span><span class="n">s</span><span class="p">,</span> <span class="o">*</span><span class="n">e</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">enc</span> <span class="o">=</span> <span class="nf">STR_ENC_GET</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">s</span> <span class="o">=</span> <span class="nf">RSTRING_PTR</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It receives a pointer (direct memory address) and goes. And this is a huge advantage for the C version. When you have a big volume of medium to large sized strings being copied over from C-Ruby to Crystal, it adds a noticeable overhead that can&rsquo;t be removed.</p>
<h3>String mapping Caveat<span class="hx:absolute hx:-mt-20" id="string-mapping-caveat"></span>
    <a href="#string-mapping-caveat" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I still have a problem though. There is one edge case I was not able to overcome yet (help is most welcome). When C-Ruby passes a unicode <code>&quot;\u0000&quot;</code> I am unable to create the same character in Crystal and I end up passing just an empty string (&quot;&quot;) which is not the same thing.</p>
<p>The way to deal with it is to receive a Ruby String (VALUE) and get the C-String from it this way:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">rb_str</span> <span class="o">=</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_str_to_str</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">c_str</span>  <span class="o">=</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_string_value_cstr</span><span class="p">(</span><span class="n">pointerof</span><span class="p">(</span><span class="n">rb_str</span><span class="p">))</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If the &ldquo;str&rdquo; is the &ldquo;\u0000&rdquo; (under Ruby 2.2.5 at least) C-Ruby raises a &ldquo;string contains null bytes&rdquo; exception. Which is why I rescue from this exception like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">c_str</span> <span class="o">=</span> <span class="no">LibRuby</span><span class="o">.</span><span class="n">rb_rescue</span><span class="p">(</span><span class="o">-&gt;</span><span class="nb">String</span><span class="o">.</span><span class="n">cr_str_from_rb_cstr</span><span class="p">,</span> <span class="n">str</span><span class="p">,</span> <span class="o">-&gt;</span><span class="nb">String</span><span class="o">.</span><span class="n">return_empty_string</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="n">to_ruby</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>When an exception is triggered I have to pass the pointer to another function to rescue from it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">return_empty_string</span><span class="p">(</span><span class="n">arg</span> <span class="p">:</span> <span class="no">LibRuby</span><span class="o">::</span><span class="no">VALUE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span><span class="n">_u8</span>
</span></span><span class="line"><span class="cl">  <span class="n">pointerof</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>But this is not correct, I am just passing the pointer to a &ldquo;0&rdquo; character, which is &ldquo;empty&rdquo;. Therefore, specs are not passing correctly:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Failures:

  1) String provides a parity with active support function
     Failure/Error: expect(&#34;#{i.to_s(16)} #{c.blank_as?}&#34;).to eq(&#34;#{i.to_s(16)} #{c.blank2?}&#34;)

       expected: &#34;0 false&#34;
            got: &#34;0 true&#34;

       (compared using ==)
     # ./spec/fast_blank_spec.rb:22:in `block (3 levels) in &lt;top (required)&gt;&#39;
     # ./spec/fast_blank_spec.rb:19:in `times&#39;
     # ./spec/fast_blank_spec.rb:19:in `block (2 levels) in &lt;top (required)&gt;&#39;

  2) String treats  correctly
     Failure/Error: expect(&#34;\u0000&#34;.blank_as?).to be_falsey
       expected: falsey value
            got: true
     # ./spec/fast_blank_spec.rb:47:in `block (2 levels) in &lt;top (required)&gt;&#39;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ary gave a simple tip later, I will add it to the conclusion below.</p>
<h3>The Synthetic Benchmarks (careful on how you interpret them!)<span class="hx:absolute hx:-mt-20" id="the-synthetic-benchmarks-careful-on-how-you-interpret-them"></span>
    <a href="#the-synthetic-benchmarks-careful-on-how-you-interpret-them" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The <a href="https://github.com/rails/rails/blob/2a371368c91789a4d689d6a84eb20b238c37678a/activesupport/lib/active_support/core_ext/object/blank.rb#L101"target="_blank" rel="noopener">original Rails ActiveSupport implementation of String#blank?</a> looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">String</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 0x3000: fullwidth whitespace</span>
</span></span><span class="line"><span class="cl">  <span class="no">NON_WHITESPACE_REGEXP</span> <span class="o">=</span> <span class="s">%r![^\s#{[0x3000].pack(&#34;U&#34;)}]!</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># A string is blank if it&#39;s empty or contains whitespaces only:</span>
</span></span><span class="line"><span class="cl">  <span class="c1">#</span>
</span></span><span class="line"><span class="cl">  <span class="c1">#   &#34;&#34;.blank?                 # =&gt; true</span>
</span></span><span class="line"><span class="cl">  <span class="c1">#   &#34;   &#34;.blank?              # =&gt; true</span>
</span></span><span class="line"><span class="cl">  <span class="c1">#   &#34;　&#34;.blank?               # =&gt; true</span>
</span></span><span class="line"><span class="cl">  <span class="c1">#   &#34; something here &#34;.blank? # =&gt; false</span>
</span></span><span class="line"><span class="cl">  <span class="c1">#</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">blank?</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 1.8 does not takes [:space:] properly</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">encoding_aware?</span>
</span></span><span class="line"><span class="cl">      <span class="nb">self</span> <span class="o">!~</span> <span class="sr">/[^[:space:]]/</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="nb">self</span> <span class="o">!~</span> <span class="no">NON_WHITESPACE_REGEXP</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s mainly a regular expression comparison, which can be a bit slow. Sam&rsquo;s version is a more straightforward loop through the string to compare each character with what&rsquo;s considered &ldquo;blank&rdquo;. There are many unicode codepoints that are considered blank, some are not, which is why the C and Crystal versions are similar, but they are different from Rails&rsquo; version.</p>
<p>In the Fast Blank gem there is a <code>benchmark</code> Ruby script to compare the C-extension against Rails&rsquo; Regex based implementation.</p>
<p>The Regex implementation is called <strong>&ldquo;Slow Blank&rdquo;</strong>. It&rsquo;s particularly slow if you pass a real empty String, so in the benchmark Sam added a <strong>&ldquo;New Slow Blank&rdquo;</strong> that checks through <code>String#empty?</code> first, and this version is faster in this edge case.</p>
<p>The fast C version is called <strong>&ldquo;Fast Blank&rdquo;</strong> but although you can consider it &ldquo;correct&rdquo; it&rsquo;s not compatible with all the edge cases from Rails. So he implemented a <code>String#blank_as?</code> which is compatible with Rails. Sam calls it <strong>&ldquo;Fast Activesupport&rdquo;</strong>.</p>
<p>In my Crystal version I did the same, having both <code>String#blank?</code> and <code>String#blank_as?</code>.</p>
<p>So, without further ado, here is the <strong>C Version over OS X</strong> benchmark for empty strings, and we exercise each function many times within a few seconds to have more accurate results (check out Evan Phoenix&rsquo;s <a href="https://github.com/evanphx/benchmark-ips"target="_blank" rel="noopener">&ldquo;benchmark/ips&rdquo;</a> to understand the &ldquo;iteration per second&rdquo; methodology).</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>================== Test String Length: 0 ==================
Warming up _______________________________________
          Fast Blank   191.708k i/100ms
  Fast ActiveSupport   209.628k i/100ms
          Slow Blank    61.487k i/100ms
      New Slow Blank   203.165k i/100ms
Calculating _______________________________________
          Fast Blank     20.479M (± 9.3%) i/s -    101.414M in   5.001177s
  Fast ActiveSupport     21.883M (± 9.4%) i/s -    108.378M in   5.004350s
          Slow Blank      1.060M (± 4.7%) i/s -      5.288M in   5.001365s
      New Slow Blank     18.883M (± 6.9%) i/s -     94.065M in   5.008899s

Comparison:
  Fast ActiveSupport: 21882711.5 i/s
          Fast Blank: 20478961.5 i/s - same-ish: difference falls within error
      New Slow Blank: 18883442.2 i/s - same-ish: difference falls within error
          Slow Blank:  1059692.6 i/s - 20.65x slower</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s super fast. Rails&rsquo; version is 20x slower on my machine.</p>
<p>Now, <strong>Crystal version over OS X</strong></p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>================== Test String Length: 0 ==================
Warming up _______________________________________
          Fast Blank   174.349k i/100ms
  Fast ActiveSupport   174.035k i/100ms
          Slow Blank    64.684k i/100ms
      New Slow Blank   215.164k i/100ms
Calculating _______________________________________
          Fast Blank      8.647M (± 1.6%) i/s -     43.239M in   5.001530s
  Fast ActiveSupport      8.580M (± 1.3%) i/s -     42.987M in   5.010759s
          Slow Blank      1.047M (± 3.7%) i/s -      5.239M in   5.008907s
      New Slow Blank     19.090M (± 9.3%) i/s -     94.672M in   5.009057s

Comparison:
      New Slow Blank: 19090034.8 i/s
          Fast Blank:  8647459.7 i/s - 2.21x slower
  Fast ActiveSupport:  8580487.9 i/s - 2.22x slower
          Slow Blank:  1047465.3 i/s - 18.22x slower</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As I explained before, even checking empty strings, the Crystal version is slower than the Ruby check for <code>String#empty?</code> (New Slow Blank) because I have the string copying routine of the Wrapper mappings. This adds overhead that is perceptible over many iterations. It&rsquo;s still 18x faster than Rails, but it loses to C-Ruby.</p>
<p>Finally, <strong>Crystal version over Ubuntu</strong></p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>================== Test String Length: 0 ==================
Warming up _______________________________________
          Fast Blank   255.883k i/100ms
  Fast ActiveSupport   260.915k i/100ms
          Slow Blank   105.424k i/100ms
      New Slow Blank   284.670k i/100ms
Calculating _______________________________________
          Fast Blank      8.895M (± 9.8%) i/s -     44.268M in   5.037761s
  Fast ActiveSupport      8.647M (± 8.2%) i/s -     43.051M in   5.020125s
          Slow Blank      1.736M (± 3.9%) i/s -      8.750M in   5.048253s
      New Slow Blank     22.170M (± 6.2%) i/s -    110.452M in   5.004909s

Comparison:
      New Slow Blank: 22170031.0 i/s
          Fast Blank:  8895113.3 i/s - 2.49x slower
  Fast ActiveSupport:  8646940.8 i/s - 2.56x slower
          Slow Blank:  1736071.0 i/s - 12.77x slower</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice that it&rsquo;s around the same ballpark, but the Rails version on Ubuntu runs almost twice as fast compared to its counterpart in OS X, which makes the comparison against the Crystal library go down from 18x to 12x.</p>
<p>The benchmark keeps comparing against strings of larger and larger sizes, from 6, to 14, to 24, up to 136 characters in length.</p>
<p>Let&rsquo;s get just the last test case of 136 characters. First with <strong>C version on OS X</strong>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>================== Test String Length: 136 ==================
Warming up _______________________________________
          Fast Blank   177.521k i/100ms
  Fast ActiveSupport   193.559k i/100ms
          Slow Blank    89.378k i/100ms
      New Slow Blank    60.639k i/100ms
Calculating _______________________________________
          Fast Blank     10.727M (± 8.7%) i/s -     53.256M in   5.006538s
  Fast ActiveSupport     11.600M (± 8.3%) i/s -     57.681M in   5.009692s
          Slow Blank      1.872M (± 5.7%) i/s -      9.385M in   5.029243s
      New Slow Blank      1.017M (± 5.3%) i/s -      5.094M in   5.022994s

Comparison:
  Fast ActiveSupport: 11600112.2 i/s
          Fast Blank: 10726792.8 i/s - same-ish: difference falls within error
          Slow Blank:  1872262.5 i/s - 6.20x slower
      New Slow Blank:  1016926.7 i/s - 11.41x slower</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The C-version is consistently much faster in all test cases and in the 136 characters it&rsquo;s still 11x faster than Rails in pure Ruby.</p>
<p>Now the <strong>Crystal version over OS X</strong>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>================== Test String Length: 136 ==================
Warming up _______________________________________
          Fast Blank   127.749k i/100ms
  Fast ActiveSupport   126.538k i/100ms
          Slow Blank    94.390k i/100ms
      New Slow Blank    60.594k i/100ms
Calculating _______________________________________
          Fast Blank      3.283M (± 1.8%) i/s -     16.480M in   5.021364s
  Fast ActiveSupport      3.235M (± 1.3%) i/s -     16.197M in   5.008315s
          Slow Blank      1.888M (± 4.4%) i/s -      9.439M in   5.009458s
      New Slow Blank    967.950k (± 4.7%) i/s -      4.848M in   5.018946s

Comparison:
          Fast Blank:  3283025.1 i/s
  Fast ActiveSupport:  3234586.5 i/s - same-ish: difference falls within error
          Slow Blank:  1887800.5 i/s - 1.74x slower
      New Slow Blank:   967950.2 i/s - 3.39x slower</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s also faster, but just by 2 to 3 times compared to pure Ruby, a far cry from 11x. But my hypothesis is that the mapping and copying of so many strings over adds a large overhead that the C version does not have.</p>
<p>And the <strong>Crystal version over Ubuntu</strong>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>================== Test String Length: 136 ==================
Warming up _______________________________________
          Fast Blank   186.810k i/100ms
  Fast ActiveSupport   187.306k i/100ms
          Slow Blank   143.439k i/100ms
      New Slow Blank    98.308k i/100ms
Calculating _______________________________________
          Fast Blank      3.517M (± 3.9%) i/s -     17.560M in   5.000791s
  Fast ActiveSupport      3.485M (± 3.8%) i/s -     17.419M in   5.006427s
          Slow Blank      2.755M (± 4.2%) i/s -     13.770M in   5.008490s
      New Slow Blank      1.551M (± 4.3%) i/s -      7.766M in   5.017853s

Comparison:
          Fast Blank:  3516960.7 i/s
  Fast ActiveSupport:  3484575.5 i/s - same-ish: difference falls within error
          Slow Blank:  2754669.0 i/s - 1.28x slower
      New Slow Blank:  1550815.2 i/s - 2.27x slower</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, the Ubuntu versions of both the Crystal library and the Ruby binary run faster and the comparison shows no more than twice as much faster. And pure Ruby&rsquo;s <code>String#empty?</code> is in the same ballpark as Crystal&rsquo;s version.</p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The most obvious conclusion is that I probably made a mistake in choosing Fast Blank as my first proof of concept. The algorithm is too trivial and a simple check for <code>String#empty?</code> in pure Ruby is orders of magnitude faster than the added overhead of mapping and string copying to Crystal.</p>
<p>Also, any use case where you have a huge amount of small bits of data being transferred from C-Ruby to Crystal or any FFI-based extension will have the overhead of data copying, which a pure C-version will not have. Which is why Fast Blank is better done in C.</p>
<p>Any other use case where you have smaller amounts of data, or data that can be transferred in bulk (less calls from C-Ruby to the extension, with arguments with a larger size, and with more costly processing) is a better candidate to benefit from extensions.</p>
<p>Again, not everything gets automatically faster, we always have to figure out the use case scenarios first. But because it&rsquo;s so much easier to write in Crystal and benchmark, we can make faster proofs of concept and scrap the idea if the measurements prove that we won&rsquo;t benefit as much.</p>
<p>The Crystal documentation recently received a <a href="https://crystal-lang.org/docs/guides/performance.html"target="_blank" rel="noopener">&ldquo;Performance Guide&rdquo;</a>. It&rsquo;s very useful for you to avoid common pitfalls that harm overall performance. Even though LLVM is quite competent in heavy optimization, it can&rsquo;t do everything. So read it through to improve your general Crystal skills.</p>
<p>That being said, I still believe that this exercise was well worth it. I will probably do some more. I&rsquo;d really want to thank Ary (Crystal creator) and Paul Hoffer for the patience in helping me out through many of the quirks I found along the way.</p>
<p>While I was finishing this post, <a href="https://github.com/SamSaffron/fast_blank/pull/20#issuecomment-230875300"target="_blank" rel="noopener">Ary pointed out</a> that I could probably ditch Strings altogether and work directly with an array of bytes, which is a good idea and I will probably try that. I think I made it clear by now that the whole String copying adds a very perceptible overhead as we saw in the benchmarks above. Let me know if someone is interested in contributing as well. With a few more tweaks I believe we can have a Crystal version that can at least compete against the C version while also being more readable and maintainable for most Rubyists, which is my goal.</p>
<p>I hope the code I published here will serve as boilerplate examples for more Crystal-based Ruby extensions in the future!</p>
]]></content:encoded><category>crystal</category><category>performance</category><category>algorithms-and-data-structures</category></item></channel></rss>