<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Daniel Lemire&#039;s blog</title>
	<atom:link href="https://lemire.me/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://lemire.me/blog</link>
	<description>Daniel Lemire is a software performance expert. He ranks among the top 2% of scientists globally (Stanford/Elsevier 2025) and is one of GitHub&#039;s top 1000 most followed developers.</description>
	<lastBuildDate>Tue, 15 Sep 2026 12:54:32 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>

<image>
	<url>https://lemire.me/blog/wp-content/uploads/2026/09/cropped-IMG_9858-2-32x32.jpeg</url>
	<title>Daniel Lemire&#039;s blog</title>
	<link>https://lemire.me/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Subnormal floating-point numbers are expensive&#8230; on Intel processors</title>
		<link>https://lemire.me/blog/2026/09/15/subnormal-floating-point-numbers-are-expensive-on-intel-processors/</link>
					<comments>https://lemire.me/blog/2026/09/15/subnormal-floating-point-numbers-are-expensive-on-intel-processors/#respond</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Tue, 15 Sep 2026 12:54:32 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22876</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_gh1asmgh1asmgh1a-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" />We represent floating-point numbers using the IEEE standard. For very small numbers, the standard uses special subnormal numbers. Unfortunately, they have a reputation of making operations slow. Thus video game programmers and machine learning specialists sometimes avoid computing with subnormal numbers for performance. How slow are they? Let me measure. I wrote a small C++ &#8230; <a href="https://lemire.me/blog/2026/09/15/subnormal-floating-point-numbers-are-expensive-on-intel-processors/" class="more-link">Continue reading <span class="screen-reader-text">Subnormal floating-point numbers are expensive&#8230; on Intel processors</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_gh1asmgh1asmgh1a-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" /><p>We represent floating-point numbers using the IEEE standard. For very small numbers, the standard uses special <em>subnormal</em> numbers. Unfortunately, they have a reputation of making operations slow. Thus video game programmers and machine learning specialists sometimes avoid computing with subnormal numbers for performance.</p>
<p>How slow are they? Let me measure. I wrote a small C++ benchmark with a few kernels over arrays of 16384 values (small enough to fit in cache):</p>
<ul>
<li>multiply each value by 0.75,</li>
<li>add two arrays,</li>
<li>divide each value by 3,</li>
<li>multiply normal values by a tiny constant (2<sup>-1030</sup>) so that the <em>inputs</em> are normal but the <em>outputs</em> are subnormal,</li>
<li>a dependent chain <code>x *= 0.9999</code> repeated 16384 times.</li>
</ul>
<p>For each kernel, I feed either normal values (in <code>[0.5, 1)</code>), subnormal values, or normal values where one value in a hundred is subnormal. The compiler is allowed to autovectorize the array computations. I use GCC 15 with <code>-O3 -march=native</code> on Linux and Apple clang 17 with the same flags on macOS. I also checked with clang 21 on Linux to make sure.</p>
<p>I ran the benchmark on five processors:</p>
<ul>
<li>Intel Xeon 6975P-C (Granite Rapids), on an AWS <code>c8i.xlarge</code> instance,</li>
<li>Intel Xeon Gold 6548N (Emerald Rapids), a server in my lab,</li>
<li>AMD EPYC 9R45 (Zen 5), on an AWS <code>c8a.xlarge</code> instance,</li>
<li>AWS Graviton 5 (Arm Neoverse V3), on a <code>c9g.xlarge</code> instance,</li>
<li>Apple M4 Max.</li>
</ul>
<p>Here are the results for <code>double</code> values, in nanoseconds per element (or per step).</p>
<p><em>Intel Granite Rapids</em></p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">kernel</th>
<th style="text-align: right;">normal</th>
<th style="text-align: right;">1% subnormal</th>
<th style="text-align: right;">subnormal</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">multiply by 0.75</td>
<td style="text-align: right;">0.17</td>
<td style="text-align: right;">0.44</td>
<td style="text-align: right;">8.35</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">add two arrays</td>
<td style="text-align: right;">0.20</td>
<td style="text-align: right;">0.18</td>
<td style="text-align: right;">0.18</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">divide by 3</td>
<td style="text-align: right;">0.51</td>
<td style="text-align: right;">0.85</td>
<td style="text-align: right;">9.38</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">normal in, subnormal out</td>
<td style="text-align: right;">0.17</td>
<td style="text-align: right;"></td>
<td style="text-align: right;">8.58</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">dependent chain</td>
<td style="text-align: right;">0.77</td>
<td style="text-align: right;"></td>
<td style="text-align: right;">32.69</td>
</tr>
</tbody>
</table>
<p><em>Intel Emerald Rapids</em></p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">kernel</th>
<th style="text-align: right;">normal</th>
<th style="text-align: right;">1% subnormal</th>
<th style="text-align: right;">subnormal</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">multiply by 0.75</td>
<td style="text-align: right;">0.21</td>
<td style="text-align: right;">0.49</td>
<td style="text-align: right;">9.25</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">add two arrays</td>
<td style="text-align: right;">0.23</td>
<td style="text-align: right;">0.25</td>
<td style="text-align: right;">0.25</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">divide by 3</td>
<td style="text-align: right;">0.57</td>
<td style="text-align: right;">0.94</td>
<td style="text-align: right;">10.40</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">normal in, subnormal out</td>
<td style="text-align: right;">0.21</td>
<td style="text-align: right;"></td>
<td style="text-align: right;">9.27</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">dependent chain</td>
<td style="text-align: right;">1.14</td>
<td style="text-align: right;"></td>
<td style="text-align: right;">36.51</td>
</tr>
</tbody>
</table>
<p><em>AMD Zen 5</em></p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">kernel</th>
<th style="text-align: right;">normal</th>
<th style="text-align: right;">1% subnormal</th>
<th style="text-align: right;">subnormal</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">multiply by 0.75</td>
<td style="text-align: right;">0.07</td>
<td style="text-align: right;">0.10</td>
<td style="text-align: right;">0.08</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">add two arrays</td>
<td style="text-align: right;">0.09</td>
<td style="text-align: right;">0.09</td>
<td style="text-align: right;">0.09</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">divide by 3</td>
<td style="text-align: right;">0.11</td>
<td style="text-align: right;">0.24</td>
<td style="text-align: right;">0.25</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">normal in, subnormal out</td>
<td style="text-align: right;">0.07</td>
<td style="text-align: right;"></td>
<td style="text-align: right;">0.07</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">dependent chain</td>
<td style="text-align: right;">0.66</td>
<td style="text-align: right;"></td>
<td style="text-align: right;">0.88</td>
</tr>
</tbody>
</table>
<p><em>AWS Graviton 5</em></p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">kernel</th>
<th style="text-align: right;">normal</th>
<th style="text-align: right;">1% subnormal</th>
<th style="text-align: right;">subnormal</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">multiply by 0.75</td>
<td style="text-align: right;">0.17</td>
<td style="text-align: right;">0.17</td>
<td style="text-align: right;">0.16</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">add two arrays</td>
<td style="text-align: right;">0.19</td>
<td style="text-align: right;">0.20</td>
<td style="text-align: right;">0.20</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">divide by 3</td>
<td style="text-align: right;">0.30</td>
<td style="text-align: right;">0.30</td>
<td style="text-align: right;">0.30</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">normal in, subnormal out</td>
<td style="text-align: right;">0.17</td>
<td style="text-align: right;"></td>
<td style="text-align: right;">0.17</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">dependent chain</td>
<td style="text-align: right;">0.91</td>
<td style="text-align: right;"></td>
<td style="text-align: right;">0.91</td>
</tr>
</tbody>
</table>
<p><em>Apple M4 Max</em></p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">kernel</th>
<th style="text-align: right;">normal</th>
<th style="text-align: right;">1% subnormal</th>
<th style="text-align: right;">subnormal</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">multiply by 0.75</td>
<td style="text-align: right;">0.06</td>
<td style="text-align: right;">0.06</td>
<td style="text-align: right;">0.06</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">add two arrays</td>
<td style="text-align: right;">0.12</td>
<td style="text-align: right;">0.12</td>
<td style="text-align: right;">0.12</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">divide by 3</td>
<td style="text-align: right;">0.11</td>
<td style="text-align: right;">0.11</td>
<td style="text-align: right;">0.11</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">normal in, subnormal out</td>
<td style="text-align: right;">0.06</td>
<td style="text-align: right;"></td>
<td style="text-align: right;">0.06</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">dependent chain</td>
<td style="text-align: right;">0.72</td>
<td style="text-align: right;"></td>
<td style="text-align: right;">0.75</td>
</tr>
</tbody>
</table>
<p>On Intel processors, a multiplication involving a subnormal number is about 45 to 50 times slower than a multiplication over normal numbers. A division is 18 times slower. The dependent chain, where each multiplication waits for the previous one, goes from about 1 ns to over 30 ns per step. A normal multiplication in the dependent chain has a latency of 4 cycles. With a subnormal, it has a latency of 128 cycles. It does not matter whether the subnormal is an input or an output: multiplying normal numbers into a subnormal result is just as slow as multiplying subnormal numbers. The exception is additions and subtractions: they run at full speed. Even if subnormals are rare (1%), the cost on Intel can be significant because when the compiler vectorizes the computation, a single subnormal can slow down a whole block of computations.</p>
<p>AMD does much better. On Zen 5, multiplications and additions run at full speed regardless of the inputs. The dependent multiplication chain is a third slower (0.66 ns to 0.88 ns per step): the multiplier needs an extra cycle or so to handle a subnormal. Divisions are about twice as slow. Interestingly, with divisions, having one subnormal in a hundred is almost as slow as having all subnormals. The two Arm processors, the Graviton 5 and the Apple M4 Max, do not care at all. Subnormal numbers are handled at full speed.</p>
<p>Thus it appears that on the latest AMD and ARM processors subnormals might not be a concern. But they remain very much a performance issue under Intel processors.</p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/09/15">My source code is available</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/09/15/subnormal-floating-point-numbers-are-expensive-on-intel-processors/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>The four-colour theorem was only the start</title>
		<link>https://lemire.me/blog/2026/09/11/the-four-colour-theorem-was-only-the-start/</link>
					<comments>https://lemire.me/blog/2026/09/11/the-four-colour-theorem-was-only-the-start/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Fri, 11 Sep 2026 19:53:31 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22869</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/kALnB-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" />Mathematicians are unhappy about OpenAI. Several influential mathematicians wrote an open letter. The gist of their argument is that they form a community that trains young people. When AI started producing breakthroughs on hard mathematical problems, I asked what a very smart 17-year-old would feel. Do you still choose a math major and train yourself &#8230; <a href="https://lemire.me/blog/2026/09/11/the-four-colour-theorem-was-only-the-start/" class="more-link">Continue reading <span class="screen-reader-text">The four-colour theorem was only the start</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/kALnB-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/exa7EX5YYeA?si=n58hi6V7oVusOB27" 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="allowfullscreen"></iframe></p>
<p><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><br />
Mathematicians are unhappy about OpenAI. <a href="https://mathandai.org">Several influential mathematicians wrote an open letter.</a> The gist of their argument is that they form a community that trains young people. </span></p>
<p>When AI started producing breakthroughs on hard mathematical problems, I asked what a very smart 17-year-old would feel. Do you still choose a math major and train yourself to prove difficult results by hand?</p>
<p>This crisis has been coming for a long time. When I was a kid, the four-colour theorem was proved by a computer, in 1976. An intense debate followed. Does it count as a proof?</p>
<p>I wrote my PhD thesis using symbolic algebra software. To my knowledge, nobody then would publish their scripts. I tried to include mine. I was told it would make me look bad.</p>
<p>The letter says:</p>
<blockquote><p>&#8220;In recent months, the success of AI in solving major mathematical problems has made headlines even outside mathematical circles. But solving problems is only a tool and proxy for achieving the primary goal of conceptual understanding and insight. Forgetting this in the world of AI may turn the tool against the primary goal. Often these solutions are announced in a rush, leaving no time for a proper writeup, the isolation of new methods and ideas, and citing relevant previous work of others. As in all creative professions, this raises severe attribution and plagiarism questions. We are witnessing a general threat to intellectual work, with misalignment between the outcome of the use of AI and its initial purpose. In many fields and activities, years of training have traditionally served not only to produce a final answer or product, but also to develop understanding and the ability to formulate new questions and ideas.&#8221;</p></blockquote>
<p>They worry that kids will not choose to become old-school mathematicians. That is a reasonable fear. Yet they do not seem to consider that some kids might still do mathematics, just in a very different way.</p>
<p><a href="https://sites.math.rutgers.edu/~zeilberg/Opinion94.html">The famous mathematician Doron Zeilberger announced the problem in 2009</a>:</p>
<blockquote><p>&#8220;Teaching computers how to discover and prove mathematical results is certainly the way to go, and I believe that mathematicians who continue to do pure human, pencil-and-paper, computer-less, research, are wasting their time.&#8221;</p></blockquote>
<p>The letter implies that people, because of AI, will stop having ideas, or will stop taking the time to understand the issues. If that were true, mathematics would continue only on computers, with no humans interested, or it would stop. Both scenarios assume that people care about mathematics only when they can claim credit. </p>
<p>I am not sure why I cannot study a proof generated by AI if I want to. I can give talks about it. What becomes less likely is the reward of having been the one who proved the result. </p>
<p>The rest of the letter makes a big deal of credit. What if the AI builds on what it read and does not give proper credit? Where is the evidence that AI is worse than human beings at citing sources? </p>
<p>The letter notes that mathematics contributes to society. It never considers that faster progress might increase those contributions. </p>
<p>My stance is simple. </p>
<p>Mathematicians, software developers, engineers, lawyers, physicians will all learn to work with AI. You cannot put the genie back in the bottle. Only a totalitarian world government could try, and some of us would rather avoid that outcome. </p>
<p>Difficult proofs will now be built with AI, just as most code will be written with AI. People who insist on pen and paper should view themselves as artists. </p>
<p>Other mathematicians will work with AI. They will have no trouble finding interested kids. They will contribute to society.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/09/11/the-four-colour-theorem-was-only-the-start/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Fear Is Not an Argument</title>
		<link>https://lemire.me/blog/2026/09/10/fear-is-not-an-argument/</link>
					<comments>https://lemire.me/blog/2026/09/10/fear-is-not-an-argument/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Thu, 10 Sep 2026 18:23:42 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22845</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/wm0AF-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />We are told that AI entities much like ChatGPT might soon kill us all. The statement is vague and unfalsifiable. It might be true, it might be false. People with credentials (e.g., Turing Award recipient Yoshua Bengio) believe it. Many still remember the Year-2000 bug. Our computers used two-digit coding for dates, and some software &#8230; <a href="https://lemire.me/blog/2026/09/10/fear-is-not-an-argument/" class="more-link">Continue reading <span class="screen-reader-text">Fear Is Not an Argument</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/wm0AF-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>We are told that AI entities much like ChatGPT might soon kill us all. The statement is vague and unfalsifiable. It might be true, it might be false. People with credentials (e.g., Turing Award recipient Yoshua Bengio) believe it.</p>
<p>Many still remember the Year-2000 bug. Our computers used two-digit coding for dates, and some software could get confused. At the time, experts worried that a bug in dates might trigger nuclear Armageddon or an infrastructure collapse. At the very least, planes could fall.</p>
<p dir="auto">Americans had a moral panic over alcohol that lasted more than a century. It began with pledges of moderation. From 1920 the Volstead Act banned nearly all legal drink. It collapsed in 1933, at great expense.</p>
<p id="ember371" class="ember-view reader-text-block__paragraph">The Club of Rome predicted mass starvation. As an answer, we sterilized by force millions of Indians, and introduced the devastating one-child policy in China. The authors were never held accountable. The projections were purely mathematical, unescapable. They said. But also totally wrong and silly. Yet, we listened to them and caused great harm.</p>
<p>End-of-the-world scenarios are nothing new. Pretty much all civilizations have lived with various such predictions.</p>
<p>Some people are offended by my comparisons. I truly do not mean to offend. But the fact that disagreeing can lead to deep offense is, by itself, a sign that we face a moral issue. There is a sense in which you must agree that these intense fears are warranted.</p>
<p>Many will remember that when OpenAI first developed GPT-2, they told the world that it was too dangerous to release. Year after year, we were warned that the next iteration of it would doom us all.</p>
<p>A large language model takes tokens (words) in and outputs tokens (words). The big models can take many, many tokens in. And they do much compute. And they are based on clever ideas like vector embeddings. But, ultimately, no large language model can do anything but output tokens. You can build a better model, but the model itself does not &#8216;learn&#8217;. It is a fixed set of weights. If you take a model that has been used for months, and always feed the same tokens, you will get the same results (up to some randomness).</p>
<p>I fear that some people exploit the fact that people cannot grasp how conceptually simple a language model is. In any case, most people don&#8217;t understand how things work.</p>
<p>Things become interesting because these models can be hooked up to tools. So you can tell your language model that whenever it outputs &#8216;boom&#8217;, then a nuclear weapon will be launched. And if you hook up a nuclear weapon, then, certainly, you may start a nuclear war. So don&#8217;t do that.</p>
<p>One unproven thesis is that the models (that take in tokens and output tokens) will &#8216;decide&#8217; to acquire access to these nuclear weapons, maybe through a subterfuge. What does that mean? It is always conveniently vague. In the movie WarGames (1983), a teenager uses his computer to access a computer in charge of nuclear weapons. He almost wipes out humanity. Could this happen by accident with a kid using a language model hooked up to the Internet? But if it happens, we should blame whoever hooked up a deadly computer to the Internet.</p>
<p>Of course, any technology is inherently dangerous. Invent the bow to go hunting, and someone might soon turn the bow against you. Invent the engine, and one might soon build tanks and destroy nations. Develop nuclear technology, and one might soon raze your cities.<br />
 <br />
Yet that is not what is at stake in these discussions. The concrete threats are not ascertained and addressed. No doubt, there are some people doing this work, hopefully in the US military. What if an adversary can take control of the economy or military installations? What if an AI agent goes rogue? It is worth investing time in designing defenses.</p>
<p>What we have instead is something of the sort:</p>
<ol>
<li>A vague but global threat. It could be a fatal virus engineered in a lab, a climate catastrophe, a fatal bug affecting all our software, an alien invasion, a rogue AI, Jews taking over our institutions.</li>
<li>A few people come forward and they offer to save us. Importantly we must give them resources and influence. Ultimately, they seek a totalitarian solution: everyone must be made to agree so that we can be saved.</li>
<li>As the process unfolds, people with an opposing viewpoint are described as a danger. They must be silenced and discredited. Eventually, it can become moral to exaggerate the threat or to rewrite counterpoints. People must be made to understand one way or another.</li>
</ol>
<p>In this instance, I refer to people who advocate that AI will doom us as AI Doomers. These people tend to carry a totalitarian ideology. Their ideas will only work if everyone is made to agree. And it would severely restrict the freedom of billions of people, although they usually present it differently.</p>
<p>Doomers do not have bad intentions. On the contrary, they are often really out there to save the world. But good intentions do not, in any way, justify the means nor guarantee a good outcome.</p>
<p><span>Human beings reason based on cultural knowledge. For centuries or more, totalitarian ideas have led to ruin or pain. We ignore the warnings at our peril. It is one after the other: alcohol, the need to restrict the number of children, and so on.</span></p>
<p>But shouldn’t we just be prudent and adopt their views, just in case? It is a fallacious argument. Members of the intellectual elite have a tendency to fall for the kind of hubris where they think that, if only they were given more power, the world would be better off. It is rarely true. Thomas Sowell has an excellent book on the topic, Intellectuals and Society. He makes the case that intellectuals often promote harmful ideas, at no cost to themselves. Rationally, we should therefore be cautious.</p>
<p><span>You are not safer without technology. In fact, the risk of human extinction is assuredly higher if we are poorer and have less technology.</span></p>
<p>Is this unprecedented? The printing press was unprecedented. Arabic numbers were unprecedented. Maybe we should go back to Roman numerals, to be safe. Fear of what is without precedent soon becomes indistinguishable from an anti-innovation stance.</p>
<p>What if you do not like the people who lead the big AI companies like OpenAI and Anthropic. Maybe you think that these billionaires are a danger. And you might be right. But consider the history of humanity. Wealthy people have primarily caused harm through the promotion of bad ideas. The mass murders are almost invariably derived from politics. Stalin, Hitler, Mao.</p>
<div class="" data-block="true" data-editor="3bk51" data-offset-key="8gbvc-0-0">
<div data-offset-key="8gbvc-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="8gbvc-0-0">Are the fears grounded in reason or is some of it signaling? We have been deploying AI-enhanced drones in Ukraine for two years. Once we designate the target, they engage, autonomously. </span>At a strategic level, Palantir’s Maven Smart System is used to pick targets. It has been deployed against Iran. I have not seen much opposition to drone attacks by Ukraine against Russia, at least in the West. I cannot recall any AI Doomer denouncing Ukraine&#8217;s drones and some even endorsed them. Yet it is largely the West that is funding these drones. If you fear rogue AIs, it seems that drones able to engage a target on their own would cause enormous worry&#8230; But it would be morally inconvenient in the West to criticize the use of AI against Russia&#8230; and so, the AI Doomers are largely silent. They do not lobby their governments to require Ukraine to abstain from building AI-driven weapons. That is another sign that they do not act on reason, but, rather, on moral grounds. You might argue that these drones are not entirely autonomous, since, as far as we know, they do not pick their own targets. But ChatGPT also does not pick the prompts. The hypocrisy is par for the course <span>for many</span>. It is akin to the governor of California dining at a fancy restaurant while a stay-at-home order is in effect. Or the prime minister of Canada ranking in the top air travelers of all time, while advocating for a carbon-neutral lifestyle. You can be quite sure that many of the AI Doomers are heavy users of AI services and, in some cases, investors. It is telling you that their stance is primarily moral. Expressing fear of AI can become a form of virtue signaling. It is a convenient stance, but you would not go so far as to stop using AI, and shut down Ukraine&#8217;s drones. </p>
<p><span> In some sense, there is also a form of luxury beliefs involved. A luxury belief is a belief that makes you look good and cost you nothing, while it might harm people who are not so well-off. AI in the form of ChatGPT is proving to be a great equalizer. My plumber has access to AI that is comparable to that of a billionaire. It has the potential to serve as a superior tutor to all these kids who are left out. Many of the people engaging in the promotion of fear are either upper middle class or better. Many of them pay little attention to the fact that many of the beneficiaries of the huge investments in AI have been the men building the data centres. Thus far, AI has been great at creating jobs for blue collars. That&#8217;s not nothing.</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/09/HRjc6QSXYAIlS2k.png"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/09/HRjc6QSXYAIlS2k.png" alt="" width="600" height="731" class="alignnone size-full wp-image-22863" srcset="https://lemire.me/blog/wp-content/uploads/2026/09/HRjc6QSXYAIlS2k.png 600w, https://lemire.me/blog/wp-content/uploads/2026/09/HRjc6QSXYAIlS2k-246x300.png 246w" sizes="auto, (max-width: 600px) 100vw, 600px" /></a><br />
</span></div>
</div>
<p>
Throughout much of the world, we are facing demographic collapse and an inverted age pyramid. Soon there may be just one worker per retiree. Choosing to have fewer kids has consequences and we are about to face them. AI might be a way out of significant problems. If you are otherwise wealthy, that might not be a significant concern to you. But for the least fortunate, it might turn out to be quite a problem. Who will take care of you when you are sick? AI and robotics might prove useful to reduce suffering, don&#8217;t we think?</p>
<p>Further, we need to consider how powerful people might use the fear of AI for their own purposes. It is entirely credible that the owners of large companies could promote fear so that they get to write the regulations that will keep out their competitors, or merely as a form of cheap marketing.</p>
<p>How do I know that it is moral? Because there cannot be reasoned debate about a moral question. The facts are obvious or you are a bad person. Whenever there is a complex question, one that involves predicting the future, that cannot be discussed, unless it is in agreement with the side of fear, then you are very likely in a moral question. « Don&#8217;t you see, AI will soon kill all of us, it is obvious. » No explanation can be demanded.</p>
<p><span>You might accuse me of, in turn, promoting fear. But it should be obvious that I am doing no such thing. What I am encouraging rather is the use of reason. I am forced to give examples where inciting fear has led to disastrous effects, but my hope is that it will lead my reader to sit and reflect.</span></p>
<p><span>To my friends who fear AI, I urge you. Use reason. Do the work. Do not rely on hasty thought experiments. Work out the details. Think. Think about the countermeasures. And, please, do not include abstract thinking machines. A language model is a box that takes in token and produces tokens. Nothing more.</span></p>
<p>And for the rest of us. Let us build. Let us bring prosperity. Let us hasten the cure for cancer. Let us dream of exploring our solar system.</p>
<p><strong>Further reading</strong>.</p>
<ul>
<li style="list-style-type: none;">
<ul>
<li><span>Nirit Weiss-Blatt, “<a href="https://www.aipanic.news/p/the-weak-foundations-of-ai-doomsday">The Weak Foundations of AI Doomsday”</a> — AI Panic, May 15, 2026.</span></li>
<li><span></span>Cal Newport, “<a href="https://www.nytimes.com/2026/06/17/opinion/ai-dangerous-openai-anthropic.html">Dear A.I. Companies, the Doom Trolling Needs to Stop</a>” — New York Times, June 17, 2026</li>
<li>Andrew Orlowski, “<a href="https://www.telegraph.co.uk/news/2026/06/05/anthropics-ai-doom-predictions-hype-share-price/">Anthropic’s doom predictions are merely hype intended to make AI look important</a>” — The Telegraph, June 5, 2026.</li>
</ul>
</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/09/10/fear-is-not-an-argument/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>A quick overview of atomics in C</title>
		<link>https://lemire.me/blog/2026/09/09/a-quick-overview-of-atomics-in-c/</link>
					<comments>https://lemire.me/blog/2026/09/09/a-quick-overview-of-atomics-in-c/#respond</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Wed, 09 Sep 2026 20:41:53 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22835</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/image-4-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />If you write in C, by default, you use a single thread. Extra cores do not help until you create more threads. However, if you include the header &#60;threads.h&#62;, you can pass a function to thrd_create, and wait for it with thrd_join. #include &#60;threads.h&#62; #include &#60;stdio.h&#62; int worker(void *arg) { printf("hello from thread %d\n", *(int &#8230; <a href="https://lemire.me/blog/2026/09/09/a-quick-overview-of-atomics-in-c/" class="more-link">Continue reading <span class="screen-reader-text">A quick overview of atomics in C</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/image-4-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>If you write in C, by default, you use a single thread. Extra cores do not help until you create more threads. However, if you include the header <code>&lt;threads.h&gt;</code>, you can pass a function to <code>thrd_create</code>, and wait for it with <code>thrd_join</code>.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #8f5902; font-style: italic;">#include</span><span style="color: #f8f8f8;"> </span><span style="color: #8f5902; font-style: italic;">&lt;threads.h&gt;</span>
<span style="color: #8f5902; font-style: italic;">#include</span><span style="color: #f8f8f8;"> </span><span style="color: #8f5902; font-style: italic;">&lt;stdio.h&gt;</span>
<span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">worker</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">void</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">arg</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">printf</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #4e9a06;">"hello from thread %d\n"</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #000;">arg</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #000; font-weight: bold;">}</span>
<span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">main</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">void</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">thrd_t</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">t</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">id</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">thrd_create</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">t</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">worker</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">id</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">thrd_join</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">t</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87;">NULL</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>Be warned that C11 threads are an optional feature. If the macro <code>__STDC_NO_THREADS__</code> is defined, you do not have them. Apple&#8217;s C library has never shipped <code>&lt;threads.h&gt;</code>, so the program above does not compile on macOS, and glibc only added it in version 2.28 (2018). On such systems you fall back on POSIX threads (<code>pthread_create</code>, <code>pthread_join</code>).</p>
<p>Once you have more than one thread, they may share memory. If two threads access the same non-atomic variable with no ordering between them, and at least one of them writes, the C language calls that a data race. In other words, it is unsafe.</p>
<p>If you have a variable and it is effectively constant, then it is fine to share it. But as soon as anyone changes it, then it might get corrupted. If it is not guarded somewhat, you are in trouble.</p>
<p>To be clear, that is what the C programming language says. I don&#8217;t mean that it will happen on your machine.</p>
<p>To get a better behavior, we can use atomic variables. In C, you have the <code>&lt;stdatomic.h&gt;</code> header.</p>
<p>An atomic integer is never garbage. You always read a value that was once written.</p>
<p>In practice, on most computers you might use today, aligned 8-, 16-, 32- and 64-bit loads and stores are atomic. The C language does not care about that, so if you don&#8217;t specifically require atomicity, you might get in trouble with your C compiler.</p>
<p>The next funny problem is that instructions can be reordered. When you write:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code>x = 2
y = 3
</code></pre>
</div>
<p>This may not happen in this sequence. The variable <code>y</code> might be set before the variable <code>x</code>. You may wonder why this is allowed at all. The fundamental reason is that our processors are quite complex. They have layers of buffers and they can execute multiple instructions at once. They can issue several memory loads or stores at once.</p>
<p>By default, in C, atomic accesses are all ordered. It is as if there is an oracle that watches all threads and comes up with a consistent story where everything is in order. This can be expensive, so we prefer not to do it that way.</p>
<p>At the other extreme is the relaxed model: your reads and stores are not garbage, and a given atomic still has one modification order (you will not see 1 and then 0 if the counter only went from 0 to 1), but there is no ordering with respect to other memory.</p>
<p>So we use something intermediate, the release and acquire semantics. They are ordering barriers. A strict barrier would be &#8216;everything before me really happens before me, and everything after me really happens after me&#8217;. (Where &#8216;really happens&#8217; refers to visible effects, the hardware and compiler are allowed to cheat as long as you don&#8217;t catch them.) It is a bit too strong. So we split it in two parts: release and acquire. Intuitively, release means &#8216;if you see me, you see all the stuff before me&#8217;. Acquire means &#8216;I take that package, and everything I do after this load really happens after it&#8217;.</p>
<p>Consider the case where you have a resource (such as a block of allocated memory). You share this resource, but count how many people have access to it. When the counter goes to zero, you free the resource.</p>
<p>One thread could do&#8230;</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code>access resource
decrement counter // I won't need it anymore
</code></pre>
</div>
<p>You see these operations happening one after the other, but they may not execute this way. It is possible that they overlap, or even that the decrement occurs before the access. It is entirely safe in a single threaded context.</p>
<p>Anyhow, so the following could happen</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code>decrement counter // I won't need it anymore
access resource
</code></pre>
</div>
<p>But what if you have a second thread that does:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">access</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">resource</span>
<span style="color: #000;">decrement</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">counter</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">//</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">I</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">won' need it anymore</span>
<span style="color: #a40000; border: 1px solid #EF2929;">if (counter is zero)</span>
<span style="color: #a40000; border: 1px solid #EF2929;">  free(resource)</span>
</code></pre>
</div>
<p>You could have this interplay:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #ce5c00; font-weight: bold;">[</span><span style="color: #000;">thread2</span><span style="color: #ce5c00; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">access</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">resource</span>
<span style="color: #ce5c00; font-weight: bold;">[</span><span style="color: #000;">thread2</span><span style="color: #ce5c00; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">decrement</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">counter</span>
<span style="color: #ce5c00; font-weight: bold;">[</span><span style="color: #000;">thread1</span><span style="color: #ce5c00; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">decrement</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">counter</span>
<span style="color: #ce5c00; font-weight: bold;">[</span><span style="color: #000;">thread2</span><span style="color: #ce5c00; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">free</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">resource</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #ce5c00; font-weight: bold;">[</span><span style="color: #000;">thread1</span><span style="color: #ce5c00; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">access</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">resource</span>
</code></pre>
</div>
<p>That would be a bug.</p>
<p>So what you first do is make the decrement a &#8216;release access&#8217; which means that operations that come before it cannot be reordered after it. So if you do it this way&#8230;</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code>access resource
decrement counter using release
</code></pre>
</div>
<p>Then it is not possible that we &#8216;see&#8217; the operations as if they happened in the reverse order.</p>
<p>But then we have a second problem. Release is enough for this thread: we cannot still be using the resource after we drop it. It does not tell the last owner that everyone else is finished. The last decrement is itself a release, so it does not observe the other threads&#8217; releases. Without an acquire, that last thread can call <code>free</code> while another thread&#8217;s earlier access is not yet done.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #ce5c00; font-weight: bold;">[</span><span style="color: #000;">thread2</span><span style="color: #ce5c00; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">access</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">resource</span>
<span style="color: #ce5c00; font-weight: bold;">[</span><span style="color: #000;">thread2</span><span style="color: #ce5c00; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">decrement</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">counter</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">using</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">release</span>
<span style="color: #ce5c00; font-weight: bold;">[</span><span style="color: #000;">thread1</span><span style="color: #ce5c00; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">decrement</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">counter</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">using</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">release</span>
<span style="color: #ce5c00; font-weight: bold;">[</span><span style="color: #000;">thread1</span><span style="color: #ce5c00; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">free</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">resource</span><span style="color: #000; font-weight: bold;">)</span>
</code></pre>
</div>
<p>Thread 2 did its access before its release decrement, but thread 1 never acquired, so it is not required to see that access as finished before <code>free</code>.</p>
<p>So we need the counterpart to a release, an acquire. The last owner acquires before it frees, and that pairs with everyone else&#8217;s release:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">access</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">resource</span>
<span style="color: #000;">decrement</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">counter</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">with</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">release</span>
<span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #4e9a06;">(</span><span style="color: #000;">counter</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">is</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">zero</span><span style="color: #4e9a06;">)</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">acquire</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">barrier</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">//</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">see</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">that</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">everyone</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">else</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">is</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">done</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">free</span><span style="color: #4e9a06;">(</span><span style="color: #000;">resource</span><span style="color: #4e9a06;">)</span>
</code></pre>
</div>
<p>Alternatively, you could do this.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">access</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">resource</span>
<span style="color: #000;">decrement</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">counter</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">with</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">release</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">and</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">acquire</span>
<span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #4e9a06;">(</span><span style="color: #000;">counter</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">is</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">zero</span><span style="color: #4e9a06;">)</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">free</span><span style="color: #4e9a06;">(</span><span style="color: #000;">resource</span><span style="color: #4e9a06;">)</span>
</code></pre>
</div>
<p>The two are equivalent, but they are not necessarily equally cheap.</p>
<p>So let us consider a nice example. Let us build a small array that several threads can share. If you are the only owner, you overwrite an element in place. If not, you copy, then you update the copy. That is called copy-on-write. It is a really nice idea that you will find in many important systems.</p>
<p>We start with the type.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #8f5902; font-style: italic;">#include</span><span style="color: #f8f8f8;"> </span><span style="color: #8f5902; font-style: italic;">&lt;assert.h&gt;</span>
<span style="color: #8f5902; font-style: italic;">#include</span><span style="color: #f8f8f8;"> </span><span style="color: #8f5902; font-style: italic;">&lt;stdatomic.h&gt;</span>
<span style="color: #8f5902; font-style: italic;">#include</span><span style="color: #f8f8f8;"> </span><span style="color: #8f5902; font-style: italic;">&lt;stdlib.h&gt;</span>
<span style="color: #8f5902; font-style: italic;">#define STR_SIZE 16</span>
<span style="color: #204a87; font-weight: bold;">typedef</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">struct</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">atomic_int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">refs</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">STR_SIZE</span><span style="color: #000; font-weight: bold;">];</span>
<span style="color: #000; font-weight: bold;">}</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">shared_array</span><span style="color: #000; font-weight: bold;">;</span>
</code></pre>
</div>
<p>The payload is a plain <code>int</code> array. Only <code>refs</code> is atomic. That is deliberate. We never write <code>values</code> while another thread might be reading them.</p>
<p>We create an instance like so.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">shared_array</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">str_new</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">void</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">shared_array</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">o</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">malloc</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">sizeof</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">o</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">==</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87;">NULL</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87;">NULL</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000; font-weight: bold;">}</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">atomic_init</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">refs</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">for</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">i</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">i</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&lt;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">STR_SIZE</span><span style="color: #000; font-weight: bold;">;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">i</span><span style="color: #ce5c00; font-weight: bold;">++</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">        </span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000; font-weight: bold;">}</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>The <code>atomic_init</code> is not an atomic access in the memory-model sense. Nobody else has the pointer yet, so there is no other thread to race with. The caller owns one reference. It is just how we initialize an <code>atomic_int</code>.</p>
<p>Here is how we might naively release an instance.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #8f5902; font-style: italic;">// not real code</span>
<span style="color: #204a87; font-weight: bold;">void</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">obj_release</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">shared_array</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">auto</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">ref</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">refs</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">refs</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">-=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">ref</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">!=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #8f5902; font-style: italic;">// we are the last copy</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">free</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>What is the problem with this code?</p>
<p>The load and the decrement are two operations. Two threads can both read 2, both subtract, the counter hits zero, and nobody frees: the resource leaks. Write the check the other way around, decrementing first and then testing whether the counter is zero, as in the pseudocode above, and you get the mirror-image bug instead: with <code>refs</code> at 2, one thread decrements to 1, the other decrements to 0, both then read 0, and both call <code>free</code>. You need one atomic subtract that hands you the previous value: only the thread that saw 1 was last.</p>
<p>So you could try</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">void</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">obj_release</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">shared_array</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">atomic_fetch_sub_explicit</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">refs</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">memory_order_relaxed</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">!=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">free</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>But suppose you have two owners, so that <code>refs</code> is 2. And you have two threads doing</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">void</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">4</span><span style="color: #000; font-weight: bold;">];</span>
<span style="color: #000;">obj_release</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">);</span>
</code></pre>
</div>
<p>One of them will call <code>free</code>, but the order could be</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000; font-weight: bold;">...
[</span><span style="color: #000;">thread1</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">4</span><span style="color: #000; font-weight: bold;">];</span>
<span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">thread2</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">atomic_fetch_sub_explicit</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">refs</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">memory_order_relaxed</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">thread1</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">atomic_fetch_sub_explicit</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">refs</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">memory_order_relaxed</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">thread1</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">free</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">thread2</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">4</span><span style="color: #000; font-weight: bold;">];</span>
</code></pre>
</div>
<p>It is a bit confusing because things are not happening in order within thread 2:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">thread2</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">atomic_fetch_sub_explicit</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">refs</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">memory_order_relaxed</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">thread2</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">4</span><span style="color: #000; font-weight: bold;">];</span>
</code></pre>
</div>
<p>But this is allowed.</p>
<p>So what we can do is put a release on the <code>atomic_fetch_sub_explicit</code> and then an acquire right before the <code>free</code>.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">void</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">obj_release</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">shared_array</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">atomic_fetch_sub_explicit</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">refs</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">memory_order_release</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">!=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">atomic_thread_fence</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">memory_order_acquire</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">free</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>The release on every decrement means &#8220;I am done with the payload.&#8221; The acquire fence, only on the last owner, means &#8220;I have seen that everyone else is done.&#8221; Then <code>free</code> is safe.</p>
<p>That release does double duty, as we are about to see. It is also what lets the last remaining owner write to the payload in place.</p>
<p>If a thread wants another reference to the same instance, it only needs a relaxed access.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">shared_array</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">str_retain</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">shared_array</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">atomic_fetch_add_explicit</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">refs</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">memory_order_relaxed</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>Why relaxed? Because the caller already holds a reference, so the object cannot be freed under us: the last owner would need our reference to be gone first.</p>
<p>We can now write <code>update</code>. It consumes the caller&#8217;s reference and returns a reference to the array that contains the new value, which may or may not be the same object. After you call it, you must not touch the pointer you passed in. There is one exception: if a copy was needed and the allocation failed, it returns <code>NULL</code> and leaves the caller&#8217;s reference to <code>o</code> untouched, so you still own it and must still release it.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">shared_array</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">update</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">size_t</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">idx</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">value</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">shared_array</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">assert</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">idx</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&lt;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">STR_SIZE</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">atomic_load_explicit</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">refs</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">memory_order_acquire</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">==</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">        </span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">idx</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">value</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000; font-weight: bold;">}</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">shared_array</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">new_o</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">str_new</span><span style="color: #000; font-weight: bold;">();</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">new_o</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">==</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87;">NULL</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87;">NULL</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000; font-weight: bold;">}</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">for</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">i</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">i</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&lt;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">STR_SIZE</span><span style="color: #000; font-weight: bold;">;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">i</span><span style="color: #ce5c00; font-weight: bold;">++</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">        </span><span style="color: #000;">new_o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">];</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000; font-weight: bold;">}</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">new_o</span><span style="color: #ce5c00; font-weight: bold;">-&gt;</span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">idx</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">value</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">obj_release</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">o</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">new_o</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>If the load reads 1, we are the only owner. No other thread holds a reference, so we can write <code>values[idx]</code> in place.</p>
<p>The load is an acquire. When the load reads 1, it may read the value written by the release decrement of the last <em>other</em> owner to drop out. Everything that thread did with <code>values</code> happens before our write. Nothing in our code appearing after such as <code>o-&gt;values[idx] = value</code> may move before it. No other thread still holds a reference, so the write does not race with a concurrent reader. Later, after a retain, other threads can see it.</p>
<p>On x64, acquire and release are effectively free at the CPU: ordinary loads already behave like acquire, ordinary stores like release. You still have to write them in C, or the compiler may reorder the payload accesses. ARM has a weaker memory model so the acquire/release require different instructions (<code>ldapr</code>, <code>ldaddl</code>) which may incur a small perforamnce hit.</p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/09/09">The code is available.</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/09/09/a-quick-overview-of-atomics-in-c/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AI programming: a layered model</title>
		<link>https://lemire.me/blog/2026/09/05/ai-programming-a-layered-model/</link>
					<comments>https://lemire.me/blog/2026/09/05/ai-programming-a-layered-model/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sat, 05 Sep 2026 14:02:12 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22831</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_km32u4km32u4km32-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />In the late 1960s and 1970s, people like David Parnas faced a problem. A decade earlier there were almost no programmers. Suddenly there were hordes of inexperienced ones. What could have been a golden era was turning into a mess: far more software, much of it falling apart.   It sent Edsger Dijkstra into a &#8230; <a href="https://lemire.me/blog/2026/09/05/ai-programming-a-layered-model/" class="more-link">Continue reading <span class="screen-reader-text">AI programming: a layered model</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_km32u4km32u4km32-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><div class="" data-block="true" data-editor="egmet" data-offset-key="9b314-0-0">
<div data-offset-key="9b314-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="9b314-0-0">In the late 1960s and 1970s, people like David Parnas faced a problem. A decade earlier there were almost no programmers. Suddenly there were hordes of inexperienced ones. What could have been a golden era was turning into a mess: far more software, much of it falling apart. </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="d2c0s-0-0">
<div data-offset-key="d2c0s-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="d2c0s-0-0"> </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="9e8td-0-0">
<div data-offset-key="9e8td-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="9e8td-0-0">It sent Edsger Dijkstra into a depression. Does this sound familiar? </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="fqomm-0-0">
<div data-offset-key="fqomm-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="fqomm-0-0"> </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="8rvod-0-0">
<div data-offset-key="8rvod-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="8rvod-0-0">AI-assisted coding is producing far more code. Whether the projects will work or crumble remains to be seen. There is a danger. </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="djbp6-0-0">
<div data-offset-key="djbp6-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="djbp6-0-0"> </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="4tm2v-0-0">
<div data-offset-key="4tm2v-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="4tm2v-0-0">I&#8217;d like to propose the layered model. </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="lqkp-0-0">
<div data-offset-key="lqkp-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="lqkp-0-0"> </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="beh5r-0-0">
<div data-offset-key="beh5r-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="beh5r-0-0">Keep a small core that changes slowly and on purpose. For that part you actually read the code. You insist on tests. You can use AI assistance, but there is no vibe coding allowed. </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="e0m4-0-0">
<div data-offset-key="e0m4-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="e0m4-0-0"> </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="9umb1-0-0">
<div data-offset-key="9umb1-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="9umb1-0-0">Everything else can move fast. There will be bugs, but the AI fixes them quickly. </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="elnou-0-0">
<div data-offset-key="elnou-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="elnou-0-0"> </span></div>
</div>
<div class="" data-block="true" data-editor="egmet" data-offset-key="1u4p7-0-0">
<div data-offset-key="1u4p7-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="1u4p7-0-0">Dependencies should be one way: the outer layers depend on the core. The core cannot depend on the outer layers.</span></div>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/09/05/ai-programming-a-layered-model/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Python sets and dictionaries can have quadratic-time performance</title>
		<link>https://lemire.me/blog/2026/09/03/python-sets-and-dictionaries-can-have-quadratic-time-performance/</link>
					<comments>https://lemire.me/blog/2026/09/03/python-sets-and-dictionaries-can-have-quadratic-time-performance/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Thu, 03 Sep 2026 14:01:45 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22821</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/quadratic-150x150.webp" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />In Python, the dict data structure is the conventional key-value structure. E.g., you might store a list of names as keys and have their phone numbers as values. Valentin Ignatev wrote this amusing post on X: It is indeed widely believed that, in the strict sense, the dict data structure and its companion, the set &#8230; <a href="https://lemire.me/blog/2026/09/03/python-sets-and-dictionaries-can-have-quadratic-time-performance/" class="more-link">Continue reading <span class="screen-reader-text">Python sets and dictionaries can have quadratic-time performance</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/09/quadratic-150x150.webp" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>In Python, the <code>dict</code> data structure is the conventional key-value structure. E.g., you might store a list of names as keys and have their phone numbers as values. Valentin Ignatev wrote this amusing post on X:</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/09/xpost.webp"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/09/xpost-612x1024.webp" alt="" width="612" height="1024" class="alignnone size-large wp-image-22824" srcset="https://lemire.me/blog/wp-content/uploads/2026/09/xpost-612x1024.webp 612w, https://lemire.me/blog/wp-content/uploads/2026/09/xpost-179x300.webp 179w, https://lemire.me/blog/wp-content/uploads/2026/09/xpost-768x1286.webp 768w, https://lemire.me/blog/wp-content/uploads/2026/09/xpost.webp 908w" sizes="auto, (max-width: 612px) 100vw, 612px" /></a><img decoding="async" alt="" src="xpost.webp" /></p>
<p>It is indeed widely believed that, in the strict sense, the <code>dict</code> data structure and its companion, the set data structure, are O(1), meaning that as you increase the size of the data structure, the time to insert or query a key remains constant.</p>
<p>Let us examine the claim.</p>
<p>A hash function is a function from objects (like strings, integers, etc.) to integer values. We typically expect hash functions to be random-like, although they should always map the same object to the same integer within the current program execution. From hash functions, we construct hash tables:</p>
<ol>
<li>Create an array of buckets.</li>
<li>Given an object, apply the hash function to map it to a bucket.</li>
<li>Store the object in the bucket. When the bucket is already occupied, use some other trick (such as using a nearby bucket).</li>
</ol>
<p>If everything goes well, access and insertion in a hash table take nearly constant time, meaning that the time they take is independent of the size of the hash table.</p>
<p>This can be almost true in many instances. However, it is not formally true. There are many reasons why it is false. For example, if your data structure grows, it might be necessary to reallocate, which will typically take time proportional to the size of the data structure. But we also have the issue of collisions. A collision is what happens when two objects have the same hash value. When we use hash tables, we assume that collisions are uncommon. But it is not difficult to create many of them by picking our objects carefully.</p>
<p>In Python, <code>set</code> and <code>dict</code> are hash tables. I can &#8216;easily&#8217; make my version of Python crumble:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">M</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #0000cf; font-weight: bold;">1</span> <span style="color: #ce5c00; font-weight: bold;">&lt;&lt;</span> <span style="color: #0000cf; font-weight: bold;">61</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #ce5c00; font-weight: bold;">-</span> <span style="color: #0000cf; font-weight: bold;">1</span>
<span style="color: #000;">values</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">i</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">M</span> <span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000;">i</span> <span style="color: #204a87; font-weight: bold;">in</span> <span style="color: #204a87;">range</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">n</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)]</span>
<span style="color: #000;">s</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #204a87;">set</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">)</span>                       <span style="color: #8f5902; font-style: italic;"># insertions</span>
<span style="color: #000;">count</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #204a87;">sum</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">v</span> <span style="color: #204a87; font-weight: bold;">in</span> <span style="color: #000;">s</span> <span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000;">v</span> <span style="color: #204a87; font-weight: bold;">in</span> <span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">)</span>   <span style="color: #8f5902; font-style: italic;"># checks</span>
</code></pre>
</div>
<p>If the insertions and the checks are constant-time operations, then the whole construction and the entire check should take linear time. I ran this on an Apple M4 Max with Python 3.14, reporting the median of three runs.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: right;">n</th>
<th style="text-align: right;">time</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="text-align: right;">1000</td>
<td style="text-align: right;">4.8 ms</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">2000</td>
<td style="text-align: right;">15.5 ms</td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">4000</td>
<td style="text-align: right;">65.5 ms</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">8000</td>
<td style="text-align: right;">257 ms</td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">16000</td>
<td style="text-align: right;">1072 ms</td>
</tr>
</tbody>
</table>
<p>The time roughly quadruples each time <em>n</em> doubles. That is quadratic time, not linear time. The membership checks behave the same way: 1066 ms at <em>n</em> = 16000. At a hundred thousand elements, building the set takes 45 seconds.</p>
<p>But could we create a hash table that would be truly constant-time? No. As the size of your data structure grows, it requires progressively slower memory. If you have a small hash table, it can reside in the CPU cache and be fast. Once it reaches megabytes in size, the data structure tends to live in RAM, which is much slower. And then, eventually, you have to store it on disk, which is even slower. And so forth.</p>
<p>To put it differently, saying that a hash table is O(1) or constant time is a model. It can be true, maybe even often, but it is not reality. Models are great teaching tools: they present a simplified model that you can quickly learn. But models can also introduce biases in how we think.</p>
<p>For example, even though you have read my paragraph that says that the dict data structure gets slower, you may not believe it. You may also believe that it is typically going to be the fastest approach you can use.</p>
<p>Let us consider another practical case. Suppose that you have a large map from strings to integers, that you build once and then only query. That is a common situation: a dictionary of words to identifiers, a lookup table of country codes, a table of feature names.</p>
<p>The <a href="https://pypi.org/project/fastconstmap/">fastconstmap</a> library builds an immutable map from a <code>dict[str, int]</code>. It is suitable when your keys are known in advance.</p>
<p>I build a map from a million random sixteen-character strings to integers, and then look up every key in a shuffled order. With a <code>dict</code>, I write the obvious loop:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">total</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #0000cf; font-weight: bold;">0</span>
<span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000;">k</span> <span style="color: #204a87; font-weight: bold;">in</span> <span style="color: #000;">probes</span><span style="color: #000; font-weight: bold;">:</span>
    <span style="color: #000;">total</span> <span style="color: #ce5c00; font-weight: bold;">+=</span> <span style="color: #000;">d</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">k</span><span style="color: #000; font-weight: bold;">]</span>
</code></pre>
</div>
<p>With fastconstmap, I ask for all the keys at once, writing the values into a buffer that I own, so that no Python object is allocated per key:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">out</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">array</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #4e9a06;">"Q"</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #204a87;">bytes</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #0000cf; font-weight: bold;">8</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">n</span><span style="color: #000; font-weight: bold;">))</span>
<span style="color: #000;">cm</span><span style="color: #ce5c00; font-weight: bold;">.</span><span style="color: #000;">get_many_into</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">probes</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">out</span><span style="color: #000; font-weight: bold;">)</span>
</code></pre>
</div>
<p>I am being generous to the <code>dict</code>. I reuse the same string objects for the lookups, and a Python string caches its hash value the first time it is computed. So the <code>dict</code> does not pay for hashing at all, while fastconstmap hashes every key every time. Here are the results, in nanoseconds per key.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: right;">n</th>
<th style="text-align: right;">dict</th>
<th style="text-align: right;">get_many_into</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="text-align: right;">1000</td>
<td style="text-align: right;">21.8</td>
<td style="text-align: right;">4.3</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">10000</td>
<td style="text-align: right;">31.9</td>
<td style="text-align: right;">4.8</td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">100000</td>
<td style="text-align: right;">48.1</td>
<td style="text-align: right;">5.2</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">1000000</td>
<td style="text-align: right;">201.9</td>
<td style="text-align: right;">11.8</td>
</tr>
</tbody>
</table>
<p>The <code>dict</code> is not constant time. It goes from 22 ns to 202 ns per key as the map grows, a factor of nine, and it is not because the algorithm changed or because of collisions. It is because a million keys, their string objects, and their integer objects occupy about 116 bytes per key, so the lookups miss in the cache. The fastconstmap version needs 9 bytes per key: it stays in the cache much longer. Pay attention to how the numbers scale: the dict becomes 10 times slower as the size grows.</p>
<p>The lesson is always the same. Some models are useful but none of them is reality. Be mindful of cognitive biases.</p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/09/upcoming">The code is available.</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/09/03/python-sets-and-dictionaries-can-have-quadratic-time-performance/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>The new Go JSON API: twice as fast, or 1.5x slower?</title>
		<link>https://lemire.me/blog/2026/08/29/the-new-go-json-api-twice-as-fast-or-1-5x-slower/</link>
					<comments>https://lemire.me/blog/2026/08/29/the-new-go-json-api-twice-as-fast-or-1-5x-slower/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sat, 29 Aug 2026 18:33:33 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22792</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/xZAWW-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />JSON is a standard format for data interchange. It is effectively a tiny subset of JavaScript made of objects and arrays. It looks as follows {"key":1, "text":[1.0,2.0]}. Many programming languages include a JSON library in their standard libraries: C#, Go, Java (soon), Python, JavaScript, etc. The Go implementation is convenient, but not especially fast. Go &#8230; <a href="https://lemire.me/blog/2026/08/29/the-new-go-json-api-twice-as-fast-or-1-5x-slower/" class="more-link">Continue reading <span class="screen-reader-text">The new Go JSON API: twice as fast, or 1.5x slower?</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/xZAWW-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>JSON is a standard format for data interchange. It is effectively a tiny subset of JavaScript made of objects and arrays. It looks as follows <code>{"key":1, "text":[1.0,2.0]}</code>.</p>
<p>Many programming languages include a JSON library in their standard libraries: C#, Go, Java (soon), Python, JavaScript, etc. The Go implementation is convenient, but not especially fast.</p>
<p>Go 1.27 makes a new JSON package (<code>encoding/json/v2</code>) available by default in its standard library. The two APIs look almost the same:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">import</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">json</span><span style="color: #f8f8f8;">    </span><span style="color: #4e9a06;">"encoding/json"</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">jsonv2</span><span style="color: #f8f8f8;">  </span><span style="color: #4e9a06;">"encoding/json/v2"</span>
<span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000;">b</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">err</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">:=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">json</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Marshal</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">v</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000;">b</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">err</span><span style="color: #f8f8f8;">  </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">jsonv2</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Marshal</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">v</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000;">err</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">json</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Unmarshal</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">b</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">v</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000;">err</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">jsonv2</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Unmarshal</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">b</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">v</span><span style="color: #000; font-weight: bold;">)</span>
</code></pre>
</div>
<p>The two are not directly comparable as they differ with respect to Unicode validation, case sensitivity, etc. So it is not a drop-in replacement.</p>
<p>However, the legacy API (<code>encoding/json</code>) has also been reimplemented on top of the new engine. You can use the legacy API with either the new engine or the old one (<code>GOEXPERIMENT=nojsonv2</code>) through a flag. So we have three possibilities.</p>
<ol>
<li><em>json (legacy)</em> — <code>encoding/json</code> built with <code>GOEXPERIMENT=nojsonv2</code>, the original implementation</li>
<li><em>json (Go 1.27)</em> — <code>encoding/json</code> as of 1.27, v1 API on the v2 backend</li>
<li><em>json/v2</em> — <code>encoding/json/v2</code></li>
</ol>
<p>I used the usual <a href="https://simdjson.org/">simdjson documents</a>: <code>twitter.json</code> (632 kB, nested objects with short string keys), <code>canada.json</code> (2.25 MB, one large array of coordinates), and <code>citm_catalog.json</code> (1.73 MB, nested objects with numeric keys). I parse them into <code>any</code> (<code>interface{}</code>), which is the general-purpose path.</p>
<p>I ran this on an Apple M4 Max and on an Intel Xeon Gold 6548N (Emerald Rapids) using Go 1.27.0, on a single core (<code>GOMAXPROCS=1</code>), reporting the median of eight runs.</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/08/json-v1-v2.png"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/08/json-v1-v2-1024x790.png" alt="" width="660" height="509" class="alignnone size-large wp-image-22795" srcset="https://lemire.me/blog/wp-content/uploads/2026/08/json-v1-v2-1024x790.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/08/json-v1-v2-300x232.png 300w, https://lemire.me/blog/wp-content/uploads/2026/08/json-v1-v2-768x593.png 768w, https://lemire.me/blog/wp-content/uploads/2026/08/json-v1-v2-1536x1186.png 1536w, https://lemire.me/blog/wp-content/uploads/2026/08/json-v1-v2.png 1938w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>When unmarshalling, the legacy API on the new backend is faster than the original on <code>twitter.json</code> (172 MB/s to 203 MB/s) and on <code>citm_catalog.json</code> (186 MB/s to 241 MB/s), but slower on <code>canada.json</code> (128 MB/s down to 106 MB/s). When marshalling, it is up to twice as fast: 198 MB/s to 374 MB/s on <code>twitter.json</code>. So merely upgrading to Go 1.27, without changing a line of code, should make marshalling faster.</p>
<p>Switching to the new API helps more. Compared to the original implementation, <code>encoding/json/v2</code> unmarshals 1.5x to 2.3x faster and marshals 1.2x to 3x faster. Compared to the Go 1.27 legacy API, unmarshalling gains another 1.8x to 2x, while marshalling gains much less (1.0x to 1.7x): part of the remaining difference is that <code>json/v2</code> does less work during marshalling.</p>
<p>Thus far, I was unmarshalling into <code>any</code>, meaning that I assumed that I did not know the structure of the document. I also round-trip a slice of 10,000 small structs:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">type</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">Record</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">struct</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">ID</span><span style="color: #f8f8f8;">     </span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;">      </span><span style="color: #4e9a06;">`json:"id"`</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">Name</span><span style="color: #f8f8f8;">   </span><span style="color: #204a87; font-weight: bold;">string</span><span style="color: #f8f8f8;">   </span><span style="color: #4e9a06;">`json:"name"`</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">Email</span><span style="color: #f8f8f8;">  </span><span style="color: #204a87; font-weight: bold;">string</span><span style="color: #f8f8f8;">   </span><span style="color: #4e9a06;">`json:"email"`</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">Active</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">bool</span><span style="color: #f8f8f8;">     </span><span style="color: #4e9a06;">`json:"active"`</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">Score</span><span style="color: #f8f8f8;">  </span><span style="color: #204a87; font-weight: bold;">float64</span><span style="color: #f8f8f8;">  </span><span style="color: #4e9a06;">`json:"score"`</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">Tags</span><span style="color: #f8f8f8;">   </span><span style="color: #000; font-weight: bold;">[]</span><span style="color: #204a87; font-weight: bold;">string</span><span style="color: #f8f8f8;"> </span><span style="color: #4e9a06;">`json:"tags"`</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>The schema is specified: the JSON must be <code>[{"id":..., "name":...}, {"id":..., "name":...}...]</code>. I still get faster unmarshalling with the new API, but the legacy API with the legacy engine is faster when marshalling.<a href="http://lemire.me/blog/wp-content/uploads/2026/08/json-records.png"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/08/json-records-1024x419.png" alt="" width="660" height="270" class="alignnone size-large wp-image-22796" srcset="https://lemire.me/blog/wp-content/uploads/2026/08/json-records-1024x419.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/08/json-records-300x123.png 300w, https://lemire.me/blog/wp-content/uploads/2026/08/json-records-768x314.png 768w, https://lemire.me/blog/wp-content/uploads/2026/08/json-records-1536x628.png 1536w, https://lemire.me/blog/wp-content/uploads/2026/08/json-records.png 1870w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>The original implementation is faster. The Go 1.27 release notes said that marshal performance is broadly at parity with the previous implementation. For my test, it is not the case.</p>
<p>So unmarshalling gets faster across the board with <code>encoding/json/v2</code>, and marshalling gets faster for <code>any</code>, but it is about 1.5x slower for typed structs in my tests.</p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/08/29">The code is available.</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/08/29/the-new-go-json-api-twice-as-fast-or-1-5x-slower/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Java&#8217;s String.indexOf can be slow (quadratic)</title>
		<link>https://lemire.me/blog/2026/08/22/javas-string-indexof-can-be-slow-quadratic/</link>
					<comments>https://lemire.me/blog/2026/08/22/javas-string-indexof-can-be-slow-quadratic/#respond</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sat, 22 Aug 2026 14:56:16 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22778</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/lYgqA-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />In Java, you find the location of a substring using indexOf. String haystack = "The quick brown fox jumps over the lazy dog"; String needle = "fox"; int index = haystack.indexOf(needle); Naively, you might implement indexOf by a loop inside a loop, like so. int naiveIndexOf(String haystack, String needle) { for (int i = 0; &#8230; <a href="https://lemire.me/blog/2026/08/22/javas-string-indexof-can-be-slow-quadratic/" class="more-link">Continue reading <span class="screen-reader-text">Java&#8217;s String.indexOf can be slow (quadratic)</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/lYgqA-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>In Java, you find the location of a substring using <code>indexOf</code>.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">String</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">haystack</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #4e9a06;">"The quick brown fox jumps over the lazy dog"</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #000;">String</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">needle</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #4e9a06;">"fox"</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">index</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">haystack</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #c4a000;">indexOf</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">needle</span><span style="color: #000; font-weight: bold;">);</span>
</code></pre>
</div>
<p>Naively, you might implement <code>indexOf</code> by a loop inside a loop, like so.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">naiveIndexOf</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">String</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">haystack</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">String</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">needle</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">for</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">i</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">i</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&lt;=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">haystack</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #c4a000;">length</span><span style="color: #000; font-weight: bold;">()</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">-</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">needle</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #c4a000;">length</span><span style="color: #000; font-weight: bold;">();</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">i</span><span style="color: #ce5c00; font-weight: bold;">++</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">j</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">for</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">j</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&lt;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">needle</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #c4a000;">length</span><span style="color: #000; font-weight: bold;">()</span><span style="color: #f8f8f8;"> </span>
<span style="color: #f8f8f8;">          </span><span style="color: #ce5c00; font-weight: bold;">&amp;&amp;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">haystack</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #c4a000;">charAt</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">i</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">+</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">j</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">==</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">needle</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #c4a000;">charAt</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">j</span><span style="color: #000; font-weight: bold;">);</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">j</span><span style="color: #ce5c00; font-weight: bold;">++</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{}</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">j</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">==</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">needle</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #c4a000;">length</span><span style="color: #000; font-weight: bold;">())</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">;</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">}</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000; font-weight: bold;">}</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">-</span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>The Java implementation is much more sophisticated, and it is highly accelerated.</p>
<p>However, there are pathological cases where the Java implementation can be slow. What do I mean? Well, you do expect that the search will be more and more expensive as the size of the string grows. Right? So if you search through a 1 kilobyte string and then search through a 10 kilobyte search, you would not be surprised if the latter takes ten times slower.</p>
<p>But what of the substring? If you search for short substrings (<code>fox</code> in my example), the everything is fine. But what if you search longer and longer substrings (<code>fox jumps</code> or <code>fox jumps over</code>)? If it gets more expensive when both the string and the substring get longer, then you have what we call a quadratic complexity. In other words, it is slow.</p>
<p>In Java, if <em>n</em> is the length of your string and <em>m</em> is the length of the substring, then the complexity of <code>indexOf</code> is O(<em>n</em>·<em>m</em>). And if you look at my naive implementation (<code>naiveIndexOf</code>) then you see that in the worst case, it might do up to close to <code>haystack.length() * needle.length()</code> comparisons, that is, it is O(<em>n</em>·<em>m</em>).</p>
<p>The exact implementation of the <code>indexOf</code> function depends on your CPU and Java version. I am using OpenJDK 25 on Apple Silicon (ARM). For my purposes, I will use as a haystack of <em>n</em> copies of <code>a</code> and for the needle, the same thing, but ending with a different letter.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #8f5902; font-style: italic;">// n &gt; m</span>
<span style="color: #000;">String</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">haystack</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #4e9a06;">"a"</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #c4a000;">repeat</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">n</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000;">String</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">needle</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #4e9a06;">"a"</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #c4a000;">repeat</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">m</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">-</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">+</span><span style="color: #f8f8f8;"> </span><span style="color: #4e9a06;">"b"</span><span style="color: #000; font-weight: bold;">;</span>
</code></pre>
</div>
<p>I measured OpenJDK 25 on an Apple M4 Max. The haystack is one megabyte. Numbers are nanoseconds per haystack character.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: right;"><em>m</em></th>
<th style="text-align: right;"><code>indexOf</code></th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="text-align: right;">512</td>
<td style="text-align: right;">140</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">1024</td>
<td style="text-align: right;">273</td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">2048</td>
<td style="text-align: right;">543</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">4096</td>
<td style="text-align: right;">1076</td>
</tr>
</tbody>
</table>
<p>At <em>m</em> = 4096, a single <code>indexOf</code> over one megabyte takes 1.1 seconds.</p>
<p>Can you do better against such adversarial inputs? The textbook solution is the Two-Way algorithm of Crochemore and Perrin (1991). The implementation is simple and your favourite AI can code it for you in any programming language.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: right;"><em>m</em></th>
<th style="text-align: right;"><code>indexOf</code></th>
<th style="text-align: right;">Two-Way</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="text-align: right;">8</td>
<td style="text-align: right;">0.44</td>
<td style="text-align: right;">0.29</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">32</td>
<td style="text-align: right;">0.48</td>
<td style="text-align: right;">0.29</td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">128</td>
<td style="text-align: right;">0.45</td>
<td style="text-align: right;">0.30</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">256</td>
<td style="text-align: right;">73.9</td>
<td style="text-align: right;">0.32</td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">1024</td>
<td style="text-align: right;">273</td>
<td style="text-align: right;">0.32</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">4096</td>
<td style="text-align: right;">1076</td>
<td style="text-align: right;">0.31</td>
</tr>
</tbody>
</table>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/08/indexof-quadratic.png"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/08/indexof-quadratic-1024x675.png" alt="" width="660" height="435" class="alignnone size-large wp-image-22780" srcset="https://lemire.me/blog/wp-content/uploads/2026/08/indexof-quadratic-1024x675.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/08/indexof-quadratic-300x198.png 300w, https://lemire.me/blog/wp-content/uploads/2026/08/indexof-quadratic-768x506.png 768w, https://lemire.me/blog/wp-content/uploads/2026/08/indexof-quadratic.png 1393w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>Two-Way stays at about 0.3 ns/character no matter how long the needle is. At <em>m</em> = 4096 it is about 3500 times faster than <code>indexOf</code> on the first-character adversary.</p>
<p>So, should you switch to Two-Way for everything? No. On random text, the <code>indexOf</code> function is much faster than Two-Way.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: right;"><em>m</em></th>
<th style="text-align: right;"><code>indexOf</code></th>
<th style="text-align: right;">Two-Way</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="text-align: right;">8</td>
<td style="text-align: right;">0.30</td>
<td style="text-align: right;">0.55</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">64</td>
<td style="text-align: right;">0.10</td>
<td style="text-align: right;">0.56</td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">256</td>
<td style="text-align: right;">0.24</td>
<td style="text-align: right;">0.53</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">4096</td>
<td style="text-align: right;">0.22</td>
<td style="text-align: right;">0.55</td>
</tr>
</tbody>
</table>
<p>And Two-Way has to do non-trivial work before the search begins. So it has additional fixed overhead. It would lose most of the time in the real world, sometimes by a wide margin.</p>
<p>Should you worry about this? No. The <code>indexOf</code> function in Java is fine.</p>
<p>If an adversary can control the needle (substring), then make sure to reject long needles. Most of the time, we search for short sequences (say, less than 80 characters). If you are worried about your system crashing, you will put bounds on inputs in any case.</p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/08/22">The Java source is available</a>.</p>
<p><em>Further reading</em>: Crochemore, M., &amp; Perrin, D. (1991). <a href="http://monge.univ-mlv.fr/~mac/Articles-PDF/CP-1991-jacm.pdf">Two-way string-matching</a>. <em>Journal of the ACM</em>, 38(3), 650–674.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/08/22/javas-string-indexof-can-be-slow-quadratic/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Parsing IP addresses in C# at crazy speeds</title>
		<link>https://lemire.me/blog/2026/08/19/parsing-ip-addresses-in-c-at-crazy-speeds/</link>
					<comments>https://lemire.me/blog/2026/08/19/parsing-ip-addresses-in-c-at-crazy-speeds/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Wed, 19 Aug 2026 19:07:48 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22772</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/Cgic2-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />We are all familiar with IP addresses such as 192.168.0.1. They are typically written as four numbers in the range 0 to 255 inclusive, separated by dots. In C#, you can parse them with the standard library using IPAddress.TryParse. Pedantic people are quick to point out that IP addresses can take different forms: they can &#8230; <a href="https://lemire.me/blog/2026/08/19/parsing-ip-addresses-in-c-at-crazy-speeds/" class="more-link">Continue reading <span class="screen-reader-text">Parsing IP addresses in C# at crazy speeds</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/Cgic2-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>We are all familiar with IP addresses such as <code>192.168.0.1</code>. They are typically written as four numbers in the range 0 to 255 inclusive, separated by dots. In C#, you can parse them with the standard library using <code>IPAddress.TryParse</code>.</p>
<p>Pedantic people are quick to point out that IP addresses can take different forms: they can be IPv6 or IPv4 and there are many weird ways to write an IPv4 address. But for the purpose of performance optimization, we care about the common case. The common case is strings such as <code>192.168.0.1</code> or <code>12.121.244.111</code>.</p>
<p>Our processors are capable of data parallelism, meaning that they have instructions (called SIMD) that can process several bytes at once, at least 16 bytes, sometimes more. <a href="https://lemire.me/blog/2023/06/08/parsing-ip-addresses-crazily-fast/">A few years ago, I showed that you can parse IPv4 addresses with SIMD</a>. I have been <a href="https://github.com/lemire/simdip">revisiting this idea with AVX-512</a>, the instruction set that recent x64 (AMD/Intel) processors support. I expect that all Intel and AMD processors made in the near future will have great support for AVX-512, and it is already the case for server processors and recent AMD processors.</p>
<p>So I wondered, could we do it in C#? People are sometimes surprised that I care about C#. Isn&#8217;t that more Microsoft slop? No. Not at all. C# and .NET are very reasonable, portable systems.</p>
<p>Plus you can write fast code in C#. I have two optimized libraries that I hope the Microsoft .NET team will one day adopt in the standard .NET library: an optimized <code>Utf8Utility.GetPointerToFirstInvalidByte</code> function used internally to validate Unicode strings (in the <a href="https://github.com/simdutf/SimdUnicode">SimdUnicode library</a>) and a <a href="https://github.com/simdutf/SimdBase64">fast base64 decoding library</a>. I love working with .NET C#.</p>
<p>As of .NET 10, we have AVX-512 support, including masked loads. What are masked loads and why do they matter? Suppose that I give you a string that is no longer than 16 bytes, but could be shorter. If you load data in a SIMD register, you normally have to load the full register width (so 8, 16, 32, 64 bytes). So what do you do when it is not possible? You can pad the input string or pull other tricks, but it gets dirty. A nice approach is to have masked loads where you, say, load the full register (say 16 bytes), but you indicate which bytes you want to be loaded from memory with a mask. So if you use <code>0b10011</code> as a mask, then only the first, second, and fifth bytes are loaded from memory. This makes it possible to initialize a 16-byte register with a string that has between 0 and 16 bytes, while never reading beyond the string. I have an article entitled <a href="https://lemire.me/blog/2022/11/08/modern-vector-programming-with-masked-loads-and-stores/">Modern vector programming with masked loads and stores</a> if you want to know more.</p>
<p>To make things trickier, C#, like Java and JavaScript, defaults to UTF-16, meaning that each character, even if it is an ASCII character like <code>A</code> or <code>1</code>, uses two bytes. The ASCII codepoint value occupies the least significant bits of a 16-bit word.</p>
<p>So what we need to do is to selectively load from a 32-byte input, and then drop the unnecessary zero bytes. The gist of it looks as follows in C#.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">unsafe</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">bool</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">TryParseAvx512</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">ReadOnlySpan</span><span style="color: #ce5c00; font-weight: bold;">&lt;</span><span style="color: #204a87; font-weight: bold;">char</span><span style="color: #ce5c00; font-weight: bold;">&gt;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">s</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">out</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">uint</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">ip</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">len</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">s</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Length</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">        </span><span style="color: #204a87; font-weight: bold;">fixed</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">char</span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">cp</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">s</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #f8f8f8;">        </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">            </span><span style="color: #8f5902; font-style: italic;">// next two lines are a trick to load just the first len characters</span>
<span style="color: #f8f8f8;">            </span><span style="color: #000;">Vector256</span><span style="color: #ce5c00; font-weight: bold;">&lt;</span><span style="color: #204a87; font-weight: bold;">ushort</span><span style="color: #ce5c00; font-weight: bold;">&gt;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">charMask</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">Vector256</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">LessThan</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">CharLaneIndex</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">Vector256</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Create</span><span style="color: #000; font-weight: bold;">((</span><span style="color: #204a87; font-weight: bold;">ushort</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #000;">len</span><span style="color: #000; font-weight: bold;">));</span>
<span style="color: #f8f8f8;">            </span><span style="color: #000;">Vector256</span><span style="color: #ce5c00; font-weight: bold;">&lt;</span><span style="color: #204a87; font-weight: bold;">ushort</span><span style="color: #ce5c00; font-weight: bold;">&gt;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">chars</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">Avx512BW</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">VL</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">MaskLoad</span><span style="color: #000; font-weight: bold;">((</span><span style="color: #204a87; font-weight: bold;">ushort</span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #000;">cp</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">charMask</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">Vector256</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Create</span><span style="color: #000; font-weight: bold;">((</span><span style="color: #204a87; font-weight: bold;">ushort</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #4e9a06;">'0'</span><span style="color: #000; font-weight: bold;">));</span>
<span style="color: #f8f8f8;">            </span><span style="color: #8f5902; font-style: italic;">// check that everything is ASCII otherwise, it is not an IP!</span>
<span style="color: #f8f8f8;">            </span><span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">Avx512BW</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">VL</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">CompareGreaterThan</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">chars</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">Vector256</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Create</span><span style="color: #000; font-weight: bold;">((</span><span style="color: #204a87; font-weight: bold;">ushort</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #0000cf; font-weight: bold;">0x7F</span><span style="color: #000; font-weight: bold;">)).</span><span style="color: #000;">ExtractMostSignificantBits</span><span style="color: #000; font-weight: bold;">()</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">!=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #f8f8f8;">            </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">                </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">false</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">            </span><span style="color: #000; font-weight: bold;">}</span>
<span style="color: #f8f8f8;">            </span><span style="color: #8f5902; font-style: italic;">// There we go, we have the address as ASCII</span>
<span style="color: #f8f8f8;">            </span><span style="color: #8f5902; font-style: italic;">// in a 16-byte register.</span>
<span style="color: #f8f8f8;">            </span><span style="color: #000;">Vector128</span><span style="color: #ce5c00; font-weight: bold;">&lt;</span><span style="color: #204a87; font-weight: bold;">byte</span><span style="color: #ce5c00; font-weight: bold;">&gt;</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">str</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">Avx512BW</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">VL</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">ConvertToVector128Byte</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">chars</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">            </span><span style="color: #8f5902; font-style: italic;">// ...</span>
<span style="color: #f8f8f8;">        </span><span style="color: #000; font-weight: bold;">}</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>This looks a bit difficult to read, but that&#8217;s fine. Most people never need to worry about such code.</p>
<p>Then we use a somewhat fancy trick where we locate the dots, and use the fact that there are only 81 ways to position the dots. We then move the bytes, do a dot product and validate. It is the same routine as the C++ code. It is not trivial, but I am working on a formal paper to document the tricks used.</p>
<p>The pedantic people will say: wait, there are other ways to write IP addresses !!! Ok fine. We handle them with a fallback, like so.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">TryParseAvx512</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">s</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">out</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">uint</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">ip</span><span style="color: #000; font-weight: bold;">))</span>
<span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">address</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">new</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">IPAddress</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">ip</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">true</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #000; font-weight: bold;">}</span>
<span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">IPAddress</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">TryParse</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">s</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">out</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">address</span><span style="color: #000; font-weight: bold;">);</span>
</code></pre>
</div>
<p>What about the cases where your processor does not support AVX-512? C# makes this dead easy. You can just guard it with one if:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">if</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">Avx512BW</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">VL</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">IsSupported</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">...</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>To benchmark this, I generated 10,000 random 32-bit addresses and parsed the resulting strings 20 million times, constructing an <code>IPAddress</code> each time. On a relatively recent Intel processor (Intel Xeon Gold 6548N, Emerald Rapids) running .NET 10, I get the following.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">function</th>
<th style="text-align: right;">ns/addr</th>
<th style="text-align: right;">million addr/s</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>IPAddress.TryParse</code></td>
<td style="text-align: right;">45.3</td>
<td style="text-align: right;">22.1</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">AVX-512 + fallback</td>
<td style="text-align: right;">14.1</td>
<td style="text-align: right;">71.1</td>
</tr>
</tbody>
</table>
<p>So the AVX-512 approach is about three times faster than the standard library. My routine itself does not take fourteen nanoseconds; there is other overhead.</p>
<p>As usual, <a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/08/19">the C# source is available</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/08/19/parsing-ip-addresses-in-c-at-crazy-speeds/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Go 1.27 will make some allocations cheaper</title>
		<link>https://lemire.me/blog/2026/08/15/go-1-27-will-make-some-allocations-cheaper/</link>
					<comments>https://lemire.me/blog/2026/08/15/go-1-27-will-make-some-allocations-cheaper/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sat, 15 Aug 2026 20:59:19 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22769</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/2N3RJ-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Like most programming languages, Go has both stack allocations, whose lifetime is limited to the current function, and dynamic (or heap) allocations. The name stack comes from the fact that the memory management is somewhat trivial. There is typically one stack per thread (or goroutine in Go). When a function needs memory, it simply appends &#8230; <a href="https://lemire.me/blog/2026/08/15/go-1-27-will-make-some-allocations-cheaper/" class="more-link">Continue reading <span class="screen-reader-text">Go 1.27 will make some allocations cheaper</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/2N3RJ-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Like most programming languages, Go has both stack allocations, whose lifetime is limited to the current function, and dynamic (or heap) allocations.</p>
<p>The name stack comes from the fact that the memory management is somewhat trivial. There is typically one stack per thread (or goroutine in Go). When a function needs memory, it simply appends data to the stack. When the function returns, the memory is dropped from the end of the stack. So the memory last allocated is deallocated first.</p>
<p>Heap memory is potentially considerably more complex. For one thing, it is meant to be accessible by several threads (or goroutines). An object can be allocated by one function and later reclaimed after an entirely different function, possibly running on a different thread (or goroutine), has dropped the last reference to it. Unlike the stack, there is no prescribed order for allocating and reclaiming heap memory. In Go, the garbage collector does the reclaiming.</p>
<p>Typically, stack allocations have a size known at compile time. Many systems give each thread a fixed-size stack, although Go grows goroutine stacks as needed.</p>
<p>There are many ways in Go to do a heap allocation. A common one is when you allocate a slice, as in this instance where you allocate memory for 100 integers:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">x</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">:=</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87;">make</span><span style="color: #000; font-weight: bold;">([]</span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">100</span><span style="color: #000; font-weight: bold;">)</span>
</code></pre>
</div>
<p>If the slice <code>x</code> is not entirely local to a function, Go will typically just allocate it on the heap. It will do so similarly when a function returns a pointer. For example, in the following instance, I assign the value 1 to a local integer variable, but I return a pointer to it.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">func</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">f</span><span style="color: #000; font-weight: bold;">()</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">x</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">:=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1</span>
<span style="color: #f8f8f8;">  </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">x</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>In C/C++, this would be quite bad. You should get a warning such as <code>address of local variable 'x' returned</code>. In Go, the variable <code>x</code> will typically get allocated on the heap.</p>
<p>In many Go programs, we end up doing a lot of heap allocations of small objects. It can become a bottleneck in some cases. Think about when you are maintaining a tree or a linked list where each value (node) is an object that must live on the heap. If the data structure is highly dynamic, you will be constantly allocating these small objects.</p>
<p>Memory allocation on the heap is usually not done at arbitrary sizes. You often cannot get exactly, say, 13 bytes. In Go, small allocations are rounded up to a size class: 8 bytes, 16 bytes, 24 bytes, 32 bytes, and so forth. There is also some overhead to each heap allocation, from rounding and from allocator metadata.</p>
<p>The compiler knows the size of the object, but prior to Go 1.27, Go would call a generic function when doing a heap allocation. This generic function would then look up the size class and take the corresponding path. Starting with 1.27, for small objects (under 80 bytes), Go relies on dedicated functions.</p>
<p>It is easy to benchmark in Go. A basic benchmark might look as follows.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">type</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">Node</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">struct</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">value</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">int64</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">next</span><span style="color: #f8f8f8;">  </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">Node</span>
<span style="color: #000; font-weight: bold;">}</span>
<span style="color: #204a87; font-weight: bold;">var</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">sink</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">any</span>
<span style="color: #204a87; font-weight: bold;">func</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">BenchmarkAllocNode16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">b</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">testing</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">B</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">for</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">b</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Loop</span><span style="color: #000; font-weight: bold;">()</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">        </span><span style="color: #000;">sink</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">Node</span><span style="color: #000; font-weight: bold;">{}</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000; font-weight: bold;">}</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>On my MacBook, the results are quite telling. Go 1.27 is nearly twice as fast!</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">allocation</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Go 1.26</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Go 1.27</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">speedup</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">16 B, has pointer</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">9.5 ns</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">5.5 ns</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">1.8x</td>
</tr>
</tbody>
</table>
<p>This will not help all software, just the components that do many small allocations.</p>
<p><em>The code is <a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/08/15">available</a>.</em></p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/08/15/go-1-27-will-make-some-allocations-cheaper/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>AI programming : are you angry yet?</title>
		<link>https://lemire.me/blog/2026/08/12/ai-programming-are-you-angry-yet/</link>
					<comments>https://lemire.me/blog/2026/08/12/ai-programming-are-you-angry-yet/#respond</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 15:50:48 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22764</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/o1lot-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />AI-assisted programming is fast evolving and there is a tension between &#8216;we no longer need to understand the code&#8217; and &#8216;what is my purpose as a programmer&#8217;. I recorded a short video on this topic with how I think the tension can result in conflicts.]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/o1lot-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>AI-assisted programming is fast evolving and there is a tension between &#8216;we no longer need to understand the code&#8217; and &#8216;what is my purpose as a programmer&#8217;. I recorded a short video on this topic with how I think the tension can result in conflicts.</p>
<p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/WweTpPgJPhg?si=_wB-VqPyAmufrElY" 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="allowfullscreen"></iframe></p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/08/12/ai-programming-are-you-angry-yet/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Profile-guided optimization in Go</title>
		<link>https://lemire.me/blog/2026/08/09/profile-guided-optimization-in-go/</link>
					<comments>https://lemire.me/blog/2026/08/09/profile-guided-optimization-in-go/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sun, 09 Aug 2026 23:17:36 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22758</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/ltISq-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />When a compiler optimizes your program, it has to guess. Which functions are worth inlining? Which side of a branch is the common one? Which method does this interface call actually reach? At compile time it cannot know, so it uses heuristics. Profile-guided optimization (PGO) replaces the guessing with measurement: you run your program, record &#8230; <a href="https://lemire.me/blog/2026/08/09/profile-guided-optimization-in-go/" class="more-link">Continue reading <span class="screen-reader-text">Profile-guided optimization in Go</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/ltISq-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>When a compiler optimizes your program, it has to guess. Which functions are worth inlining? Which side of a branch is the common one? Which method does this interface call actually reach? At compile time it cannot know, so it uses heuristics. Profile-guided optimization (PGO) replaces the guessing with measurement: you run your program, record where it spends its time, and hand that recording back to the compiler for a second build.</p>
<p>PGO is a common feature of compiler systems. Google applied PGO to Chrome under Windows in 2016, <a href="https://blog.chromium.org/2016/10/making-chrome-on-windows-faster-with-pgo.html">reporting gains of up to 15%</a>. I expect <a href="https://webkit.org/blog/15249/">all mainstream Web browsers to be built with PGO</a>. </p>
<p>There are now fancier techniques than mere heuristics with PGO. You can use AI to recognize patterns and so forth. But they are not always widely available.</p>
<p>Go has supported PGO since version 1.20. You collect a profile, and pass it to the compiler.</p>
<p>A CPU profile is a statistical record of where a program spends its time. While the program runs, the Go runtime interrupts it about a hundred times a second and writes down the call stack at that instant. After a few seconds you have thousands of such samples, and counting them tells you which functions were executing and who called them. In Go you produce one by wrapping the work you care about:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">f</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">_</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">:=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">os</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Create</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #4e9a06;">"cpu.pprof"</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000;">pprof</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">StartCPUProfile</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">f</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;">   </span><span style="color: #8f5902; font-style: italic;">// from runtime/pprof</span>
<span style="color: #204a87; font-weight: bold;">defer</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">pprof</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">StopCPUProfile</span><span style="color: #000; font-weight: bold;">()</span>
</code></pre>
</div>
<p>The compiler reads the call-stack counts and uses them for two things above all: inlining call sites that turn out to be hot, and devirtualizing interface calls whose target is nearly always the same concrete type.</p>
<p>I took three JSON documents that I wanted to parse:</p>
<ul>
<li><code>twitter.json</code> (632 kB), a nest of small objects with short string keys</li>
<li><code>canada.json</code> (2.25 MB), essentially one enormous array of floating-point coordinates</li>
<li><code>citm_catalog.json</code> (1.73 MB), deeply nested objects with numeric keys</li>
</ul>
<p>I parse each of them with the standard library&#8217;s <code>encoding/json</code> into an <code>interface{}</code>. The baseline, with no profile, parses at 112 MB/s for <code>twitter.json</code>, 74 MB/s for <code>canada.json</code> and 116 MB/s for <code>citm_catalog.json</code>.</p>
<p>The procedure is three commands:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code>go<span style="color: #f8f8f8;"> </span>build<span style="color: #f8f8f8;"> </span>-o<span style="color: #f8f8f8;"> </span>bench<span style="color: #f8f8f8;"> </span>.<span style="color: #f8f8f8;">                        </span><span style="color: #8f5902; font-style: italic;"># ordinary build</span>
./bench<span style="color: #f8f8f8;"> </span>-profile<span style="color: #f8f8f8;"> </span>cpu.pprof<span style="color: #f8f8f8;"> </span>-train<span style="color: #f8f8f8;"> </span>twitter.json<span style="color: #f8f8f8;">   </span><span style="color: #8f5902; font-style: italic;"># collect a CPU profile</span>
go<span style="color: #f8f8f8;"> </span>build<span style="color: #f8f8f8;"> </span>-pgo<span style="color: #ce5c00; font-weight: bold;">=</span>cpu.pprof<span style="color: #f8f8f8;"> </span>-o<span style="color: #f8f8f8;"> </span>bench_pgo<span style="color: #f8f8f8;"> </span>.<span style="color: #f8f8f8;">     </span><span style="color: #8f5902; font-style: italic;"># build again, with the profile</span>
</code></pre>
</div>
<p>I did it three times, profiling each document on its own, and then measured all three documents against each of the three builds.</p>
<p><span>Each panel of the figure is one document being parsed, and the three bars inside it are the three PGO builds: the binary trained on twitter.json, the one trained on canada.json, and the one trained on citm_catalog.json. Bar height is the speed gain over the ordinary, profile-free build of that same document, in percent, so zero means PGO changed nothing and a bar below the axis means the PGO build was slower. The green bar in each panel is the matched case, where the profile was collected on the very document being measured.</span></p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/08/go-pgo.png"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/08/go-pgo-707x1024.png" alt="" width="660" height="956" class="alignnone size-large wp-image-22759" srcset="https://lemire.me/blog/wp-content/uploads/2026/08/go-pgo-707x1024.png 707w, https://lemire.me/blog/wp-content/uploads/2026/08/go-pgo-207x300.png 207w, https://lemire.me/blog/wp-content/uploads/2026/08/go-pgo-768x1112.png 768w, https://lemire.me/blog/wp-content/uploads/2026/08/go-pgo-1061x1536.png 1061w, https://lemire.me/blog/wp-content/uploads/2026/08/go-pgo.png 1292w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>The gains are modest. The best result is <code>canada.json</code> at +4.7%, and most differences are in the 2–3% range. Profiling one document usually helps the others, but not reliably. Profiling <code>twitter.json</code> gave a decent improvement everywhere: +3.1%, +2.0%, +2.8%. But profiling <code>canada.json</code> bought 4.7% on <code>canada.json</code> and essentially nothing anywhere else. Interestingly, profiling <code>citm_catalog.json</code> produced a mere +0.8% on its own document while helping <code>twitter.json</code> more.</p>
<p>A 3% speedup is not exciting in isolation, but it may come nearly for free. Observe how you may get slightly negative results for cases you did not train for. That&#8217;s expected generally, but the effect is modest in the case of Go because its optimizations are themselves modest in the first pace. That is, you are not getting a much an effect, but the process is less likely to backfire for other workloads.</p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/08/09">The code is available.</a><br />
<a href="http://lemire.me/blog/wp-content/uploads/2026/08/go-pgo.png"></a><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/08/09"></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/08/09/profile-guided-optimization-in-go/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>How fast is C++26’s std::hive?</title>
		<link>https://lemire.me/blog/2026/08/02/how-fast-is-c26s-stdhive/</link>
					<comments>https://lemire.me/blog/2026/08/02/how-fast-is-c26s-stdhive/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sun, 02 Aug 2026 17:00:10 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22750</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/si3ao-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />C++26 adds a new container to the standard library: std::hive. It is meant to occupy the ground between std::vector and std::list. Like a vector, it keeps its elements in contiguous blocks of memory, so scanning it does not require you to chase a pointer for every element. Like a list, it never moves an element &#8230; <a href="https://lemire.me/blog/2026/08/02/how-fast-is-c26s-stdhive/" class="more-link">Continue reading <span class="screen-reader-text">How fast is C++26’s std::hive?</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/08/si3ao-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>C++26 adds a new container to the standard library: <code>std::hive</code>. It is meant to occupy the ground between <code>std::vector</code> and <code>std::list</code>. Like a vector, it keeps its elements in contiguous blocks of memory, so scanning it does not require you to chase a pointer for every element. Like a list, it never moves an element once it has been inserted: your pointers, references and iterators stay valid, and you may erase any element in constant time without disturbing the others.</p>
<p>Internally, a hive is a linked list of blocks. Each block carries a <em>skipfield</em>: a small integer per slot that tells the iterator how many erased slots to jump over.</p>
<p>No standard library ships <code>std::hive</code> yet to my knowledge. Fortunately there is an implementation (<a href="https://github.com/mattreecebentley/plf_hive">plf::hive</a> by Matt Bentley) as a single header file that you can use today.</p>
<p>I use elements of type <code>uint64_t</code>, GCC 16.1 with <code>-O3 -march=native</code>, on an Intel Xeon Gold 6548N (Emerald Rapids), pinned to one core. Numbers are nanoseconds per element, along with the cycles and instructions retired per element.</p>
<p>We start from an empty container and append a million values. The container is then destroyed.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">container</th>
<th style="text-align: right;">ns/element</th>
<th style="text-align: right;">instructions/element</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::vector</code> (<code>reserve</code>)</td>
<td style="text-align: right;">0.29</td>
<td style="text-align: right;">8.0</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::vector</code></td>
<td style="text-align: right;">0.81</td>
<td style="text-align: right;">8.0</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::hive</code></td>
<td style="text-align: right;">1.57</td>
<td style="text-align: right;">16.2</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::hive</code> (<code>reserve</code>)</td>
<td style="text-align: right;">1.76</td>
<td style="text-align: right;">17.0</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::list</code></td>
<td style="text-align: right;">14.22</td>
<td style="text-align: right;">220.0</td>
</tr>
</tbody>
</table>
<p>A <code>std::list</code> needs one allocation per element, and glibc&#8217;s malloc and free together cost over 200 instructions per element. It is an order of magnitude behind everyone else. That is not news.</p>
<p>The interesting comparison is vector against hive. A hive is about twice the cost of a vector, and it needs twice the instructions. This is the price of the skipfield: every insertion writes an element <em>and</em> a skipfield entry, and maintains the block bookkeeping. Note that calling <code>reserve</code> on a hive does not help in my experiments.</p>
<p>Next we iterate over the the container and sum the values.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">container</th>
<th style="text-align: right;">ns/element</th>
<th style="text-align: right;">cycles/element</th>
<th style="text-align: right;">instructions/element</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::vector</code></td>
<td style="text-align: right;">0.22</td>
<td style="text-align: right;">0.78</td>
<td style="text-align: right;">1.0</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::list</code></td>
<td style="text-align: right;">1.51</td>
<td style="text-align: right;">5.27</td>
<td style="text-align: right;">4.0</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::hive</code></td>
<td style="text-align: right;">1.77</td>
<td style="text-align: right;">6.18</td>
<td style="text-align: right;">9.0</td>
</tr>
</tbody>
</table>
<p>A hive iterates no faster than a linked list here, slightly slower, in fact, and about eight times slower than a vector. (Update: Joseph Garvin points out that I measure the happy case for the <code>std::list</code> in this instance where all the entries were allocated in sequence. The worst case scenario for <code>std::list</code> when the nodes are all over the heap can be much slower.)</p>
<p>The vector loop retires one instruction per element and finishes in 0.78 cycles: the processor is executing several elements at once. This is possible because the <code>std::vector</code> implementation benefits from autovectorization: the compiler recognizes that it can load several words at once in wide (SIMD). Further, it does not have to check the bitfield like the <code>std::hive</code> data structure.</p>
<p>We can check this. Walk the same container with two independent iterators, one starting halfway in, and count the cost per element visited:</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">container</th>
<th style="text-align: right;">one traversal</th>
<th style="text-align: right;">two interleaved traversals</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::vector</code></td>
<td style="text-align: right;">0.78 cycles</td>
<td style="text-align: right;">0.79 cycles</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::list</code></td>
<td style="text-align: right;">5.27 cycles</td>
<td style="text-align: right;">3.02 cycles</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::hive</code></td>
<td style="text-align: right;">6.18 cycles</td>
<td style="text-align: right;">3.10 cycles</td>
</tr>
</tbody>
</table>
<p>The vector does not care: it was already throughput-bound. The hive and the list get nearly twice as fast per element, because two independent chains can be in flight at once. Hive iteration is latency-bound, exactly like list iteration. It merely has better locality.</p>
<p>That locality does show up when the data gets big. At ten million elements the list falls apart while the hive holds steady:</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">container</th>
<th style="text-align: right;">100K</th>
<th style="text-align: right;">1M</th>
<th style="text-align: right;">10M</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::vector</code></td>
<td style="text-align: right;">0.08</td>
<td style="text-align: right;">0.22</td>
<td style="text-align: right;">0.32</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::list</code></td>
<td style="text-align: right;">1.48</td>
<td style="text-align: right;">1.51</td>
<td style="text-align: right;">3.51</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::hive</code></td>
<td style="text-align: right;">1.76</td>
<td style="text-align: right;">1.77</td>
<td style="text-align: right;">1.96</td>
</tr>
</tbody>
</table>
<p>Erasing is what a hive is for, so it would be unfair not to look. I erase half the elements at scattered positions using <code>std::remove_if</code>:</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">container</th>
<th style="text-align: right;">ns per original element</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::hive</code></td>
<td style="text-align: right;">2.1</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::vector</code></td>
<td style="text-align: right;">3.0</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::list</code></td>
<td style="text-align: right;">77.4</td>
</tr>
</tbody>
</table>
<p>The hive wins, but by less than you might expect, and at ten million elements the ordering reverses (1.3 ns for the vector against 2.5 for the hive). <code>std::remove_if</code> is a single streaming pass, and streaming passes are cheap. Of course the vector moved every surviving element and invalidated every pointer into it, which is precisely what a hive promises not to do.</p>
<p>Memory, measured by asking glibc how many bytes it has handed out, per live element:</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">container</th>
<th style="text-align: right;">after building</th>
<th style="text-align: right;">after <code>shrink_to_fit</code></th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::vector</code></td>
<td style="text-align: right;">8.4</td>
<td style="text-align: right;">8.0</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::hive</code></td>
<td style="text-align: right;">9.4</td>
<td style="text-align: right;">9.4</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>std::list</code></td>
<td style="text-align: right;">32.0</td>
<td style="text-align: right;"></td>
</tr>
</tbody>
</table>
<p>A hive costs about a byte per element over a vector, for a payload of eight bytes, when the vector is packed tight. A list costs more due to the overhead of the linked list.</p>
<p>A vector built by <code>push_back</code> has a capacity that typically exceeds its size. Thus even if you have 8-byte entries, you will use, on average, more than 8 bytes per entry even for large vectors. You can recover the excess capacity with the <code>shrink_to_fit</code> method.</p>
<p>What should we conclude?</p>
<p>The <code>std::hive</code> data structure is not a faster vector. But it is a much better <code>std::list</code>. It gives you the same guarantees that make people reach for a list, stable references, cheap erasure anywhere, while using less memory.</p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/08/02">My source code is available</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/08/02/how-fast-is-c26s-stdhive/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title>Memory-level parallelism: AMD is the king</title>
		<link>https://lemire.me/blog/2026/07/25/memory-level-parallelism-amd-is-the-king/</link>
					<comments>https://lemire.me/blog/2026/07/25/memory-level-parallelism-amd-is-the-king/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sat, 25 Jul 2026 15:07:52 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22742</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/07/Bh4a1-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />When your program asks for memory that is not in cache, the processor has to go to RAM. That trip costs on the order of 100 nanoseconds. On a 3 GHz core, that is about 300 cycles of doing nothing. Memory latency has not improved in ten years. The 2016 Broadwell answers a random access &#8230; <a href="https://lemire.me/blog/2026/07/25/memory-level-parallelism-amd-is-the-king/" class="more-link">Continue reading <span class="screen-reader-text">Memory-level parallelism: AMD is the king</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/07/Bh4a1-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>When your program asks for memory that is not in cache, the processor has to go to RAM. That trip costs on the order of 100 nanoseconds. On a 3 GHz core, that is about 300 cycles of doing nothing.</p>
<p>Memory latency has not improved in ten years. The 2016 Broadwell answers a random access in 100 ns. The 2025 Turin, with DDR5-6400 and every advantage of a decade of progress, takes 140 ns. It got worse.</p>
<p>The good news is that a modern core does not have to sit still. It can issue a second request before the first one comes back, and a third, and a tenth. The number of requests a single core can keep in flight is its memory-level parallelism. It is one of the most important numbers in software performance, and one of the least advertised: you will not find it on a spec sheet.</p>
<p>Thankfully, memory-level parallelism has improved a lot. To measure it, I use my <a href="https://github.com/lemire/testingmlp">testingmlp</a> benchmark. The idea is a pointer chase. We build a 1 GiB array containing a single random cycle covering every element: each element holds the index of the next. Following the cycle is inherently serial. Each load has to complete before you know the address of the next one, so a single chase measures pure memory latency and nothing else. Then we run several such chases at once, from different starting points on the same cycle. We call these <em>lanes</em>. With two lanes, the core has two independent loads to work on. With twenty, twenty. We increase the number of lanes and watch the throughput. When adding a lane stops helping, we have found the limit. As my metric, I use the total estimated bandwidth.</p>
<p>I ran experiments on the Amazon cloud (AWS). The bandwidth shape is the same everywhere: a steep, nearly linear climb as we add lanes, then a knee, then a plateau. <br />
<a href="http://lemire.me/blog/wp-content/uploads/2026/07/mlp-curves.png"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/07/mlp-curves-723x1024.png" alt="" width="660" height="935" class="alignnone size-large wp-image-22745" srcset="https://lemire.me/blog/wp-content/uploads/2026/07/mlp-curves-723x1024.png 723w, https://lemire.me/blog/wp-content/uploads/2026/07/mlp-curves-212x300.png 212w, https://lemire.me/blog/wp-content/uploads/2026/07/mlp-curves-768x1087.png 768w, https://lemire.me/blog/wp-content/uploads/2026/07/mlp-curves-1085x1536.png 1085w, https://lemire.me/blog/wp-content/uploads/2026/07/mlp-curves.png 1159w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>How did it evolve over time? Intel went from 10 to 30, meaning that a single Intel core can sustain 30 memory requests at once in practice. AMD went from 15 to 58. Graviton went from 6 to 19.</p>
<p>Intel was flat for a long time. Broadwell and Cascade Lake both sit at 10 concurrent misses. Ice Lake doubled it to 20. Granite Rapids is at 30. Intel has roughly tripled in a decade, with all the gain arriving in the last two generations.</p>
<p>AMD started ahead and stayed ahead, then jumped. Naples was already at 15 in 2018, when Intel was at 10. Milan reached 22. And then Turin does something different in kind: 58 concurrent cache lines from a single core.</p>
<p>Graviton 1 was a toy: 6 concurrent misses. Graviton 2 doubled it, Graviton 3 went to 17, and then Graviton 4 essentially stood still at 18. Graviton 5 only reaches 19. But look at the latency panel: since 2017, Graviton 5 is the only chip in this entire collection that made a random access <em>faster</em> than its predecessor. <a href="https://www.amazon.science/blog/graviton5s-improved-design-increases-speed-and-energy-efficiency-beyond-moores-law">AWS advertised better DRAM latency for Graviton 5</a>, and that claim holds up.</p>
<p>So who wins? On bandwidth and memory-level parallelism, it is AMD, and it is not close. The Zen 5 core in the <code>m8a</code> instances sustains 58 concurrent cache-line fetches and 24.5 GiB/s of random-access throughput from one core. AMD is roughly twice as fast as Intel.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Instance</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Year</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Processor</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Memory</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Latency</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Peak BW</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Concurrency</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">m8i.large</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">2025</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">Xeon 6975P-C, Granite Rapids</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">DDR5-7200</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">133 ns</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">13.3 GiB/s</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">30</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">m8a.large</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">2025</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">EPYC 9R45, Zen 5 (Turin)</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">DDR5-6400</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">142 ns</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">24.5 GiB/s</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">58</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">m9g.large</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">2026</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">Graviton 5, Neoverse V3</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">DDR5-8800</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">96 ns</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">12.0 GiB/s</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">19</td>
</tr>
</tbody>
</table>
<p><em>The raw output, the system information from each machine, and the scripts are <a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/07/25">in the usual place</a>.</p>
<p><span data-offset-key="bfh9p-0-0">Note that Apple Silicon </span></em><span data-offset-key="bfh9p-1-0"><a href="https://lemire.me/blog/2025/07/09/memory-level-parallelism-apple-m2-vs-apple-m4/" rel="noopener noreferrer nofollow" target="_blank" role="link" class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1inkyih r-rjixqe r-1ddef8g r-tjvw6i r-1loqt21">does even better, </a></span><em><span data-offset-key="bfh9p-2-0">but it is another category.</span></em></p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/07/25/memory-level-parallelism-amd-is-the-king/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Does a PhD Pay Off?</title>
		<link>https://lemire.me/blog/2026/07/24/does-a-phd-pay-off/</link>
					<comments>https://lemire.me/blog/2026/07/24/does-a-phd-pay-off/#respond</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Fri, 24 Jul 2026 20:13:57 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22737</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/07/kfKMS-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Every week, I discuss with people who want to get a PhD. For years, I have been advising people not to pursue a PhD. It may come as a surprise to some. You would expect people with a PhD to earn more money. Individuals who complete doctorates tend to have higher cognitive abilities and greater &#8230; <a href="https://lemire.me/blog/2026/07/24/does-a-phd-pay-off/" class="more-link">Continue reading <span class="screen-reader-text">Does a PhD Pay Off?</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/07/kfKMS-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Every week, I discuss with people who want to get a PhD. For years, I have been advising people not to pursue a PhD. It may come as a surprise to some.</p>
<p>You would expect people with a PhD to earn more money. Individuals who complete doctorates tend to have higher cognitive abilities and greater motivation. But smarter people tend to earn more, period.</p>
<p>So do people with a PhD earn more?</p>
<p>Historically, PhD holders earn more, but the bulk of the observed advantage is concentrated among those who get a professorship after the PhD. And there is no certain path from the PhD to a professorship. We have been producing many more PhDs than we have professorship, for decades. And the disparity is ever growing.</p>
<p>When I entered university at the beginning of the 1990s, about 0.5% of the Canadian population had a PhD. This has nearly tripled today, and it is fast increasing. Something of the order of one person out of 80 has a PhD. Comparatively, there is roughly one professor or university-level instructor per 900 people. With a fast aging population, we simply do not need many more professors and instructors than we already have.</p>
<p>There are specific fields where some jobs are difficult to get without a PhD. Machine learning is one such example. Many people in the industry have a PhD, and they tend to select those who also have a PhD. Further, there is a somewhat direct relationship between the work you might do during your PhD, if you are any good, and the actual work you might do later. It is much less clear in a lot of other disciplines.</p>
<p>The most significant economic cost of a PhD is not tuition but the years of delayed full-time earnings and career progression. In the tech industry, it is typical to award half a year of experience for each year spent on a PhD. This means that even though the individual starting with a PhD might earn more starting out, they are not necessarily getting a higher lifetime income. </p>
<p>Benjamin et al. (2025) find that the early-career benefits a PhD can be effectively zero:</p>
<blockquote>
<p>In the short run, pursuing a PhD entails substantial opportunity costs. Early-career earnings for PhD graduates are significantly lower than those of individuals with master’s or professional degrees, reflecting prolonged enrolment and delayed entry into the labour market. These costs are especially high for non-completers, particularly those who exit the program after several years without earning a credential. Over the lifecycle, earnings do eventually recover (and surpass those of bachelor’s and master’s graduates) but only under specific conditions. The most favourable long-run outcomes are concentrated among those who secure academic employment and remain in full-time work late into life. This “double premium,” combining higher earnings and longer careers, plays a central role in shaping the average return to a PhD. Outside academia, PhD holders resemble master’s graduates in both earnings and employment patterns.</p>
</blockquote>
<p>Thus, the financial case for a PhD is narrower than people assume. If you fail to get a professorship, or you want an early retirement, you may very well end up with a poor outcome. And it is not getting better over time. </p>
<p><em>References</em></p>
<ul>
<li>Altonji, J. G., &amp; Zhu, Z. (2025). Returns to specific graduate degrees: Estimates using Texas administrative records (NBER Working Paper No. 33530). National Bureau of Economic Research. https://www.nber.org/papers/w33530</li>
<li>Benjamin, D., Miloucheva, B., &amp; Vigezzi, N. (2025). The opportunity cost of a PhD: Spending your twenties (Working Paper No. 802). University of Toronto, Department of Economics. https://www.economics.utoronto.ca/public/workingPapers/tecipa-802.pdf</li>
<li>Cooper, P. Is grad school worth it? A comprehensive return on investment analysis. Foundation for Research on Equal Opportunity. https://freopp.org/whitepapers/is-grad-school-worth-it-a-comprehensive-return-on-investment-analysis/</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/07/24/does-a-phd-pay-off/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>From Institutions to Individuals: the White House Report on Revitalizing U.S. Scientific Leadership</title>
		<link>https://lemire.me/blog/2026/07/22/from-institutions-to-individuals-the-white-house-report-on-revitalizing-u-s-scientific-leadership/</link>
					<comments>https://lemire.me/blog/2026/07/22/from-institutions-to-individuals-the-white-house-report-on-revitalizing-u-s-scientific-leadership/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Wed, 22 Jul 2026 14:32:18 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22727</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/07/Capture-decran-le-2026-07-22-a-10.24.40-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />In 1945, Vannevar Bush published a report entitled Science: The Endless Frontier. His thesis was that prosperity follows from basic research. The report was highly influential in the United States and elsewhere. It led to the creation of an entirely new government bureaucracy. With this report, Bush popularized the linear model of innovation: innovation (such &#8230; <a href="https://lemire.me/blog/2026/07/22/from-institutions-to-individuals-the-white-house-report-on-revitalizing-u-s-scientific-leadership/" class="more-link">Continue reading <span class="screen-reader-text">From Institutions to Individuals: the White House Report on Revitalizing U.S. Scientific Leadership</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/07/Capture-decran-le-2026-07-22-a-10.24.40-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>In 1945, Vannevar Bush published a report entitled <a href="https://www.govinfo.gov/content/pkg/GOVPUB-PR32_400-e7966ee70a4f7b47f862431c9776f727/pdf/GOVPUB-PR32_400-e7966ee70a4f7b47f862431c9776f727.pdf">Science: The Endless Frontier</a>. His thesis was that prosperity follows from basic research. The report was highly influential in the United States and elsewhere. It led to the creation of an entirely new government bureaucracy.</p>
<p>With this report, Bush popularized the linear model of innovation: innovation (such as medical cures) flows sequentially from basic research to applied research to development to production and diffusion. Grow basic research, and the rest will follow.</p>
<p>When Bush wrote his report, basic research was not usually supported directly by the state. We did not have a large basic research infrastructure. And yet, the West had just lived through an unprecedented period of rapid scientific progress: the theory of evolution, electromagnetism, radio communication, special and general relativity, quantum mechanics, nuclear technology, rockets, the combustion engine, and more. We would get the invention of the transistor only two years after Bush’s report. We also did not have today’s peer-review mechanism.</p>
<p>Even though Bush’s report has been viewed as a piece of genius that unlocked a golden era of scientific prosperity, I believe that the linear model of innovation is hopelessly naïve. I believe the thesis that a large bureaucracy delivering funding to other bureaucracies (such as universities) is how we get innovation is absurd. Except perhaps in the domain of computing (“bits”), we have been largely stagnant technologically since about the 1970s. So Bush’s model failed over time. To be clear, it might have worked for a while by encouraging more young people to study engineering and science. It may also have shone a favorable light on a few enterprising professors who got to promote useful ideas.</p>
<p>If you visit a research lab today in a leading university, what you are most likely to see is a boring bureaucracy that caters to whatever is politically favorable at the moment—a bureaucracy that plays it safe and avoids controversy. You see young people seeking well-paid jobs, going through the motions with often little genuine interest in, say, curing cancer. We have never published so many research papers—the volume has been growing exponentially ever since Bush wrote his report—but it is doubtful that this is how technological breakthroughs are achieved.</p>
<p>The evidence is overwhelming that shoddy science is widespread. We have a severe reproducibility crisis: if you redo an experiment (even a highly cited one), you are likely to fail to reproduce the results. This affects psychology, medicine, and many other fields. The system does not particularly care because the incentives to get things right are not there. As long as the work is politically aligned, solidity of the results seems secondary.</p>
<p>There was a TV show (<em>The Big Bang Theory</em>) where the main character, Sheldon Cooper—an awkward genius—gets to work on crazy ideas. That is how Bush imagined it: fund young people like Sheldon Cooper, and you will get extraordinary breakthroughs. In the real world, Sheldon would not get very far on campus. I have met misfits like him. When they are incapable of playing the political game, the system crushes them. But even if that were not the case, extraordinary intelligence needs to be applied to the right problems to be of value. You could have a ChatGPT that is brighter than any of us in every possible way, and it could still be deployed simply to fill out forms faster and better than we do—it may not cure cancer.</p>
<p>The American government has just released what might be considered an update to Bush’s report. Michael Kratsios wrote a report entitled <a href="https://www.whitehouse.gov/wp-content/uploads/2026/07/Science-A-New-Golden-Age.pdf">Science, A New Golden Age</a>. The report states outright that the linear model no longer holds. It states what I have argued for vehemently: innovation is not a linear process. Take large-language models, for example, which can be used by engineers and scientists to further their research. I have also argued that the success of large-language models today has as much to do with the users as with the researchers.</p>
<p>At this point, some people engage in the following type of rhetoric: if we had not invented calculus, we would not have AI today; therefore, calculus caused AI. But you could also say that the subsidized nail factory in the Soviet Union, which made overpriced and bad nails, was necessary to hold Landau’s house together, and that without those nails we would not have the theory of Landau levels. The causality argument goes in all directions.</p>
<p>Innovation is the result of a complex system. We see that the United States and, more recently, China are innovative countries. In 2026, you do not go to France for the latest advances. The evidence is overwhelming that scientific and technological progress depends as much on culture as on anything else. It is not something to be managed by bureaucrats.</p>
<p>One of the cultural ingredients that seems essential is meritocracy. You must put the people who are good at building on top of your hierarchy. This does not happen magically. You need a set of incentives in which rewarding the wrong people is costly.</p>
<p>What does Kratsios propose? Many interesting ideas that, I expect, could renew our culture. He proposes to break out of the Cold War–era funding model. Today, the research funding mechanism is centered around the government giving money to the university bureaucracy. The grant might be in the name of one professor, but the recipient is still the university. In the new model, instead of funding universities, the government would assign money directly to individuals in various ways (short grants, prizes, and so forth). This would shift power away from administrators toward individuals who know how to get things done. It would also neutralize some of the political power of the current mandarin class of scientists who control access to the top positions.</p>
<p>The report recommends restoring permissionless innovation. It is sometimes poorly understood how limited the system has become. I once had a graduate student undertake interviews with practitioners. This required an ethics approval which, in her case, took a few months to obtain. Again, the system has built up political structures that seek to block innovation it does not like. They need to be torn down, the sooner the better.</p>
<p>The report has many other interesting recommendations. One that I particularly like is an AI-guided agenda. We need to hook up our brand-new AIs to experimental devices. We are not going to cure aging with chatbots. We need experiments on a massive scale.</p>
<p>Will Kratsios’s vision move from report to reality? History shows that cultural and institutional change is never easy. Yet the stakes could not be higher. By embracing meritocracy, permissionless innovation, and ambitious AI-augmented experimentation, we have a genuine chance to escape decades of stagnation and rekindle the spirit of discovery that once defined the West. The opportunity is before us. It must not be squandered.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/07/22/from-institutions-to-individuals-the-white-house-report-on-revitalizing-u-s-scientific-leadership/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Using AI to build your own software</title>
		<link>https://lemire.me/blog/2026/07/16/using-ai-to-build-your-own-software/</link>
					<comments>https://lemire.me/blog/2026/07/16/using-ai-to-build-your-own-software/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Thu, 16 Jul 2026 20:00:21 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22724</guid>

					<description><![CDATA[A few years ago, a friend of mine was stuck. He needed to quickly process over a hundred high-quality images according to a complicated sequence. He was using Photoshop, but it was going to take him days. Initially, he asked for my help, could I do the manual labor? I spent 15 minutes writing a &#8230; <a href="https://lemire.me/blog/2026/07/16/using-ai-to-build-your-own-software/" class="more-link">Continue reading <span class="screen-reader-text">Using AI to build your own software</span></a>]]></description>
										<content:encoded><![CDATA[<p>A few years ago, a friend of mine was stuck. He needed to quickly process over a hundred high-quality images according to a complicated sequence. He was using Photoshop, but it was going to take him days. Initially, he asked for my help, could I do the manual labor? I spent 15 minutes writing a script with ImageMagick that processed all the images in seconds, but in a completely automated way.</p>
<p>When my kids were young, instead of helping them study algebra and grammar, I wrote small JavaScript apps for them to use. I built a small collection of educational tools.</p>
<p>The great success story of AI for me is exactly this: AI helps you write your own tools, faster and better.</p>
<p>Last night, I was struggling with videos I had to process. I wanted to add nice subtitles to them. There are software applications for that, but they require manual labor and don’t always work the way I want them to. After a long night, I had an insight: why don’t I ask my AI to help build the automated tool I need? So I did—and it worked really well, very quickly. So what’s the lesson here? Maybe that we should spend more time building our own software for our own personal use than we used to.</p>
<p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/Falc7QnHy7k?si=REbbbCN_EYdOgdaL" 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="allowfullscreen"></iframe></p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/07/16/using-ai-to-build-your-own-software/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>X just gave us an interface that AI agents can use. I pointed it at my own posts.</title>
		<link>https://lemire.me/blog/2026/07/11/x-just-gave-us-an-interface-that-ai-agents-can-use-i-pointed-it-at-my-own-posts/</link>
					<comments>https://lemire.me/blog/2026/07/11/x-just-gave-us-an-interface-that-ai-agents-can-use-i-pointed-it-at-my-own-posts/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sat, 11 Jul 2026 19:53:17 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22714</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/07/image-5-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />I have been on X for a long time. Like most people who post regularly, I have a gut feeling for what might interest people. I post in the morning. Longer posts seem to do better. But gut feelings are not measurements. And until recently, digging into your own posting data meant either clicking around &#8230; <a href="https://lemire.me/blog/2026/07/11/x-just-gave-us-an-interface-that-ai-agents-can-use-i-pointed-it-at-my-own-posts/" class="more-link">Continue reading <span class="screen-reader-text">X just gave us an interface that AI agents can use. I pointed it at my own posts.</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/07/image-5-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>I have been on X for a long time. Like most people who post regularly, I have a gut feeling for what might interest people. I post in the morning. Longer posts seem to do better.</p>
<p>But gut feelings are not measurements. And until recently, digging into your own posting data meant either clicking around the web UI or writing custom scripts. Neither is particularly friendly when you want to ask <em>ad hoc</em> questions with an AI assistant.</p>
<p>X recently launched hosted <a href="https://modelcontextprotocol.io/">MCP</a> servers: official endpoints that AI tools can connect to. MCP is a protocol for plugging tools into language models: the model can search posts, manage bookmarks, fetch trends, and so on. In practice, I connected an AI coding agent to the X MCP server and simply started asking questions about my account.</p>
<p>I spent a session exploring about two months of my own activity. Here is what I found interesting.</p>
<p>Over roughly sixty days (mid-May through mid-July 2026), I published on the order of 435 posts that were not pure retweets of other people—mostly a mix of original posts, replies, and a few X Articles. The agent pulled them through the MCP tools, kept the public metrics (likes, views, reposts), and ran simple analyses.</p>
<p>I asked for every post to be binned by local hour of day (America/Toronto, Eastern time), and for each hour: how many posts, and the min / median / max view count.</p>
<p>My posting is heavily skewed toward the morning:</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Local hour</th>
<th style="text-align: right;">Posts</th>
<th style="text-align: right;">Median views</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">08:00–08:59</td>
<td style="text-align: right;">45</td>
<td style="text-align: right;">454</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">09:00–09:59</td>
<td style="text-align: right;">58</td>
<td style="text-align: right;">1,067</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">10:00–10:59</td>
<td style="text-align: right;">30</td>
<td style="text-align: right;">194</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">11:00–11:59</td>
<td style="text-align: right;">42</td>
<td style="text-align: right;">284</td>
</tr>
</tbody>
</table>
<p>The 9 a.m. hour is both my busiest and, among busy hours, my strongest by median views. The overall median across all hours was only about 188 views, so most of what I write is quiet. The distribution is heavy-tailed: a few posts get tens or hundreds of thousands of impressions; the rest are background noise.</p>
<p>I then binned posts by character length in steps of 25 characters (using the text as returned by the API, including short <code>t.co</code> URLs).</p>
<p>The bulk of my writing is short, often a reply of a few dozen characters:</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: right;">Characters</th>
<th style="text-align: right;">Posts</th>
<th style="text-align: right;">Median likes</th>
<th style="text-align: right;">Max likes</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="text-align: right;">0–25</td>
<td style="text-align: right;">44</td>
<td style="text-align: right;">1</td>
<td style="text-align: right;">195</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">25–50</td>
<td style="text-align: right;">87</td>
<td style="text-align: right;">1</td>
<td style="text-align: right;">60</td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">50–75</td>
<td style="text-align: right;">69</td>
<td style="text-align: right;">0</td>
<td style="text-align: right;">98</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">75–100</td>
<td style="text-align: right;">51</td>
<td style="text-align: right;">1</td>
<td style="text-align: right;">61</td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">100–125</td>
<td style="text-align: right;">33</td>
<td style="text-align: right;">1</td>
<td style="text-align: right;">32</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">…</td>
<td style="text-align: right;"></td>
<td style="text-align: right;"></td>
<td style="text-align: right;"></td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">175–200</td>
<td style="text-align: right;">12</td>
<td style="text-align: right;">5</td>
<td style="text-align: right;">58</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">200–225</td>
<td style="text-align: right;">17</td>
<td style="text-align: right;">4</td>
<td style="text-align: right;">456</td>
</tr>
<tr style="background: #ffffff;">
<td style="text-align: right;">275–300</td>
<td style="text-align: right;">19</td>
<td style="text-align: right;">4</td>
<td style="text-align: right;">385</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="text-align: right;">300–325</td>
<td style="text-align: right;">48</td>
<td style="text-align: right;">46.5</td>
<td style="text-align: right;">470</td>
</tr>
</tbody>
</table>
<p>Under about 175 characters, the median stays at zero or one like. Around full-length posts (roughly the old 280-character regime and a bit beyond), engagement jumps. The 300–325 character band is where a large fraction of my “serious” posts live, and the median likes there are an order of magnitude higher than for short replies.</p>
<p>I also asked the AI to identify the posts that had the most likes, the following types of posts were liked:</p>
<ul>
<li>AI vs. “experts” claiming models are nowhere near human intelligence</li>
<li>Go adding SIMD-style data-parallelism to the standard library</li>
<li>SIMD-accelerated data processing talks and library notes (JSON, string→integer maps, vulnerability-report fatigue)</li>
<li>Nvidia hardware, university AI-cheating, C++ contracts</li>
</ul>
<p>The interesting part is the workflow. I did not export a CSV by hand and open Excel. I asked an agent, connected to X’s MCP server. If AI agents can do this for one account’s metrics, they can do it for bug trackers, logs, paper drafts, and codebases.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/07/11/x-just-gave-us-an-interface-that-ai-agents-can-use-i-pointed-it-at-my-own-posts/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Chatting with an AI Won’t Make You a Top Programmer</title>
		<link>https://lemire.me/blog/2026/06/21/chatting-with-ai-wont-make-you-a-top-programmer/</link>
					<comments>https://lemire.me/blog/2026/06/21/chatting-with-ai-wont-make-you-a-top-programmer/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sun, 21 Jun 2026 17:51:16 +0000</pubDate>
				<category><![CDATA[]]></category>
		<category><![CDATA[essay]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22706</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/06/image-4-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />When I was a kid, most people did not know how to type. We took typing class. The final exam was a speed test: words per minute. Today, you will not impress anyone by saying you can type. In fact, cursive writing is fading. Kids increasingly cannot read or write it. We type constantly. We &#8230; <a href="https://lemire.me/blog/2026/06/21/chatting-with-ai-wont-make-you-a-top-programmer/" class="more-link">Continue reading <span class="screen-reader-text">Chatting with an AI Won’t Make You a Top Programmer</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/06/image-4-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p dir="auto">When I was a kid, most people did not know how to type. We took typing class. The final exam was a speed test: words per minute. Today, you will not impress anyone by saying you can type. In fact, cursive writing is fading. Kids increasingly cannot read or write it. We type constantly. We forget how many skills are learned, and how often some of these skills have faded.</p>
<p dir="auto">But not everything fades. Socrates would be immensely popular today as a teacher. I still buy and recommend paper books.</p>
<p dir="auto">Is reading and writing code more like Socrates, or more like cursive writing? There are clear signs that code could become like cursive writing. This year, I have met more than one student who could use AI to build an application but could not read or write code. It is not new. Software has long had non-technical people who describe what they built or designed. In fact, in much of the industry, the standard view was that once you had a university degree, you no longer coded. Coding was for monkeys or low-status employees. Top engineers paid a million dollars a year at Google or Meta know how to write code. They often read and write assembly and TypeScript. They know it all.</p>
<p dir="auto">Why the discrepancy?</p>
<p dir="auto">We pay an engineer a million dollars because he understands concepts few others grasp. He outruns others because he sees the problems more deeply. Reading and writing large amounts of code is part of how you gain those insights. Chatting with an AI will not make you a top 1% programmer. In the future, top engineers might read more code than anyone could in the past. These engineers will not be everywhere, but they will pack a punch. “But Daniel, people say programming is solved. Why read or write code?” Be careful with your models. When television arrived, some predicted it would replace the university lecturer. In some respects the model was correct, yet it did not happen. The lecturer’s job was never to deliver a TV show. The Google engineer paid a million dollars was never a machine that produces code. Nobody actually wants code, any more than they want raw text.</p>
<p dir="auto">In fact, I predict a bifurcation in the tooling. The best engineers will work with tools that maximize their understanding of the code. I believe that reading and writing code, at a high level, is more like studying Socrates than like cursive writing. It is a necessary mental labor that does not become obsolete just because we have better tools for generating output.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/06/21/chatting-with-ai-wont-make-you-a-top-programmer/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>Parsing JSON at compile time with C++26 static reflection</title>
		<link>https://lemire.me/blog/2026/06/14/parsing-json-at-compile-time-with-c26-static-reflection/</link>
					<comments>https://lemire.me/blog/2026/06/14/parsing-json-at-compile-time-with-c26-static-reflection/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sun, 14 Jun 2026 14:59:44 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22702</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/06/AdxWs-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Suppose that you have a configuration file in JSON. Something like this: { "width": 1920, "height": 1080, "fullscreen": true, "title": "My Game", "volume": 0.8 } Normally you ship this file alongside your program, open it at startup, read it, and parse it. That is a lot of work for data that never changes. What if &#8230; <a href="https://lemire.me/blog/2026/06/14/parsing-json-at-compile-time-with-c26-static-reflection/" class="more-link">Continue reading <span class="screen-reader-text">Parsing JSON at compile time with C++26 static reflection</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/06/AdxWs-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Suppose that you have a configuration file in JSON. Something like this:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000; font-weight: bold;">{</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">"width"</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1920</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">"height"</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1080</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">"fullscreen"</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">true</span><span style="color: #000; font-weight: bold;">,</span>
<span style="color: #f8f8f8;">  </span><span style="color: #204a87; font-weight: bold;">"title"</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #f8f8f8;"> </span><span style="color: #4e9a06;">"My Game"</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">"volume"</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0.8</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>Normally you ship this file alongside your program, open it at startup, read it, and parse it. That is a lot of work for data that never changes. What if the file is fixed at build time? Could the compiler read it, parse it, and bake the result directly into the executable as a constant?</p>
<p>With C++26, the answer is yes. We need two new ingredients, all of which are usable right now with the latest version of the GCC compiler (16).</p>
<ol>
<li><code>#embed</code> to pull the file into the program at compile time,</li>
<li>A software library supporting <em>static reflection</em> like simdjson.</li>
</ol>
<p>Let me show you how far we can take this.</p>
<p>The new <code>#embed</code> directive reads a file and expands it into a comma-separated list of byte values. To read the file <code>data.json</code> at compile time and keep it around as a constant, we write:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">constexpr</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">const</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">char</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">json_data</span><span style="color: #000; font-weight: bold;">[]</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #8f5902; font-style: italic;">#embed "data.json"</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0</span>
<span style="color: #000; font-weight: bold;">};</span>
</code></pre>
</div>
<p>I use <code>constexpr</code> because I want the compiler to be allowed to inspect these bytes during constant evaluation. The trailing <code>, 0</code> simply appends a null terminator, so the array can be treated as an ordinary C string.</p>
<p>There is no run-time input/output of any kind. The bytes are part of the program.</p>
<p>But embedded bytes are not yet useful by themselves. What I really want is a typed C++ object. In my example, the target type is this configuration struct:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">struct</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">Window</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">  </span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;">         </span><span style="color: #000;">width</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">  </span><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;">         </span><span style="color: #000;">height</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">  </span><span style="color: #204a87; font-weight: bold;">bool</span><span style="color: #f8f8f8;">        </span><span style="color: #000;">fullscreen</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">std</span><span style="color: #ce5c00; font-weight: bold;">::</span><span style="color: #000;">string</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">title</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #f8f8f8;">  </span><span style="color: #204a87; font-weight: bold;">double</span><span style="color: #f8f8f8;">      </span><span style="color: #000;">volume</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #000; font-weight: bold;">};</span>
</code></pre>
</div>
<p>The traditional way to populate such a struct from JSON is to write, by hand, one line per field: read <code>"width"</code>, store it into <code>width</code>, read <code>"height"</code>, store it into <code>height</code>, and so on. It is tedious. And because it runs at startup, a malformed file becomes a run-time error, discovered by your users rather than by you.</p>
<p>Recent versions of <a href="https://github.com/simdjson/simdjson">simdjson</a> can parse JSON <em>at compile time</em> using C++26 static reflection. The entry point is <code>simdjson::compile_time::parse_json</code>, and it does something I still find slightly magical: it reads the JSON and, from the keys it finds, and synthesises the struct type for you.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #8f5902; font-style: italic;">#define SIMDJSON_STATIC_REFLECTION 1</span>
<span style="color: #8f5902; font-style: italic;">#include</span><span style="color: #f8f8f8;"> </span><span style="color: #8f5902; font-style: italic;">"simdjson.h"</span>
<span style="color: #204a87; font-weight: bold;">constexpr</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">const</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">char</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">json_data</span><span style="color: #000; font-weight: bold;">[]</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #8f5902; font-style: italic;">#embed "data.json"</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0</span>
<span style="color: #000; font-weight: bold;">};</span>
<span style="color: #204a87; font-weight: bold;">constexpr</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">auto</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">window</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">simdjson</span><span style="color: #ce5c00; font-weight: bold;">::</span><span style="color: #000;">compile_time</span><span style="color: #ce5c00; font-weight: bold;">::</span><span style="color: #000;">parse_json</span><span style="color: #ce5c00; font-weight: bold;">&lt;</span><span style="color: #000;">json_data</span><span style="color: #ce5c00; font-weight: bold;">&gt;</span><span style="color: #000; font-weight: bold;">();</span>
</code></pre>
</div>
<p>The variable <code>window</code> is a value computed entirely by the compiler. Its type is generated from the document: it has a <code>width</code> and a <code>height</code> (both 64-bit integers), a <code>bool</code> <code>fullscreen</code>, a <code>double</code> <code>volume</code>, and a <code>title</code>. From here on I write <code>window.width</code> and it behaves like any ordinary field.</p>
<p>How do I know the parsing really happened at compile time? Because I can assert things about the result that the compiler must check before the program even exists:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">static_assert</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">window</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">width</span><span style="color: #f8f8f8;">      </span><span style="color: #ce5c00; font-weight: bold;">==</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1920</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #204a87; font-weight: bold;">static_assert</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">window</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">height</span><span style="color: #f8f8f8;">     </span><span style="color: #ce5c00; font-weight: bold;">==</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1080</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #204a87; font-weight: bold;">static_assert</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">window</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">fullscreen</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">==</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87;">true</span><span style="color: #000; font-weight: bold;">);</span>
</code></pre>
</div>
<p>If I corrupt the JSON — delete a brace, misspell <code>true</code>, leave a trailing comma — the program no longer compiles, and the error points at the <code>parse_json</code> line. The broken file is caught at build time, on my machine, instead of at startup on someone else&#8217;s.</p>
<p>Because <code>window</code> is a genuine compile-time constant, any computation over it is a constant too. Consider this function:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">int</span><span style="color: #f8f8f8;">  </span><span style="color: #000;">screen_area</span><span style="color: #000; font-weight: bold;">()</span><span style="color: #f8f8f8;">   </span><span style="color: #000; font-weight: bold;">{</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">window</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">width</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">window</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">height</span><span style="color: #000; font-weight: bold;">;</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>Compiled with <code>-O3</code>, there is no multiplication, no field access, and certainly no parsing left — only the answers, as immediate values (here on my macBook):</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #f57900;">screen_area:</span><span style="color: #f8f8f8;">    </span><span style="color: #000;">mov</span><span style="color: #f8f8f8;">  </span><span style="color: #000;">w0</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">#0</span><span style="color: #000;">xa400</span><span style="color: #f8f8f8;">        </span><span style="color: #8f5902; font-style: italic;">// 0x1fa400 = 2073600</span>
<span style="color: #f8f8f8;">                </span><span style="color: #000;">movk</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">w0</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">#0</span><span style="color: #000;">x1f</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">lsl</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">#16</span>
<span style="color: #f8f8f8;">                </span><span style="color: #000;">ret</span>
</code></pre>
</div>
<p>The JSON has vanished from the binary. It was read and parsed exactly once, by the compiler, and all that survives is the number 2073600.</p>
<p>Because static reflection is so new, when building with GCC 16, you need to pass the flags <code>-std=c++26 -freflection</code>: the <code>-freflection</code> flag is necessary to activate compile-time reflection You must also set the simdjson macro <code>SIMDJSON_STATIC_REFLECTION=1</code> before importing the <code>simdjson.h</code>. It is a temporary safeguard.</p>
<p>The <a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/06/16">source code to reproduce these examples is available</a>.</p>
<p><em>Reference</em>: <a href="https://wg21.link/p2996">P2996 — Reflection for C++26</a> and <a href="https://simdjson.org">the simdjson library</a>.</p>
<p><em>Credit</em>: The simdjson implementation is joint work with Francisco Geiman Thiesen.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/06/14/parsing-json-at-compile-time-with-c26-static-reflection/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title>Sovereign</title>
		<link>https://lemire.me/blog/2026/06/09/22693/</link>
					<comments>https://lemire.me/blog/2026/06/09/22693/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Tue, 09 Jun 2026 18:39:32 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22693</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/06/Capture-decran-le-2026-06-09-a-14.39.13-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />The keyword in politics these days is ‘sovereign’. What few will admit is that it is effectively the adoption of the American strategy: Make America Great Again. In other words, reindustrialization of key sectors of the economy. The UK used to be a computing champion. Our chip designs (ARM) originated from the UK. Canada had &#8230; <a href="https://lemire.me/blog/2026/06/09/22693/" class="more-link">Continue reading <span class="screen-reader-text">Sovereign</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/06/Capture-decran-le-2026-06-09-a-14.39.13-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>The keyword in politics these days is ‘sovereign’.</p>
<p>What few will admit is that it is effectively the adoption of the American strategy: Make America Great Again. In other words, reindustrialization of key sectors of the economy. The UK used to be a computing champion. Our chip designs (ARM) originated from the UK. Canada had BlackBerry, everyone was using Canadian phones.</p>
<p>Like Canada, many countries have progressively slid into financialization. Huge banks and bank-related businesses, surrounded by emptied factories.</p>
<p>Part of it was the doing of economists who promoted globalization. We are going to make our best CPUs in Taiwan, because they have a comparative advantage (whatever that means).</p>
<p>Another part is the rise of the managerial class, or our version of the technocracy: the summum of the status game is to make PowerPoint presentations in a nice office. Everyone has 2 or 3 university degrees. And if you don’t have many degrees, what is wrong with you?</p>
<p>I think that this is breaking apart for a few reasons.</p>
<p>One of them is Trump. And I don’t mean bombastic statements, bad hair color or overly long ties… But rather the realization that globalization might leave your people economically better off for a time because they have cheap stuff… But it also leaves your people with few skills. You can fund a robotics factory near Montreal, and you’ll find 2000 people with robotics PhDs, but nobody actually knows how to build robots. The analogy is thus: salary is not everything (to the great chagrin of economists). I have left a much better paying job. My previous job meant that I had to sit in an office and do little concrete. I would have had a great and early retirement… but I would never have developed my skills nor would I have built anything. And that’s what we did at the country level. Great total compensation, but a dead-end skill-wise.</p>
<p>Another factor, I believe, is the COVID era (2020-2023) and its final outcome: empty offices, closed coffee shops. What happened at work was illegible. Lots of people in offices. Certainly, something important was happening. Entire businesses and government organizations have now migrated partially or entirely to a pajama party of some kind. Netflix in the middle of the workday is no longer a dream, but a reality.</p>
<p>Another element is educational misalignment. A country like Canada has the most schooled population in history. You cannot throw a rock without hitting someone with a PhD. Meanwhile, we are not making robots or microchips. You can’t even pay with your phone in the Montreal subway. It is a project for another decade, maybe. We are using a push strategy: push more people with degrees into the economy and you are going to get a fancier economy. Won&#8217;t work.</p>
<p>Finally, the AI breakthrough of 2022 is the final nail in the proverbial coffin. My country (Canada) claimed for decades that we were the AI powerhouse. All these PDFs online can’t lie, can they? Canada basically invited modern AI, didn’t it? We did. On paper. On paper we did a great many things. In practice? Few know how to build anything.</p>
<p>In a country like Canada, the population has not yet caught up. They blame the orange man for whatever trouble they see. And the politicians promise to do what they must: shower money to build tech sovereignty. It won’t work. They will try again. It won’t work again. The cycle makes things worse because it sustains a managerial class that is great at politics but terrible at building.</p>
<p>Meanwhile, you can’t escape preference falsification. People will use ChatGPT, Claude, Grok, Gemini. And if Elon produces robots, they’ll want them.</p>
<p>Trump will leave office in two years… Canada and the UK will still be flat-lined economically. The USA will still surge ahead.</p>
<p>Countries like Canada and the UK will have to realize that it is industry and know-how first. Build stuff and the wealth will follow. Stop the virtue signaling. Stop the credentialism. Build.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/06/09/22693/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>How much do amd64 microarchitecture levels help in Go?</title>
		<link>https://lemire.me/blog/2026/06/06/how-much-do-amd64-microarchitecture-levels-help-in-go/</link>
					<comments>https://lemire.me/blog/2026/06/06/how-much-do-amd64-microarchitecture-levels-help-in-go/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sat, 06 Jun 2026 20:25:19 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22682</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/06/Capture-decran-le-2026-06-06-a-16.18.20-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Our 64-bit Intel and AMD processors have evolved over decades. When you compile a Go program for a 64-bit Intel or AMD processor, the compiler targets, by default, a nearly 20-year-old instruction set. The binary that comes out runs on essentially any x64 chip, but it also leaves on the table every instruction that was &#8230; <a href="https://lemire.me/blog/2026/06/06/how-much-do-amd64-microarchitecture-levels-help-in-go/" class="more-link">Continue reading <span class="screen-reader-text">How much do amd64 microarchitecture levels help in Go?</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/06/Capture-decran-le-2026-06-06-a-16.18.20-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Our 64-bit Intel and AMD processors have evolved over decades. When you compile a Go program for a 64-bit Intel or AMD processor, the compiler targets, by default, a nearly 20-year-old instruction set. The binary that comes out runs on essentially any x64 chip, but it also leaves on the table every instruction that was added since 2003.</p>
<p>We often refer to <a href="https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels"><em>microarchitecture levels</em></a>. Each level bundles a set of instruction-set extensions that you can assume are present:</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Level</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Adds (roughly)</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><strong>v1</strong></td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">the original AMD64 baseline (SSE2)</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><strong>v2</strong></td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>popcnt</code>, SSE4.2</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><strong>v3</strong></td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">AVX2</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><strong>v4</strong></td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">AVX-512 (F/BW/DQ/VL)</td>
</tr>
</tbody>
</table>
<p>In my view, this ladder is already slightly obsolete. It was frozen around 2020, and the hardware has moved on. We would need to add the latest AVX-512 sub-extensions (VBMI, VBMI2, VNNI, BF16, FP16, VPOPCNTDQ, and so on), which recent server and consumer chips support but which <code>v4</code> does not require. While <code>v1</code> through <code>v4</code> are a useful common language, a realistic &#8220;use everything this CPU offers&#8221; target today would need at least a <code>v5</code>, and arguably the whole scheme should be replaced by finer-grained feature detection.</p>
<p>In any case, the Go toolchain exposes this <code>v1</code> through <code>v4</code> ladder via the <a href="https://go.dev/wiki/MinimumRequirements#amd64"><code>GOAMD64</code></a> environment variable. Setting <code>GOAMD64=v3</code> tells the compiler it may use everything up to and including AVX2. The default is <code>v1</code>, the lowest common denominator.</p>
<p>This raises an obvious question. If I take a real, performance-sensitive library and recompile it at each level, how much do I actually gain? I picked <a href="https://github.com/RoaringBitmap/roaring">Roaring Bitmaps</a>, a compressed bitset data structure used in databases and search engines.</p>
<p>A Roaring Bitmap stores a set of 32-bit integers. It splits the 32-bit space into chunks of 65,536 values, keyed by the high 16 bits, and stores each chunk in a <em>container</em> that holds only the low 16 bits. A container comes in one of three shapes, and the library always keeps whichever is smallest:</p>
<ul>
<li>an array container: a sorted list of 16-bit values, used when the chunk is sparse (a few thousand elements at most);</li>
<li>a bitmap container: a flat 8 KB bit vector (65,536 bits, one per possible value), used when the chunk is dense;</li>
<li>a run container: a list of <code>[start, length]</code> intervals, used when the set bits cluster into consecutive runs.</li>
</ul>
<p>I fetched the latest release of the library, then ran its own benchmark suite four times, once per level, collecting eight samples each. I did this on a single Intel Xeon Gold 6548N (Emerald Rapids, which supports all four levels, including AVX-512) under Go 1.26.2 and Roaring v2.18.2.</p>
<p>A <em>population count</em> (or <em>popcount</em>, also called the Hamming weight) is simply the number of bits set to 1 in a machine word. Roaring leans on it constantly: the cardinality of a bitmap container, how many values it holds, is the sum of the population counts of its 1024 64-bit words. Modern x86 chips have a dedicated <code>popcnt</code> instruction that does this in a single operation, but it only became available at the <code>v2</code> level (SSE4.2, 2008). Without it, the compiler has to fall back to a multi-instruction bit-twiddling sequence.</p>
<p>The clearest single result is population count: counting the number of set bits in a bitmap container. The <code>v1</code> baseline cannot use the <code>popcnt</code> instruction, so Go emits a software fallback. The moment we move to <code>v2</code>, <code>popcnt</code> becomes available and the time is cut almost in half:<a href="http://lemire.me/blog/wp-content/uploads/2026/06/popcount_levels.svg"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/06/popcount_levels.svg" alt="" width="660" height="660" class="alignnone size-large wp-image-22686" role="img" /></a></p>
<p>That is a 43% reduction, and it is free: no source change, just a compiler flag. Notice, though, that <code>v3</code> and <code>v4</code> do nothing more. A single <code>popcnt</code> instruction is already optimal; as far as the Go compiler is concerned, AVX2 and AVX-512 have nothing to add.</p>
<p>Population count is the easy win. What about the rest of the library?</p>
<p>Another clear win is building a container from a dense bitmap. The <code>FromDense array</code> benchmark takes a raw 8 KB bit vector and constructs the most compact container for it: it popcounts every word to learn the cardinality, then scans out the positions of the set bits. That word-at-a-time popcount-and-scan loop is exactly what the compiler can auto-vectorize once 256-bit registers are available, so the gains keep coming past <code>v2</code>:</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/06/fromdense_levels.svg"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/06/fromdense_levels.svg" alt="" width="660" height="660" class="alignnone size-large wp-image-22683" role="img" /></a></p>
<p><code>v2</code> already cuts 21% by using scalar <code>popcnt</code>/<code>tzcnt</code> instructions, and <code>v3</code> (AVX2) nearly doubles that to a 38% reduction. As with popcount, <code>v4</code> adds nothing.</p>
<p>Set operations show the same pattern. The <code>IntersectionCardinality</code> benchmark counts how many values two bitmaps have in common: for bitmap containers, it ANDs the words pairwise and population-counts the result, without ever materializing the intersection. Here <code>v2</code> does essentially nothing (the scalar <code>popcnt</code> is already in the inner loop), but <code>v3</code> lets the compiler widen the AND-and-count loop to 256-bit registers, cutting the time by 22%:<br />
<a href="http://lemire.me/blog/wp-content/uploads/2026/06/intersectcard_levels.svg"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/06/intersectcard_levels.svg" alt="" width="660" height="660" class="alignnone size-large wp-image-22684" role="img" /></a></p>
<p>Takeaways:</p>
<ol>
<li>On modern hardware, everyone should be using <code>v2</code> or better. The resulting binary will run in any data center and on any non-ancient laptop.</li>
<li>The <code>v3</code> level might be worth investigating.</li>
<li>The <code>v4</code> level should have helped in some of my benchmarks, but it did not. I suspect that the Go compiler is just not great at it.</li>
</ol>
<p>(Obviously: run your own benchmarks.)</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/06/06/how-much-do-amd64-microarchitecture-levels-help-in-go/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Embodied cognition and agentic AI</title>
		<link>https://lemire.me/blog/2026/05/28/embodied-cognition-and-agentic-ai/</link>
					<comments>https://lemire.me/blog/2026/05/28/embodied-cognition-and-agentic-ai/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Thu, 28 May 2026 23:04:35 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22669</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Gemini_Generated_Image_x5dd1px5dd1px5dd-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Where is your intelligence located? In your brain? It is a simplistic answer. A better model is that your intelligence is embodied. Consider a cook working at an expensive restaurant. He has all his favorite knives and cooking instructions, placed exactly where he wants them. His kitchen is part of his intelligence, of his skills. &#8230; <a href="https://lemire.me/blog/2026/05/28/embodied-cognition-and-agentic-ai/" class="more-link">Continue reading <span class="screen-reader-text">Embodied cognition and agentic AI</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Gemini_Generated_Image_x5dd1px5dd1px5dd-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Where is your intelligence located? In your brain?</p>
<p>It is a simplistic answer. A better model is that your intelligence is embodied.</p>
<p>Consider a cook working at an expensive restaurant. He has all his favorite knives and cooking instructions, placed exactly where he wants them. His kitchen is part of his intelligence, of his skills. The same cook working in your kitchen can probably cook better than you do, but he can&#8217;t reproduce the same meals he would prepare in his favorite kitchen.</p>
<p>We often assess computer programmers using whiteboard tests. It is an endless source of complaints. Programmers rightly point out that it forces them out of their element. They are just not as good when you take away their laptop. It is not an excuse, it is a real issue: you are cutting them off from part of what makes them so intelligent.</p>
<p>To sum it up, the model of intelligence as a brain in a jar, disconnected from anything else, is ridiculous.</p>
<p>If you accept the idea of embodied intelligence, then many actions that we view as a consequence of our intelligence are actually part of our intelligence. First and foremost, language. Our ability to talk or write to each other means that I am not limited by my own person. Have you ever heard of human beings isolated in small tribes making technological breakthroughs? Nah. Progress requires lots of people communicating together. Up until a few decades ago, progress required cities. Today I am less certain than it does, as I can more and more communicate with anyone in the world from anywhere. But language is still critical, we have not invented anything better. Similarly, having hands and the ability to build sophisticated tools (like laptops) allows us to extend our intelligence.</p>
<p>At the end of 2022, we got a breakthrough technology: ChatGPT. It built on several pre-existing ideas such as (large) language models, neural networks, and so forth. That&#8217;s the &#8216;GPT&#8217; part. But an important, if underappreciated, part of the breakthrough was the &#8216;Chat&#8217; component. Someone had the idea of connecting a large language model with a chat interface. Maybe this came naturally and obviously to people building this system, but it should not be assumed to be trivial or unimportant.</p>
<p>Language is a key component of our intelligence, and, thus, it makes sense that it would be pivotal for machine intelligence.</p>
<p>We embodied the AI software in a chat box.</p>
<p>The next step was what we call today &#8216;agentic AI&#8217;. We keep the chat box, but we add the ability for the AI software to interact with tools, and to make plans to use them. In effect, we give the AI more agency: it can do stuff and learn from the results as they happen. It is starting to resemble a human being with hands and tools.</p>
<p>I was talking with a colleague this week. My colleague is all in on the AI revolution. He uses his AI to help him write better and faster, and to get his data analysis done faster, without so much help from technical experts.</p>
<p>But my colleague was not aware of the agentic AI approach. I tried to explain on the phone. What does it mean to give the AI access to tools? Is this only about saving the effort of copying and pasting the AI&#8217;s response?</p>
<p><a href="https://youtu.be/wHNPkr44jD4?si=1264HYVSb0NaVBIR">I ended up making a video</a> where I start an AI in a shell within something called RStudio. It is an environment people use to program in R, to do data analysis. I don&#8217;t use R or RStudio, but thanks to the AI, I was able to build an entire climate research project in a few minutes, complete with the retrieval of the data from the web.</p>
<p>How did the AI do it? I recorded it. It tried a few things, initially struggling to download the data. At some point, it finds out that it needs new R packages, so it installs them, and once they are installed, it can proceed to generate figures, verifying that it works.</p>
<p>Agentic AI greatly extends machine intelligence by improving the embodiment of AI.</p>
<p>I believe that it is not yet understood as it should be.</p>
<p>In Montreal, the most established professor in the field of AI is Yoshua Bengio. He started his own non-trivial enterprise a few years ago (Element AI). His latest venture is Law Zero, which aims to create a Scientist AI. The first goal of this project is to build AI without the agentic component. It should be a disembodied AI that has no goal of its own, no agency.</p>
<p>I fear that Bengio suffers from what Kevin Kelly called Thinkism. Let me quote from Kelly&#8217;s 2008 essay.</p>
<blockquote>
<p>No intelligence, no matter how super duper, can figure out how human body works simply by reading all the known scientific literature in the world and then contemplating it. No super AI can simply think about all the current and past nuclear fission experiments and then come up with working nuclear fusion in a day. Between not knowing how things work and knowing how they work is a lot more than thinkism. There are tons of experiments in the real world which yields tons and tons of data that will be required to form the correct working hypothesis. Thinking about the potential data will not yield the correct data. Thinking is only part of science; maybe even a small part. (&#8230;) Thinkism is not enough. Without conducting experiments, building prototypes, having failures, and engaging in reality, an intelligence can have thoughts but not results. It cannot think its way to solving the world’s problems. (&#8230;) The Singularity is an illusion that will be constantly retreating — always “near” but never arriving. We’ll wonder why it never came after we got AI. Then one day in the future, we’ll realize it already happened. The super AI came, and all the things we thought it would bring instantly — personal nanotechnology, brain upgrades, immortality — did not come. Instead other benefits accrued, which we did not anticipate, and took long to appreciate. Since we did not see them coming, we look back and say, yes, that was the Singularity.</p>
</blockquote>
<p>I believe that University professors are especially prone to thinkism. They view intelligence as being centered on what is happening in their brain. When you live in an ivory tower, it is easy to dismiss the real world as the core source of intelligence. Further, they are often people who did quite well in school where thinkism is naturally prevalent.</p>
<p>I have been a professor most of my life. However, I tire quickly of talking with other professors. What I most enjoy is working with people who have new tools that they apply in the real world. Unsurprisingly, I spent most of my time working with software that people deploy in the real world.</p>
<p>What Kelly is saying is that a high degree of intelligence is not enough to do much of anything. The real world is not the final stage of your thinking process. It is maybe the most important part of it.</p>
<p>And thus, when you connect your AI with the real world, giving it the ability of running experiments (as virtually all software developers do today), you get impressive results that go much beyond what AI software can do on its own.</p>
<p>Agency is not a feature. Agency is primary.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/05/28/embodied-cognition-and-agentic-ai/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Parsing IPv6 Addresses Crazily Fast with AVX-512</title>
		<link>https://lemire.me/blog/2026/05/23/parsing-ipv6-addresses-crazily-fast-with-avx-512/</link>
					<comments>https://lemire.me/blog/2026/05/23/parsing-ipv6-addresses-crazily-fast-with-avx-512/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sat, 23 May 2026 02:45:11 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22658</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Capture-decran-le-2026-05-22-a-22.42.35-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Every machine connected to the Internet has an address called an IP address. Originally, these addresses were 32-bit integers (IPv4), giving a theoretical maximum of about four billion distinct addresses. We are all familiar with these addresses (e.g., 192.168.0.0). There was a big fuss about how we would run out of addresses. It never happened &#8230; <a href="https://lemire.me/blog/2026/05/23/parsing-ipv6-addresses-crazily-fast-with-avx-512/" class="more-link">Continue reading <span class="screen-reader-text">Parsing IPv6 Addresses Crazily Fast with AVX-512</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Capture-decran-le-2026-05-22-a-22.42.35-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Every machine connected to the Internet has an address called an IP address. Originally, these addresses were 32-bit integers (IPv4), giving a theoretical maximum of about four billion distinct addresses. We are all familiar with these addresses (e.g., <code>192.168.0.0</code>). There was a big fuss about how we would run out of addresses. It never happened because we don&#8217;t actually need every device to have its own unique address. Your home router needs an address, but every device in your home does not need a worldwide unique address.</p>
<p>Nevertheless, the range was extended to cover 128 bits (IPv6). An IPv6 address is conventionally written as eight groups of four hexadecimal digits separated by colons. For example:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #0000cf; font-weight: bold;">2001</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000;">db8</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">85</span><span style="color: #000;">a3</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">0000</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">0000</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">8</span><span style="color: #000;">a2e</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">0370</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">7334</span>
</code></pre>
</div>
<p>Because addresses often contain runs of zeros, the format allows two shortcuts:</p>
<ul>
<li>Leading zeroes within a group may be omitted: <code>2001:db8:85a3:0:0:8a2e:370:7334</code>.</li>
<li>A single run of all-zero groups may be replaced by <code>::</code>: <code>2001:db8:85a3::8a2e:370:7334</code>.</li>
</ul>
<p>The double-colon trick can appear only once in an address, and can match one or more zero groups. Hence <code>::1</code> is the loopback address (all zeros except the last group), and <code>::</code> is the unspecified address (all zeros).</p>
<p>IPv6 also accepts an embedded IPv4 address in the last 32 bits, written in the usual dotted-decimal form. This is mostly used for IPv4-mapped IPv6 addresses such as <code>::ffff:192.168.1.1</code>. The longest possible textual form is 45 characters:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #0000cf; font-weight: bold;">0000</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">0000</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">0000</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">0000</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">0000</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #000;">ffff</span><span style="color: #000; font-weight: bold;">:</span><span style="color: #0000cf; font-weight: bold;">255.255.255.255</span>
</code></pre>
</div>
<p>So a parser must accept hexadecimal groups, the compressed form, and an optional IPv4 tail. It is more involved than parsing IPv4.</p>
<p>The standard C function for the job is <code>inet_pton</code>, available on essentially every system.</p>
<p>Can we do better?</p>
<p><a href="https://lemire.me/blog/2023/06/08/parsing-ip-addresses-crazily-fast/">A few years ago, I showed that you could parse IPv4 addresses really fast</a>. Can we do the same with IPv6?</p>
<p>The trick is to use data parallelism: we invoke the so-called SIMD instructions that all our processors support. These instructions can process potentially dozens of bytes at once.</p>
<p>Shreesh Adiga gave it a try with AVX-512, the powerful instruction set supported by recent Intel server processors and all new AMD CPUs. The idea is to load the entire string into a 512-bit register, find the colons with a single comparison, compute the spacing between them to drive a byte-level expand, translate hex digits via a permute, and finish with a multiply-accumulate that combines the hex digits into bytes. Almost the whole parser is branch-free, meaning that there are few <code>if</code> clauses.</p>
<p>I put together a small benchmark that generates random IPv6 addresses with <code>inet_ntop</code> (so the addresses are written in their canonical, compressed form) and parses each one with both <code>inet_pton</code> and the AVX-512 routine. The benchmark runs on an Intel Xeon Gold 6548N CPU @ 2.8 GHz (Emerald Rapids) with GCC, compiled with <code>-march=native -O3</code>.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">function</th>
<th style="text-align: right;">ns/addr</th>
<th style="text-align: right;">speed (Mv/s)</th>
<th style="text-align: right;">instr/addr</th>
<th style="text-align: right;">instr/cycle</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>inet_pton</code></td>
<td style="text-align: right;">175.3</td>
<td style="text-align: right;">5.7</td>
<td style="text-align: right;">954</td>
<td style="text-align: right;">1.56</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">AVX-512</td>
<td style="text-align: right;">14.0</td>
<td style="text-align: right;">71.3</td>
<td style="text-align: right;">120</td>
<td style="text-align: right;">2.45</td>
</tr>
</tbody>
</table>
<p>The AVX-512 routine is about <strong>12 times faster</strong> than <code>inet_pton</code>, parsing more than 70 million addresses per second on a single core. It uses eight times fewer instructions, and runs them at a higher throughput (2.45 instructions per cycle versus 1.56).</p>
<p>The source code used for this benchmark is available <a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/05/22">on my blog repository</a>.</p>
<p><strong>Update</strong>. Peter Fors points out that the step in my benchmark, where I sum<br />
up the bytes, adds some overhead especially under GCC. Thus I underestimate the speed slightly.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/05/23/parsing-ipv6-addresses-crazily-fast-with-avx-512/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Only 17% of all 64-bit Integers are products of two 32-bit integers</title>
		<link>https://lemire.me/blog/2026/05/22/only-17-of-all-64-bit-integers-are-products-of-two-32-bit-integers/</link>
					<comments>https://lemire.me/blog/2026/05/22/only-17-of-all-64-bit-integers-are-products-of-two-32-bit-integers/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Fri, 22 May 2026 01:16:35 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22652</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Gemini_Generated_Image_pbz9q4pbz9q4pbz9-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />In software programming, the product between two integers is often computed to a fixed number of bits with overflow. Consider 8-bit integers. If you multiply 127 by 127, you get back the number 1 as an 8-bit unsigned integer, with an overflow. The actual full product is 16129. To represent 16129, you typically use 16 &#8230; <a href="https://lemire.me/blog/2026/05/22/only-17-of-all-64-bit-integers-are-products-of-two-32-bit-integers/" class="more-link">Continue reading <span class="screen-reader-text">Only 17% of all 64-bit Integers are products of two 32-bit integers</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Gemini_Generated_Image_pbz9q4pbz9q4pbz9-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>In software programming, the product between two integers is often computed to a fixed number of bits with overflow. Consider 8-bit integers. If you multiply 127 by 127, you get back the number 1 as an 8-bit unsigned integer, with an overflow. The actual full product is 16129. To represent 16129, you typically use 16 bits of precision.</p>
<p>Thus we have the notion of the full product. The full product of two 32-bit integers is typically represented using 64 bits. The question that preoccupied me is what fraction of all 64-bit integers can be written as the product of two 32-bit integers.</p>
<p>You might wonder why you would care?</p>
<p>We often design hash functions: they are special functions that take an input and generate a random-looking output. Several years ago I designed a very fast hash function called <a href="https://github.com/simdhash/clhash">clhash</a>. It is a super-fast hash function for strings having a few hundred bytes or more. If you don&#8217;t know about clhash, <a href="https://github.com/simdhash/clhash">check it out</a>. It is interesting in its own right.</p>
<p>This clhash hash function uses a type of multiplication typical of cryptographic applications. I was trying to argue that our approach had benefits compared with techniques based on standard multiplications. Let me illustrate. A simple hash function for 32-bit integers could take the least significant bits and multiply them with the most significant bits.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #8f5902; font-style: italic;">// simpleHighLowHash is a simple (and weak) 32-bit hash</span>
<span style="color: #8f5902; font-style: italic;">// that multiplies the high 16 bits by the low 16 bits.</span>
<span style="color: #000;">func</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">simpleHighLowHash</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">x</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint32</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint32</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #f57900;">high</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">:</span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">x</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&gt;&gt;</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">16</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #f8f8f8;">    </span><span style="color: #f57900;">low</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">:</span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">x</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0xFFFF</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">high</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">low</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>Maybe you&#8217;d want the hash function to be uniform: all possible 32-bit hash values should be equally probable. It is only possible in this instance if the hash function can produce all 32-bit hash values, which is not the case.</p>
<p>The great mathematician Erdös showed that the proportion of all <code>2n</code>-bit values that can be generated by the product of two n-bit values goes to zero as <code>n</code> becomes large. This means that if you have, say, 10000000-bit integers multiplying 10000000-bit integers, you&#8217;d expect relatively few 20000000-bit integers to be produced. But what about practical cases like 32-bit integers or 64-bit integers?</p>
<p>You can just brute-force the problem easily up to the multiplication of 16-bit integers into 32-bit products. At that point, slightly one out of five 32-bit numbers is a product between two 16-bit integers. About 80% of all 32-bit integers are never produced by this hash. However, the running time grows exponentially, and brute force won&#8217;t scale all the way to 32 bits.</p>
<p>So what do we do about the 32-bit case? That is, what do you do when you multiply two 32-bit integers to produce a 64-bit product? What fraction of 64-bit values can the following function produce?</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">func</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">simpleHighLowHash</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">x</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint64</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint64</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">    </span><span style="color: #f57900;">high</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">:</span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">x</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&gt;&gt;</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">32</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #f8f8f8;">    </span><span style="color: #f57900;">low</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">:</span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">x</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0xFFFFFFFF</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #f8f8f8;">    </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint64</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">high</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">uint64</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">low</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>Can we get an exact result?</p>
<p>Yes!!!</p>
<p><a href="https://blue.butler.edu/~jewebste/">Webster</a> and his colleagues <a href="https://arxiv.org/pdf/1908.04251">built the math</a> to allow us to scale up the exact computation. <a href="https://github.com/jewebste/multiplication-table-problem">He was kind enough to publish his code</a>.</p>
<p>There are 3,215,709,724,700,470,902 64-bit (unsigned) integers that can be written as a product of two 32-bit integers. That&#8217;s about 17% of all possible values.</p>
<p><img decoding="async" alt="" src="products_percentage.png" /><a href="http://lemire.me/blog/wp-content/uploads/2026/05/products_percentage.png"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/05/products_percentage-1024x640.png" alt="" width="660" height="413" class="alignnone size-large wp-image-22653" srcset="https://lemire.me/blog/wp-content/uploads/2026/05/products_percentage-1024x640.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/05/products_percentage-300x188.png 300w, https://lemire.me/blog/wp-content/uploads/2026/05/products_percentage-768x480.png 768w, https://lemire.me/blog/wp-content/uploads/2026/05/products_percentage.png 1200w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>What about actually computing a pair of integers given their product? One approach consists of computing its full prime factorization, and then using those factors to build all possible divisors that are strictly less than <code>2^32</code>, starting with a set of candidates containing only <code>1</code> and iteratively multiplying existing candidates by each prime factor (only keeping products that stay below <code>2^32</code>). We can avoid adding duplicates to our set by processing unique prime factors with their multiplicity. Finally, we select the maximum such candidate <code>m</code> as the largest divisor under <code>2^32</code>, compute the corresponding leftover <code>n / m</code>, and report whether a valid split into two 32-bit factors exists. In general, the answer (if it exists) is not unique: this returns the pair where one value is maximized. In Python, the code might look as follows.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000;">p</span> <span style="color: #204a87; font-weight: bold;">in</span> <span style="color: #000;">factor_multiplicities</span><span style="color: #000; font-weight: bold;">:</span>
    <span style="color: #000;">new_candidates</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">[]</span>
    <span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000;">c</span> <span style="color: #204a87; font-weight: bold;">in</span> <span style="color: #000;">candidates</span><span style="color: #000; font-weight: bold;">:</span>
        <span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000;">i</span> <span style="color: #204a87; font-weight: bold;">in</span> <span style="color: #204a87;">range</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">factor_multiplicities</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">p</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">):</span>
            <span style="color: #204a87; font-weight: bold;">if</span> <span style="color: #000;">c</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">p</span> <span style="color: #ce5c00; font-weight: bold;">**</span> <span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #0000cf; font-weight: bold;">2</span><span style="color: #ce5c00; font-weight: bold;">**</span><span style="color: #0000cf; font-weight: bold;">32</span><span style="color: #000; font-weight: bold;">:</span>
                <span style="color: #000;">new_candidates</span><span style="color: #ce5c00; font-weight: bold;">.</span><span style="color: #000;">append</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">c</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">p</span> <span style="color: #ce5c00; font-weight: bold;">**</span> <span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">))</span>
    <span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000;">new_c</span> <span style="color: #204a87; font-weight: bold;">in</span> <span style="color: #000;">new_candidates</span><span style="color: #000; font-weight: bold;">:</span>
        <span style="color: #000;">candidates</span><span style="color: #ce5c00; font-weight: bold;">.</span><span style="color: #000;">append</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">new_c</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000;">m</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #204a87;">max</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">candidates</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #204a87;">print</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #4e9a06;">f"Maximum candidate: {</span><span style="color: #000;">m</span><span style="color: #4e9a06;">}"</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #000;">leftover</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">n</span> <span style="color: #ce5c00; font-weight: bold;">//</span> <span style="color: #000;">m</span>
<span style="color: #204a87;">print</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #4e9a06;">f"Leftover: {</span><span style="color: #000;">leftover</span><span style="color: #4e9a06;">}"</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #204a87; font-weight: bold;">if</span> <span style="color: #000;">leftover</span> <span style="color: #ce5c00; font-weight: bold;">&gt;=</span> <span style="color: #0000cf; font-weight: bold;">2</span><span style="color: #ce5c00; font-weight: bold;">**</span><span style="color: #0000cf; font-weight: bold;">32</span><span style="color: #000; font-weight: bold;">:</span>
    <span style="color: #204a87;">print</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #4e9a06;">"Leftover is too large, cannot find a suitable candidate."</span><span style="color: #000; font-weight: bold;">)</span>
</code></pre>
</div>
<p>You might be able to come up with a more efficient algorithm. I find it interesting to consider that if you pick a value at random, it will usually fail! That is, most 64-bit integers cannot be written as the product of two 32-bit integers.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/05/22/only-17-of-all-64-bit-integers-are-products-of-two-32-bit-integers/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>SIMD-accelerated integer-to-string conversion</title>
		<link>https://lemire.me/blog/2026/05/18/simd-accelerated-integer-to-string-conversion/</link>
					<comments>https://lemire.me/blog/2026/05/18/simd-accelerated-integer-to-string-conversion/#respond</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Mon, 18 May 2026 19:39:49 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22639</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Gemini_Generated_Image_bzgphmbzgphmbzgp-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Converting a 64-bit integer to its decimal string representation is a mundane task that shows up everywhere: logging, JSON serialization, CSV output, debug prints, etc. In C++, you might use std::to_chars, sprintf, or some library routine. How do these functions work? At a high level, they repeatedly divide by ten. Start with your integer k. &#8230; <a href="https://lemire.me/blog/2026/05/18/simd-accelerated-integer-to-string-conversion/" class="more-link">Continue reading <span class="screen-reader-text">SIMD-accelerated integer-to-string conversion</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Gemini_Generated_Image_bzgphmbzgphmbzgp-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Converting a 64-bit integer to its decimal string representation is a mundane task that shows up everywhere: logging, JSON serialization, CSV output, debug prints, etc. In C++, you might use <code>std::to_chars</code>, <code>sprintf</code>, or some library routine.</p>
<p>How do these functions work? At a high level, they repeatedly divide by ten. Start with your integer <code>k</code>. Divide it by ten, use the remainder as the last digit (it is between 0 and 9 inclusively). You then add the code point value of the character <code>0</code> to get the ASCII digit. To go faster, you can divide by 100 and use a lookup table so that the value between 0 and 99 inclusively is mapped to a string.</p>
<p>So far so good. Unfortunately, even with all these optimizations, this string generation may become a performance bottleneck. Can you do better?</p>
<p>Let us assume that you have a recent AMD processor or an Intel server. Then you have powerful data-parallel instructions (AVX-512) that can multiply eight 64-bit integers at once. We often refer to these instructions as SIMD (single instruction multiple data). My colleague Jaël Champagne Gareau and I recently published a new paper on exactly this problem. The title says it all: <a href="https://onlinelibrary.wiley.com/doi/10.1002/spe.70079">Converting an Integer to a Decimal String in Under Two Nanoseconds</a>.</p>
<p>When you write <code>n / 100</code> in code, an optimizing compiler converts the operation to a multiplication followed by a shift. It is often described as a multiplicative inverse. Generally, you can replace the division of <code>n</code> by <code>d</code> with the division of <code>c * n</code> or <code>c * n + c</code> by <code>m</code> for convenient integers <code>c</code> and <code>m</code> chosen so that they approximate the reciprocal: <code>c/m ~= 1/d</code>. We often call <code>c * n + c</code> a fused multiply-add. Picking <code>m</code> to be a power of two means that the division by <code>m</code> is just a shift. Then you can get the remainder of the division by using the remainder of the division by <code>m</code>, multiplied by <code>d</code> and divided again by <code>m</code>, which is essentially a multiplication followed by a shift (<a href="https://arxiv.org/abs/2012.12369">Lemire et al., 2021</a>).</p>
<p>We can put this to good use with the Integer Fused Multiply-Add (IFMA) instructions available on recent Intel and AMD processors. They essentially allow you to compute eight instances of <code>(c * n + c)/m</code> in one instruction. The expression <code>(c * n + c)/m</code> gives you the division, but we need the remainder, so instead we pick <code>(c * n + c)%m</code> which we need to multiply by the divisor.</p>
<p>The fun thing with AVX-512 instructions is that they can use a different <code>c</code> and a different divisor for each of the eight operations. Using Intel intrinsic functions, our core routine which converts a value smaller than <code>10^8</code> to eight digits looks as follows:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><span></span><code><span style="color: #000;">__m512i</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">to_string_avx512ifma_8digits</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">uint64_t</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">n</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #f8f8f8;"> </span><span style="color: #000; font-weight: bold;">{</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">__m512i</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">bcstq_l</span><span style="color: #f8f8f8;">   </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">_mm512_set1_epi64</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">n</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">  </span><span style="color: #204a87; font-weight: bold;">constexpr</span><span style="color: #f8f8f8;"> </span><span style="color: #204a87; font-weight: bold;">uint64_t</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">twoto52</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">0x10000000000000ULL</span><span style="color: #000; font-weight: bold;">;</span><span style="color: #f8f8f8;"> </span><span style="color: #8f5902; font-style: italic;">// 2^52</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">__m512i</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">ifma_const</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">_mm512_setr_epi64</span><span style="color: #000; font-weight: bold;">(</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">twoto52</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">/</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">100000000</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">twoto52</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">/</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">10000000</span><span style="color: #000; font-weight: bold;">,
</span><span style="color: #f8f8f8;">    </span><span style="color: #000;">twoto52</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">/</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1000000</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">twoto52</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">/</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">100000</span><span style="color: #000; font-weight: bold;">,</span>
<span style="color: #f8f8f8;">    </span><span style="color: #000;">twoto52</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">/</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">10000</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">twoto52</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">/</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">1000</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> 
</span><span style="color: #000;">    twoto52</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">/</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">100</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">twoto52</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">/</span><span style="color: #f8f8f8;"> </span><span style="color: #0000cf; font-weight: bold;">10</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">__m512i</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">zmmTen</span><span style="color: #f8f8f8;">    </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">_mm512_set1_epi64</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #0000cf; font-weight: bold;">10</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">__m512i</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">asciiZero</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">_mm512_set1_epi64</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #4e9a06;">'0'</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">__m512i</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">lowbits_l</span><span style="color: #f8f8f8;">  </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">_mm512_madd52lo_epu64</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">ifma_const</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> 
</span><span style="color: #000;">    bcstq_l</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">ifma_const</span><span style="color: #000; font-weight: bold;">);</span><span style="color: #f8f8f8;"> </span><span style="color: #8f5902; font-style: italic;">// ifma_const * bcstq_l + ifma_const</span>
<span style="color: #f8f8f8;">  </span><span style="color: #000;">__m512i</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">highbits_l</span><span style="color: #f8f8f8;"> </span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">_mm512_madd52hi_epu64</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">asciiZero</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> 
</span><span style="color: #000;">    zmmTen</span><span style="color: #000; font-weight: bold;">,</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">lowbits_l</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #f8f8f8;">  </span><span style="color: #204a87; font-weight: bold;">return</span><span style="color: #f8f8f8;"> </span><span style="color: #000;">highbits_l</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>It compiles down to two multiplication-add instructions: <code>vpmadd52huq</code>. That&#8217;s it. Two instructions to generate eight digits.</p>
<p>It works by broadcasting <code>n</code> across all eight 64-bit lanes of a <code>__m512i</code> vector (<code>bcstq_l</code>). It then prepares a vector of carefully chosen multiplicative inverses (<code>ifma_const</code>) that represent the reciprocals of <code>10^8</code>, <code>10^7</code>, &#8230; The magic happens in the single <code>_mm512_madd52lo_epu64</code> instruction, which simultaneously performs eight fused multiply-adds: each lane computes <code>(ifma_const[i] * n + ifma_const[i])</code> using 52-bit low-half multiplication, effectively extracting the quotient when dividing by the corresponding power of ten. A second <code>_mm512_madd52hi_epu64</code> instruction (with a vector of ten and a vector of <code>'0'</code>) then isolates the digit values and adds the ASCII <code>'0'</code> offset in the high 52 bits, producing eight packed digit characters in a single 512-bit register.</p>
<p>If all your integers require eight digits, you are done. But in the general case, putting this to good use requires a bit of effort.</p>
<p>Thankfully, even if you, say, need only six digits, you can do the full 8-digit computation and then use a <em>masked store</em> if you want to store only six digits, ignoring the two leftovers. That is, instruction sets like AVX-512 allow you to write only some of the data to memory, which is quite convenient.</p>
<p>We have two variants. One is branch-heavy and does well on homogeneous data (numbers with similar digit lengths). The other is branch-light and better for mixed workloads. A quick profiling step can pick the right one for your dataset.</p>
<p>Our implementation is consistently 1.4–2× faster than the best competitors and 2–4× faster than <code>std::to_chars</code> across a wide range of inputs. What I find interesting is that even if the <code>std::to_chars</code> implementation is not at all naive, you can do significantly better in many ways. James Anhalt&#8217;s approach (<code>jeaiii</code>) is also quite fast on modern hardware.</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/05/graph.png"><img loading="lazy" decoding="async" src="http://lemire.me/blog/wp-content/uploads/2026/05/graph-1024x606.png" alt="" width="660" height="391" class="alignnone size-large wp-image-22648" srcset="https://lemire.me/blog/wp-content/uploads/2026/05/graph-1024x606.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/05/graph-300x178.png 300w, https://lemire.me/blog/wp-content/uploads/2026/05/graph-768x454.png 768w, https://lemire.me/blog/wp-content/uploads/2026/05/graph.png 1352w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p><strong>Further reading</strong><br />
&#8211; The paper: <a href=" https://doi.org/10.1002/spe.70079">doi:10.1002/spe.70079</a><br />
&#8211; The benchmarks are on <a href="https://github.com/fastfloat/int_serialization_benchmark">GitHub</a> (fully reproducible) <br />
&#8211; Shortly after our paper came online, Barend Erasmus created <a href="https://github.com/simditoa/simditoa">a software library implementing our proposed approach</a>. I am not certain that Barend includes both the homogeneous and heterogeneous approaches.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/05/18/simd-accelerated-integer-to-string-conversion/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Checking multiplication overflow</title>
		<link>https://lemire.me/blog/2026/05/06/checking-multiplication-overflow/</link>
					<comments>https://lemire.me/blog/2026/05/06/checking-multiplication-overflow/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Wed, 06 May 2026 20:15:20 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22629</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Capture-decran-le-2026-05-06-a-16.14.54-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Suppose that x is a variable of an unsigned type. In C/C++, it could be of type size_t for example. You have an expression like 6 * x and you want to know whether 6 * x overflows. That is, you want to know if 6 * x exceeds the range of values that can &#8230; <a href="https://lemire.me/blog/2026/05/06/checking-multiplication-overflow/" class="more-link">Continue reading <span class="screen-reader-text">Checking multiplication overflow</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Capture-decran-le-2026-05-06-a-16.14.54-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Suppose that <code>x</code> is a variable of an unsigned type. In C/C++, it could be of type <code>size_t</code> for example.</p>
<p>You have an expression like <code>6 * x</code> and you want to know whether <code>6 * x</code> overflows. That is, you want to know if <code>6 * x</code> exceeds the range of values that can be represented by the type. In most cases, a variable of type <code>size_t</code> will be about to represent all values in the range <code>[0, 2^64-1]</code>. Instead of 64, let me use a variable for the number of bits: <code>[0, 2^L-1]</code>.</p>
<p>The easiest approach is to compare <code>x</code> with <code>(2^L-1) // 6</code> where I use the symbol <code>//</code> to denote the integer division (as opposed to <code>/</code>).</p>
<p>But can you do otherwise ?</p>
<p>If the value does not overflow, we know for sure that <code>(6 * x)//6 == x</code>. The interesting question is what happens when it overflows. We can answer this directly for an arbitrary non-zero constant <code>a</code> in the range <code>[1, 2^L-1]</code>.</p>
<p>Let <code>k = (a*x)//2^L</code> be the number of times the multiplication wraps around. The effective (wrapped) value computed by the machine is <code>r = a*x - k*2^L</code>, with <code>0 &lt;= r &lt; 2^L</code>. Overflow happens precisely when <code>k &gt;= 1</code>. We have that <code>k &lt;= a − 1</code> because <code>x&lt;2^L</code>.</p>
<p>Performing the integer division of <code>r = a*x - k*2^L</code> by <code>a</code>, we get <code>x</code> plus <code>-k*2^L//a</code>. When <code>k</code> is non-zero, this last value (<code>-k*2^L//a</code>) is one of <code>-2^L//a</code>, <code>-2* 2^L//a</code>, &#8230;, <code>-(a-1) * 2^L//a</code>.</p>
<ul>
<li>When <code>k = 0</code> (no overflow): <code>r // a = x</code>.</li>
<li>When <code>k ≥ 1</code>: <code>r // a = x + (negative integer) ≠ x</code>.</li>
</ul>
<p>Hence we have the following result.</p>
<p><em>Theorem</em> If <code>x</code> is of an unsigned type and <code>a</code> is a non-zero constant, then <code>a * x</code> overflows if and only if <code>(a * x)//a != x</code>.</p>
<p><span>In practice, a simple comparison x with <code>(2^L-1) // a</code>  is likely more efficient. Optimizing compilers might be able to convert <code>(a * x)//a != x</code> to a simple comparison. Unfortunately, the Go compiler (for example) cannot.</span></p>
<p>An open question is whether there is a more mathematically elegant check.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/05/06/checking-multiplication-overflow/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Mapping Strings to Float Arrays in Go: How Fast Can We Go?</title>
		<link>https://lemire.me/blog/2026/05/05/mapping-strings-to-float-arrays-in-go-how-fast-can-we-go/</link>
					<comments>https://lemire.me/blog/2026/05/05/mapping-strings-to-float-arrays-in-go-how-fast-can-we-go/#respond</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Tue, 05 May 2026 19:01:09 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22624</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Capture-decran-le-2026-05-05-a-15.04.28-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />A common pattern in modern software is to map a string key to a small array of floating-point numbers. Word embeddings, feature vectors, lookup tables for physical constants: all variations on the same theme. In Go, the obvious way to write this is a map[string][]float32. But how fast is it, really, and can we do &#8230; <a href="https://lemire.me/blog/2026/05/05/mapping-strings-to-float-arrays-in-go-how-fast-can-we-go/" class="more-link">Continue reading <span class="screen-reader-text">Mapping Strings to Float Arrays in Go: How Fast Can We Go?</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/05/Capture-decran-le-2026-05-05-a-15.04.28-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>A common pattern in modern software is to map a string key to a small array of floating-point numbers. Word embeddings, feature vectors, lookup tables for physical constants: all variations on the same theme. In Go, the obvious way to write this is a <code>map[string][]float32</code>. But how fast is it, really, and can we do better?</p>
<p>I have been working on <a href="https://github.com/lemire/constmap">constmap</a>, a Go library that builds an immutable map from strings to <code>uint64</code> values using the <a href="https://arxiv.org/abs/2201.01174">binary fuse filter construction</a>. A lookup amounts to one hash, three array reads, and two XORs. There is no comparison, no chaining, no probing. The whole table fits in roughly 9 bytes per key, which often means it fits in cache where a Go map does not.</p>
<p>Go has fast maps, you cannot easily beat them in performance. But if you build a smaller data structure that causes fewer cache misses, you can definitively go faster.</p>
<p>By default, the constmap returns a <code>uint64</code>. But what if your value is an array of eight <code>float32</code> numbers? You have at least two options:</p>
<ol>
<li>Keep the arrays in a separate slice <code>[][]float32</code>. The constmap returns the index.</li>
<li>Store a pointer to the float array directly inside the constmap&#8217;s <code>uint64</code>.</li>
</ol>
<p>The second option requires the <code>unsafe</code> package because we are smuggling a pointer through an integer field. It has some limitations.</p>
<ul>
<li>You cannot and should not deserialize the data structure to disk.</li>
<li>You must make sure that a reference remains to your float array, or else the garbage collector could collect it and you&#8217;d be left with a dangling pointer. It is trickier than it sounds because Go can collect your memory if it sees that it is no longer used. And it cannot see through your <code>unsafe</code> calls converting an integer to a pointer value. Thankfully, you can just put all your arrays of floats in an array and call <code>runtime.KeepAlive(mybigarray)</code> at a strategic location: this will prevent Go from collecting <code>mybigarray</code>. The call to <code>runtime.KeepAlive</code> is not free but also quite cheap so you can possibly use a lot of such calls. benchmark</li>
</ul>
<p>I built three lookups over 100,000 keys, each mapping to an 8-element <code>[]float32</code>. We always access the first element of the array, to make sure that the bencmark is a bit fair. We have a large set of random queries (a query is a string).</p>
<p>We compare <code>map[string][]float32</code>, the standard <code>constmap</code> coupled with an array (so that the <code>constmap</code> constains indexes), and the <code>constmap</code> that contains what is effectively a pointer to the location of the <code>[]float32</code>.</p>
<p>Run on an Apple M4 Max with Go&#8217;s standard benchmark harness:</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Lookup</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">Time per op</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"><code>map[string][]float32</code></td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">21 ns</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">ConstMap → index → <code>[][]float32</code></td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">11 ns</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">ConstMap → pointer → <code>*[8]float32</code></td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">8.7 ns</td>
</tr>
</tbody>
</table>
<p>The constmap with an index is already a twice as fast as the Go map. Replacing the index by a raw pointer shaves another 2 ns by skipping the indirection through the <code>[][]float32</code> slice header. It is a speedup of about 20% in my case.</p>
<p>The result is interesting on its own: a constmap lookup is fast enough that the <em>next</em> memory load, the slice header read, becomes a measurable fraction of the work.</p>
<p>The benchmark and code are in <a href="https://github.com/lemire/constmap">github.com/lemire/constmap</a>. Run them with:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #204a87; font-weight: bold;">go</span> <span style="color: #000;">test</span> <span style="color: #ce5c00; font-weight: bold;">-</span><span style="color: #000;">bench</span> <span style="color: #4e9a06;">'FloatArray'</span> <span style="color: #ce5c00; font-weight: bold;">-</span><span style="color: #000;">benchtime</span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000;">s</span>
</code></pre>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/05/05/mapping-strings-to-float-arrays-in-go-how-fast-can-we-go/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>House prices and fertility</title>
		<link>https://lemire.me/blog/2026/04/30/house-prices-and-fertility/</link>
					<comments>https://lemire.me/blog/2026/04/30/house-prices-and-fertility/#respond</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Thu, 30 Apr 2026 22:17:34 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22619</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/04/XRF75-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />No, rising house prices are not the driver of sharp fertility declines. The evidence shows only modest, mixed effects that cannot explain the large drops observed in places like Canada. What the Research Actually Shows: A well-known study by Dettling and Kearney (2014) found that rising house prices have opposing effects: they slightly increase fertility &#8230; <a href="https://lemire.me/blog/2026/04/30/house-prices-and-fertility/" class="more-link">Continue reading <span class="screen-reader-text">House prices and fertility</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/04/XRF75-150x150.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>No, rising house prices are not the driver of sharp fertility declines. The evidence shows only modest, mixed effects that cannot explain the large drops observed in places like Canada.</p>
<p>What the Research Actually Shows:</p>
<p>A well-known study by Dettling and Kearney (2014) found that rising house prices have opposing effects: they slightly increase fertility among homeowners (via a &#8220;home equity&#8221; or wealth effect) and slightly decrease it among renters (via a price effect). At average U.S. homeownership rates, the net effect was a small increase in fertility.</p>
<p>This pattern has held up in other countries. For example, Daysal et al. (2021) and related work confirm similar homeowner/renter dynamics in Denmark and elsewhere.</p>
<p>Clark (2012) found that expensive housing markets are associated with a modest delay in age at first birth (roughly 3–4 years after controls), but the overall impact on completed fertility remains limited.</p>
<p>Canadian evidence aligns with this. Clark and Ferrer (2019) analyzed longitudinal data and found that higher lagged house prices were positively associated with the probability of an additional birth among homeowners in some specifications, but the effects were small and did not drive large-scale declines. They noted that falling school-aged children in high-price cities like Vancouver or Toronto are better explained by selective migration of people with preferences for fewer children into urban centers, rather than existing residents having fewer kids due to prices.</p>
<p>These effects are too modest to explain Canada&#8217;s fertility rate of about 1.25–1.3 children per woman (a record low, far below the 2.1 replacement level).</p>
<p>Expensive cities do have fewer children on average, but this largely reflects self-sorting: higher-income, higher-educated people (who tend to have fewer children) cluster in costly urban areas. Correlation is not causation.</p>
<p>Fertility is declining across most developed countries, regardless of housing costs:</p>
<p>&#8211; Israel maintains a high fertility rate (~2.9 children per woman, highest in the OECD), despite expensive housing; Tel Aviv is pricier than Montreal.<br />
&#8211; Japan has long had very low fertility (~1.2) despite more affordable housing in many areas compared to Canada.</p>
<p>As Richard Florida summarized in &#8220;Don&#8217;t Blame Expensive Housing for Falling Fertility&#8221;: intuition suggests high costs deter families, but demographic research points more strongly to higher education and related lifestyle shifts as drivers of lower birth rates. Richer societies and cities tend to have lower fertility.</p>
<p>Fertility choices are heavily shaped early in life. A 16-year-old planning her future is unlikely to base major decisions on distant future house prices in her 20s or 30s. Models ignoring how teenagers view family, career, and life priorities miss the bigger picture.</p>
<p>Housing costs matter at the margins (with offsetting effects by tenure), but they are not the story behind broad fertility collapses. Policies focused on housing affordability are unlikely to reverse fertility trends.</p>
<p>We have no reason whatsoever to believe that a collapse in housing prices in a country like Canada would drive fertility upward. It is motivated reasoning.</p>
<p>High house prices are a terrible thing in my opinion. Investing all your capital in houses is an odd way to get prosperity. But it does not drive our fertility collapse.</p>
<p dir="auto"><strong>References</strong></p>
<p dir="auto">Clark, Jeremy. 2012. “Do Women Delay Family Formation in Expensive Housing Markets?” <em>Demographic Research</em> 27(1): 1–24. <a href="https://doi.org/10.4054/DemRes.2012.27.1" target="_blank" rel="noopener noreferrer nofollow">https://doi.org/10.4054/DemRes.2012.27.1</a></p>
<p dir="auto">Clark, Jeremy, and Ana Ferrer. 2019. “The Effect of House Prices on Fertility: Evidence from Canada.” <em>Economics: The Open-Access, Open-Assessment E-Journal</em> 13(2019-38): 1–32. <a href="https://doi.org/10.5018/economics-ejournal.ja.2019-38" target="_blank" rel="noopener noreferrer nofollow">https://doi.org/10.5018/economics-ejournal.ja.2019-38</a></p>
<p dir="auto">Daysal, N. Meltem, Michael F. Lovenheim, Nikolaj Siersbæk, and David N. Wasser. 2021. “Home Prices, Fertility, and Early-Life Health Outcomes.” <em>Journal of Public Economics</em> 198: 104366. <a href="https://doi.org/10.1016/j.jpubeco.2021.104366" target="_blank" rel="noopener noreferrer nofollow">https://doi.org/10.1016/j.jpubeco.2021.104366</a></p>
<p dir="auto">Dettling, Lisa J., and Melissa S. Kearney. 2014. “House Prices and Birth Rates: The Impact of the Real Estate Market on the Decision to Have a Baby.” <em>Journal of Public Economics</em> 110: 82–100. <a href="https://doi.org/10.1016/j.jpubeco.2013.09.009" target="_blank" rel="noopener noreferrer nofollow">https://doi.org/10.1016/j.jpubeco.2013.09.009</a> (NBER Working Paper 17485, 2011/2014 version)</p>
<p dir="auto">Florida, Richard. 2018. “Don’t Blame Expensive Housing for Falling Fertility.” <em>CityLab</em> (Bloomberg), June 14, 2018. <a href="https://www.bloomberg.com/news/articles/2018-06-14/the-complex-relationship-between-house-prices-and-fertility" target="_blank" rel="noopener noreferrer nofollow">https://www.bloomberg.com/news/articles/2018-06-14/the-complex-relationship-between-house-prices-and-fertility</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/04/30/house-prices-and-fertility/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>You can beat the binary search</title>
		<link>https://lemire.me/blog/2026/04/27/you-can-beat-the-binary-search/</link>
					<comments>https://lemire.me/blog/2026/04/27/you-can-beat-the-binary-search/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Mon, 27 Apr 2026 17:32:13 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22600</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/04/Gemini_Generated_Image_rsv3zvrsv3zvrsv3-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />We sometimes have to look for a value in a sorted array. The simplest algorithm consists in just going through the values one by one, until we encounter the value, or exhaust the array. We sometimes call this algorithm a linear search. In C++, you can get the desired effect with the std::find function. For &#8230; <a href="https://lemire.me/blog/2026/04/27/you-can-beat-the-binary-search/" class="more-link">Continue reading <span class="screen-reader-text">You can beat the binary search</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/04/Gemini_Generated_Image_rsv3zvrsv3zvrsv3-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>We sometimes have to look for a value in a sorted array. The simplest algorithm consists in just going through the values one by one, until we encounter the value, or exhaust the array. We sometimes call this algorithm a linear search. In C++, you can get the desired effect with the <code>std::find</code> function.</p>
<p>For large arrays, you can do better with a binary search. Binary search is a classic algorithm that efficiently locates a target value in a sorted array by repeatedly dividing the search interval in half. Starting with the entire array, it compares the target to the middle element: if the target is smaller, it discards the upper half; if larger, it discards the lower half. This process continues until the target is found or the interval is empty. It is much faster than linear search for large datasets. In C++, this is implemented by the <code>std::binary_search</code> function, which returns a boolean indicating whether the value is present.</p>
<p>The popular Roaring Bitmap format uses arrays of 16-bit integers of size ranging from 1 to 4096. We sometimes have to check whether a value is present. We use a binary search.</p>
<p>I wanted a faster approach. I had two insights.</p>
<ol>
<li>Virtually all processors today have data parallel instructions (sometimes called SIMD) that can check several values at once. Both 64-bit ARM and x64 processors (Intel/AMD) always support comparing eight 16-bit integers with a target value using a single instruction. This suggests that you should not bother going down in the binary search to blocks that are smaller than eight elements. And you may also want to cheaply compare sixteen elements or more.</li>
<li>The binary search checks one value at a time. However, recent processors can load and check more than one value at once. They have excellent memory-level parllelism. This suggest that instead of a binary search, we might want to try a quaternary search: instead of splitting arrays in halves, we might split them in quarters. The net result might generate a few more instructions but the number of instructions is likely not the limiting factor.</li>
</ol>
<p>Thus, I created something I call the SIMD Quad algorithm. It is an efficient search algorithm for sorted arrays of 16-bit unsigned integers, combining a quaternary interpolation search with SIMD (Single Instruction, Multiple Data). The algorithm divides the array into fixed-size blocks of 16 elements (except maybe for the last block) and uses the last element of each block as interpolation keys to quickly narrow down the search to a single block, then employs SIMD instructions to check all 16 elements in that block simultaneously.</p>
<p>The core idea is to perform a hierarchical search: first, use interpolation search on a coarser level (block boundaries) to find the likely block containing the target value, then switch to SIMD for fine-grained parallel checking within the block. This hybrid approach leverages the strengths of both algorithmic optimization (interpolation search reduces comparisons logarithmically) and hardware acceleration (SIMD checks multiple elements at once).</p>
<ol>
<li>Initial Check: If the array has fewer than 16 elements, perform a simple linear search through all elements.</li>
<li>Block Division: Divide the array into blocks of 16 consecutive elements. For an array of size <code>cardinality</code>, there are <code>num_blocks = cardinality / 16</code> full blocks.</li>
<li>Quaternary Interpolation Search: Use the last element of each block (at positions <code>16-1</code>, <code>32-1</code>, etc.) as keys for interpolation. The search performs a quaternary (base-4) interpolation to find the block where the target <code>pos</code> is likely located. This involves comparing the target against quarter-points of the current search range and adjusting the base accordingly.</li>
<li>Block Selection: After narrowing down, select the appropriate block index <code>lo</code> based on the interpolation results.</li>
<li>SIMD Check: If a valid block is found, load the 16 elements into SIMD registers (using NEON on ARM or SSE2 on x64) and perform parallel equality comparisons with the target value. If any match is found, return true.</li>
<li>Remainder Check: For any elements not in full blocks (remainder), perform a linear search.</li>
</ol>
<p>How does it do? I wrote a benchmark. The benchmark works as follows. For each array size from 2 to 4096 elements, it generates 100,000 sorted arrays of 16-bit unsigned integers. For each size, it performs 10 million membership queries in &#8220;cold&#8221; mode (each query searches a different array, simulating cache misses) and 10 million queries in &#8220;warm&#8221; mode (queries are grouped by array, with each array being searched 100 times consecutively, simulating cache hits). The benchmark measures the average time per query for three algorithms: linear search (<code>std::find</code>), binary search (<code>std::binary_search</code>), and the new SIMD Quad algorithm.</p>
<p>I use two systems. An Apple M4 with Apple LLVM and an Intel Emeral Rapids processor with GCC.</p>
<p>Firstly, let us compare the linear search with the binary search.</p>
<p>Intel/GCC:</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_intel-scaled.png"><img loading="lazy" decoding="async" class="alignnone size-large wp-image-22609" src="http://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_intel-1024x427.png" alt="" width="660" height="275" srcset="https://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_intel-1024x427.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_intel-300x125.png 300w, https://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_intel-768x320.png 768w, https://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_intel-1536x640.png 1536w, https://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_intel-2048x853.png 2048w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>Apple/LLVM</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_apple-scaled.png"><img loading="lazy" decoding="async" class="alignnone size-large wp-image-22608" src="http://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_apple-1024x427.png" alt="" width="660" height="275" srcset="https://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_apple-1024x427.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_apple-300x125.png 300w, https://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_apple-768x320.png 768w, https://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_apple-1536x640.png 1536w, https://lemire.me/blog/wp-content/uploads/2026/04/comparison_plot_apple-2048x853.png 2048w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>The result is clear. The binary search beats the linear search as soon as the arrays get large. That is to be expected.</p>
<p>On a cold cache, the linear search is relatively worse. That is to be expected because it accesses more data, causing more cache faults.</p>
<p>We have established that the binary search is the net winner over the linear search. Let us now compare with the SIMD Quad algorithm.</p>
<p>Intel/GCC:<br />
<a href="http://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_intel-scaled.png"><img loading="lazy" decoding="async" class="alignnone size-large wp-image-22607" src="http://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_intel-1024x427.png" alt="" width="660" height="275" srcset="https://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_intel-1024x427.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_intel-300x125.png 300w, https://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_intel-768x320.png 768w, https://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_intel-1536x640.png 1536w, https://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_intel-2048x853.png 2048w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>Apple/LLVM</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_apple-scaled.png"><img loading="lazy" decoding="async" class="alignnone size-large wp-image-22606" src="http://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_apple-1024x427.png" alt="" width="660" height="275" srcset="https://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_apple-1024x427.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_apple-300x125.png 300w, https://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_apple-768x320.png 768w, https://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_apple-1536x640.png 1536w, https://lemire.me/blog/wp-content/uploads/2026/04/binary_vs_simd_apple-2048x853.png 2048w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>The results differ markedly between the Intel and Apple platform. On the Intel platform the SIMD Quad is more than twice as fast as the binary search on the warm cache. The benefits are lesser on the cold cache. On the Apple platform, the reverse is true, it is with the cold cache that the SIMD Quad is more than twice as fast, whereas the benefits are more marginal on the warm cache.</p>
<p>But the important point is that, in all instances, SIMD Quad is faster than the binary search.</p>
<p>The SIMD component of the algorithm is rather straightforward: we use specialized instructions that save work. So it is easy to see why it might make things faster. There are few instructions, fewer branches.</p>
<p>But what about the &#8216;quad&#8217; part. Does it matter? So I tried a binary version of the same algorithm. It has the same SIMD optimization, but I am dropping the quaternary interpolation search and replacing it with a standard binary search.</p>
<p>Intel/GCC:</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_intel-scaled.png"><img loading="lazy" decoding="async" class="alignnone size-large wp-image-22605" src="http://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_intel-1024x427.png" alt="" width="660" height="275" srcset="https://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_intel-1024x427.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_intel-300x125.png 300w, https://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_intel-768x320.png 768w, https://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_intel-1536x640.png 1536w, https://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_intel-2048x853.png 2048w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>Apple/LLVM</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_apple-scaled.png"><img loading="lazy" decoding="async" class="alignnone size-large wp-image-22604" src="http://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_apple-1024x427.png" alt="" width="660" height="275" srcset="https://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_apple-1024x427.png 1024w, https://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_apple-300x125.png 300w, https://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_apple-768x320.png 768w, https://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_apple-1536x640.png 1536w, https://lemire.me/blog/wp-content/uploads/2026/04/three_way_comparison_apple-2048x853.png 2048w" sizes="auto, (max-width: 660px) 100vw, 660px" /></a></p>
<p>To put it in simple terms, the quad approach has little effect on the Apple platform, but it is a decent optimization on the Intel platform for large arrays in the cold case. The quaternary search better exploits the memory-level parallelism on my Intel server.</p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/04/26/benchmark">My source code is available</a>.</p>
<p><strong>Conclusion</strong>. What my results suggest is that while a textbook binary search is a decent algorithm, you can do better in ways that matter. Standard algorithms were often not designed for computers that have so much parallelism. The SIMD Quad algorithm tries to leverage both the memory-level and data parallelism. Further, I suspect that we can do even better than my algorithm. Let us get creative!</p>
<p><strong>Further reading</strong>: <a href="https://lemire.me/blog/2019/01/16/faster-intersections-between-sorted-arrays-with-shotgun/">Faster intersections between sorted arrays with shotgun</a></p>
<h2>Appendix (source code)</h2>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #204a87; font-weight: bold;">bool</span> <span style="color: #000;">simd_quad</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">const</span> <span style="color: #204a87; font-weight: bold;">uint16_t</span> <span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">carr</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">cardinality</span><span style="color: #000; font-weight: bold;">,</span> 
<span style="color: #204a87; font-weight: bold;">            uint16_t</span> <span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
    <span style="color: #204a87; font-weight: bold;">constexpr</span> <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">gap</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #0000cf; font-weight: bold;">16</span><span style="color: #000; font-weight: bold;">;</span>
    <span style="color: #204a87; font-weight: bold;">if</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">cardinality</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">gap</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
      <span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">j</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span> <span style="color: #000;">j</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">cardinality</span><span style="color: #000; font-weight: bold;">;</span> <span style="color: #000;">j</span><span style="color: #ce5c00; font-weight: bold;">++</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
          <span style="color: #204a87; font-weight: bold;">if</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">carr</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">j</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">==</span> <span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #204a87; font-weight: bold;">return</span> <span style="color: #204a87;">true</span><span style="color: #000; font-weight: bold;">;</span>
        <span style="color: #000; font-weight: bold;">}</span>
        <span style="color: #204a87; font-weight: bold;">return</span> <span style="color: #204a87;">false</span><span style="color: #000; font-weight: bold;">;</span>
    <span style="color: #000; font-weight: bold;">}</span>
    <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">num_blocks</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">cardinality</span> <span style="color: #ce5c00; font-weight: bold;">/</span> <span style="color: #000;">gap</span><span style="color: #000; font-weight: bold;">;</span>
    <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">base</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span>
    <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">n</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">num_blocks</span><span style="color: #000; font-weight: bold;">;</span>
    <span style="color: #204a87; font-weight: bold;">while</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">n</span> <span style="color: #ce5c00; font-weight: bold;">&gt;</span> <span style="color: #0000cf; font-weight: bold;">3</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
      <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">quarter</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">n</span> <span style="color: #ce5c00; font-weight: bold;">&gt;&gt;</span> <span style="color: #0000cf; font-weight: bold;">2</span><span style="color: #000; font-weight: bold;">;</span>

      <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">k1</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">carr</span><span style="color: #000; font-weight: bold;">[(</span><span style="color: #000;">base</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #000;">quarter</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">gap</span> <span style="color: #ce5c00; font-weight: bold;">-</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">];</span>
      <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">k2</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">carr</span><span style="color: #000; font-weight: bold;">[(</span><span style="color: #000;">base</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">2</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">quarter</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">gap</span> <span style="color: #ce5c00; font-weight: bold;">-</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">];</span>
      <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">k3</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">carr</span><span style="color: #000; font-weight: bold;">[(</span><span style="color: #000;">base</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">3</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">quarter</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">gap</span> <span style="color: #ce5c00; font-weight: bold;">-</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">];</span>

      <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">c1</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">k1</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">);</span>
      <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">c2</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">k2</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">);</span>
      <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">c3</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">k3</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">);</span>

      <span style="color: #000;">base</span> <span style="color: #ce5c00; font-weight: bold;">+=</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">c1</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #000;">c2</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #000;">c3</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">quarter</span><span style="color: #000; font-weight: bold;">;</span>
      <span style="color: #000;">n</span> <span style="color: #ce5c00; font-weight: bold;">-=</span> <span style="color: #0000cf; font-weight: bold;">3</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">quarter</span><span style="color: #000; font-weight: bold;">;</span>
    <span style="color: #000; font-weight: bold;">}</span>
    <span style="color: #204a87; font-weight: bold;">while</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">n</span> <span style="color: #ce5c00; font-weight: bold;">&gt;</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
        <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">half</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">n</span> <span style="color: #ce5c00; font-weight: bold;">&gt;&gt;</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">;</span>
        <span style="color: #000;">base</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">carr</span><span style="color: #000; font-weight: bold;">[(</span><span style="color: #000;">base</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #000;">half</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">gap</span> <span style="color: #ce5c00; font-weight: bold;">-</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">)</span> 
<span style="color: #ce5c00; font-weight: bold;">                 ?</span> <span style="color: #000;">base</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #000;">half</span> <span style="color: #ce5c00; font-weight: bold;">:</span> <span style="color: #000;">base</span><span style="color: #000; font-weight: bold;">;</span>
        <span style="color: #000;">n</span> <span style="color: #ce5c00; font-weight: bold;">-=</span> <span style="color: #000;">half</span><span style="color: #000; font-weight: bold;">;</span>
    <span style="color: #000; font-weight: bold;">}</span>
    <span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">lo</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">carr</span><span style="color: #000; font-weight: bold;">[(</span><span style="color: #000;">base</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">gap</span> <span style="color: #ce5c00; font-weight: bold;">-</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">)</span> 
<span style="color: #ce5c00; font-weight: bold;">                ?</span> <span style="color: #000;">base</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">1</span> <span style="color: #ce5c00; font-weight: bold;">:</span> <span style="color: #000;">base</span><span style="color: #000; font-weight: bold;">;</span>

    <span style="color: #204a87; font-weight: bold;">if</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">lo</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">num_blocks</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
        <span style="color: #204a87; font-weight: bold;">const</span> <span style="color: #204a87; font-weight: bold;">uint16_t</span> <span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">blk</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">carr</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #000;">lo</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">gap</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #8f5902; font-style: italic;">#ifdef __ARM_NEON</span>
        <span style="color: #000;">uint16x8_t</span> <span style="color: #000;">needle</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vdupq_n_u16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">);</span>
        <span style="color: #000;">uint16x8_t</span> <span style="color: #000;">v0</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vld1q_u16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">blk</span><span style="color: #000; font-weight: bold;">);</span>
        <span style="color: #000;">uint16x8_t</span> <span style="color: #000;">v1</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vld1q_u16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">blk</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">8</span><span style="color: #000; font-weight: bold;">);</span>
        <span style="color: #000;">uint16x8_t</span> <span style="color: #000;">hit</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vorrq_u16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">vceqq_u16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">v0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">needle</span><span style="color: #000; font-weight: bold;">),</span> 
<span style="color: #000;">                  vceqq_u16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">v1</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">needle</span><span style="color: #000; font-weight: bold;">));</span>
        <span style="color: #204a87; font-weight: bold;">return</span> <span style="color: #000;">vmaxvq_u16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">hit</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #ce5c00; font-weight: bold;">!=</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #8f5902; font-style: italic;">#else</span>
        <span style="color: #204a87; font-weight: bold;">__m128i</span> <span style="color: #000;">needle</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">_mm_set1_epi16</span><span style="color: #000; font-weight: bold;">((</span><span style="color: #204a87; font-weight: bold;">short</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">);</span>
        <span style="color: #204a87; font-weight: bold;">__m128i</span> <span style="color: #000;">v0</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">_mm_loadu_si128</span><span style="color: #000; font-weight: bold;">((</span><span style="color: #204a87; font-weight: bold;">const</span> <span style="color: #204a87; font-weight: bold;">__m128i</span> <span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000; font-weight: bold;">)</span><span style="color: #000;">blk</span><span style="color: #000; font-weight: bold;">);</span>
        <span style="color: #204a87; font-weight: bold;">__m128i</span> <span style="color: #000;">v1</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">_mm_loadu_si128</span><span style="color: #000; font-weight: bold;">((</span><span style="color: #204a87; font-weight: bold;">const</span> <span style="color: #204a87; font-weight: bold;">__m128i</span> <span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000; font-weight: bold;">)(</span><span style="color: #000;">blk</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">8</span><span style="color: #000; font-weight: bold;">));</span>
        <span style="color: #204a87; font-weight: bold;">__m128i</span> <span style="color: #000;">hit</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">_mm_or_si128</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">_mm_cmpeq_epi16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">v0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">needle</span><span style="color: #000; font-weight: bold;">),</span>
                                   <span style="color: #000;">_mm_cmpeq_epi16</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">v1</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">needle</span><span style="color: #000; font-weight: bold;">));</span>
        <span style="color: #204a87; font-weight: bold;">return</span> <span style="color: #000;">_mm_movemask_epi8</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">hit</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #ce5c00; font-weight: bold;">!=</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #8f5902; font-style: italic;">#endif</span>
    <span style="color: #000; font-weight: bold;">}</span>

    <span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">int32_t</span> <span style="color: #000;">j</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">num_blocks</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">gap</span><span style="color: #000; font-weight: bold;">;</span> <span style="color: #000;">j</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">cardinality</span><span style="color: #000; font-weight: bold;">;</span> <span style="color: #000;">j</span><span style="color: #ce5c00; font-weight: bold;">++</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
        <span style="color: #204a87; font-weight: bold;">uint16_t</span> <span style="color: #000;">v</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">carr</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">j</span><span style="color: #000; font-weight: bold;">];</span>
        <span style="color: #204a87; font-weight: bold;">if</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">v</span> <span style="color: #ce5c00; font-weight: bold;">&gt;=</span> <span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #204a87; font-weight: bold;">return</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">v</span> <span style="color: #ce5c00; font-weight: bold;">==</span> <span style="color: #000;">pos</span><span style="color: #000; font-weight: bold;">);</span>
    <span style="color: #000; font-weight: bold;">}</span>
    <span style="color: #204a87; font-weight: bold;">return</span> <span style="color: #204a87;">false</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/04/27/you-can-beat-the-binary-search/feed/</wfw:commentRss>
			<slash:comments>20</slash:comments>
		
		
			</item>
		<item>
		<title>The fastest way to match characters on ARM processors?</title>
		<link>https://lemire.me/blog/2026/04/19/the-fastest-way-to-match-characters-on-arm-processors/</link>
					<comments>https://lemire.me/blog/2026/04/19/the-fastest-way-to-match-characters-on-arm-processors/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sun, 19 Apr 2026 20:41:04 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22587</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/04/Capture-decran-le-2026-04-19-a-16.24.56-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Consider the following problem. Given a string, you must match all of the ASCII white-space characters (\t, \n, \r, and the space) and some characters important in JSON (:, ,, [, ], {, }). JSON is a text-based data format used for web services. A toy JSON document looks as follows. { "name": "Alice", "age": &#8230; <a href="https://lemire.me/blog/2026/04/19/the-fastest-way-to-match-characters-on-arm-processors/" class="more-link">Continue reading <span class="screen-reader-text">The fastest way to match characters on ARM processors?</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/04/Capture-decran-le-2026-04-19-a-16.24.56-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Consider the following problem. Given a string, you must match all of the ASCII white-space characters (<code>\t</code>, <code>\n</code>, <code>\r</code>, and the space) and some characters important in JSON (<code>:</code>, <code>,</code>, <code>[</code>, <code>]</code>, <code>{</code>, <code>}</code>). JSON is a text-based data format used for web services. A toy JSON document looks as follows.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #000; font-weight: bold;">{</span>
  <span style="color: #204a87; font-weight: bold;">"name"</span><span style="color: #000; font-weight: bold;">:</span> <span style="color: #4e9a06;">"Alice"</span><span style="color: #000; font-weight: bold;">,</span>
  <span style="color: #204a87; font-weight: bold;">"age"</span><span style="color: #000; font-weight: bold;">:</span> <span style="color: #0000cf; font-weight: bold;">30</span><span style="color: #000; font-weight: bold;">,</span>
  <span style="color: #204a87; font-weight: bold;">"email"</span><span style="color: #000; font-weight: bold;">:</span> <span style="color: #4e9a06;">"alice@example.com"</span><span style="color: #000; font-weight: bold;">,</span>
  <span style="color: #204a87; font-weight: bold;">"tags"</span><span style="color: #000; font-weight: bold;">:</span> <span style="color: #000; font-weight: bold;">[</span><span style="color: #4e9a06;">"developer"</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #4e9a06;">"python"</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #4e9a06;">"open-source"</span><span style="color: #000; font-weight: bold;">],</span>
  <span style="color: #204a87; font-weight: bold;">"active"</span><span style="color: #000; font-weight: bold;">:</span> <span style="color: #204a87; font-weight: bold;">true</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>We want to solve this problem using SIMD (single-instruction-multiple-data) instructions. With these instructions, you can compare a block of 16 bytes with another block of 16 bytes in one instruction.</p>
<p>It is a subproblem in the fast simdjson JSON library when we index a JSON document. We call this task <em>vectorized classification</em>. We also use the same technique when parsing DNS records, and so forth. In the actual simdjson library, we must also handle strings and quotes, and it gets more complicated.</p>
<p>I need to define what I mean by &#8216;matching&#8217; the characters. In my case, it is enough to get, for each block of 64 bytes, two 64-bit masks: one for spaces and one for important characters. To illustrate, let me consider a 16-byte variant:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code>{"name": "Ali" }
1000000100000001 // important characters
0000000010000010 // spaces
</code></pre>
</div>
<p>Thus, I want to get back the numbers <code>0b1000000100000001</code> and <code>0b0000000010000010</code> in binary format (they are 33025 and 130 in decimal).</p>
<p>I refer you to Langdale and Lemire (2019) for how to do it using the conventional SIMD instructions available on ARM processors (NEON). Their key idea is a table-driven, branch-free classifier: for each byte, use SIMD table lookups to map each nibble to a bitmask, and compare to decide whether the byte belongs to a target set (whitespace or structural JSON characters). This avoids doing many separate equality comparisons per character.</p>
<p>There is now a better way on recent ARM processors.</p>
<p>The 128-bit version of NEON was introduced in 2011 with the ARMv8-A architecture (AArch64). Apple played an important role and it was first used by the Apple A7 chip in the iPhone 5S. You can count on all 64-bit ARM processors to support NEON, which is convenient. (There are 32-bit ARM processors but they are mostly used for embedded systems, not mainstream computing.)</p>
<p>ARM NEON is good but getting old. It is no match for the AVX-512 instruction set available on x64 (AMD and Intel) processors. Not only do the AVX-512 instructions support wider registers (64 bytes as opposed to ARM NEON&#8217;s 16 bytes), but they also have more powerful instructions.</p>
<p>But ARM has something else to offer: Scalable Vector Extension (SVE) and its successor, SVE2. Though SVE was first introduced in 2016, it took until 2022 before we had actual access. The Neoverse V1 architecture used by the Amazon Graviton 3 is the first one I had access to. Soon after, we got SVE2 with the Neoverse V2 and N2 architectures. Today it is readily available: the Graviton4 on AWS, the Microsoft Cobalt 100 on Azure, the Google Axion on Google Cloud (and newer Google Cloud ARM CPUs), the NVIDIA Grace CPU, as well as several chips from Qualcomm, MediaTek, and Samsung. Notice who I am not including? Apple. For unclear reasons, Apple has not yet adopted SVE2.</p>
<p>I have mixed feelings about SVE/SVE2. Like RISC-V, it breaks with the approach from ARM NEON and x64 SIMD that uses fixed-length register sizes (16 bytes, 32 bytes, 64 bytes). This means that you are expected to code without knowing how wide the registers are.</p>
<p>This is convenient for chip makers because it gives them the option of adjusting the register size to better suit their market. Yet it seems to have failed. While the Graviton 3 processor from Amazon had 256-bit registers&#8230; all commodity chips have had 128-bit registers after that.</p>
<p>On the plus side, SVE/SVE2 has masks a bit like AVX-512, so you can load and process data only in a subset of the registers. It solves a long-standing problem with earlier SIMD instruction sets where the input is not a multiple of the register size. Both SVE/SVE2 and AVX-512 might make tail handling nicer. Being able to operate on only part of the register allows clever optimizations. Sadly, SVE/SVE2 does not allow you to move masks to and from a general-purpose register efficiently, unlike AVX-512. And that&#8217;s a direct consequence of their design with variable-length registers. Thus, even though your registers might always be 128-bit and contain 16 bytes, the instruction set is not allowed to assume that a mask fits in a 16-bit word.</p>
<p>I was pessimistic regarding SVE/SVE2 until I learned that <a href="https://lemire.me/blog/2025/03/29/mixing-arm-neon-with-sve-code-for-fun-and-profit/">it is designed to be interoperable with ARM NEON</a>. Thus you can use the SVE/SVE2 instructions with your ARM NEON code. This works especially well if you know that the SVE/SVE2 registers match the ARM NEON registers (16 bytes).</p>
<p>For the work I do, there are two SVE2 instructions that are important: <code>match</code> and <code>nmatch</code>. In their 8-bit versions, what they do is the following: given two vectors <code>a</code> and <code>b</code>, each containing up to 16 bytes, <code>match</code> sets a predicate bit to <code>true</code> for each position <code>i</code> where <code>a[i]</code> equals <em>any</em> of the bytes in <code>b</code>. In other words, <code>b</code> acts as a small lookup set, and <code>match</code> tests set membership for every byte of <code>a</code> simultaneously. The <code>nmatch</code> instruction is the logical complement: it sets a predicate bit to <code>true</code> wherever <code>a[i]</code> does <em>not</em> match any byte in <code>b</code>. A single instruction thus replaces a series of equality comparisons and OR-reductions that would otherwise be needed. In the code below, <code>op_chars</code> holds the 8 structural JSON characters and <code>ws_chars</code> holds the 4 whitespace characters; calling <code>svmatch_u8</code> once on a 16-byte chunk <code>d0</code> produces a predicate that has a <code>true</code> bit exactly where that input byte is a structural character. The code uses SVE2 intrinsics: compiler-provided C/C++ functions that map almost one-to-one to CPU SIMD instructions, so you get near-assembly control without writing assembly.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #8f5902; font-style: italic;">// : , [ ] { }</span>
<span style="color: #204a87; font-weight: bold;">uint8_t</span> <span style="color: #000;">op_chars_data</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">16</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">{</span>
    <span style="color: #0000cf; font-weight: bold;">0x3a</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0x2c</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0x5b</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0x5d</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0x7b</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0x7d</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> 
    <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span>
<span style="color: #000; font-weight: bold;">};</span>
<span style="color: #8f5902; font-style: italic;">// \t \n \r ' '</span>
<span style="color: #204a87; font-weight: bold;">uint8_t</span> <span style="color: #000;">ws_chars_data</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">16</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">{</span>
    <span style="color: #0000cf; font-weight: bold;">0x09</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0x0a</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0x0d</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0x20</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> 
    <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span>
<span style="color: #000; font-weight: bold;">};</span>

<span style="color: #8f5902; font-style: italic;">// load the characters in SIMD registers</span>
<span style="color: #000;">svuint8_t</span> <span style="color: #000;">op_chars</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">svld1_u8</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">svptrue_b8</span><span style="color: #000; font-weight: bold;">(),</span> <span style="color: #000;">op_chars_data</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000;">svuint8_t</span> <span style="color: #000;">ws_chars</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">svld1_u8</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">svptrue_b8</span><span style="color: #000; font-weight: bold;">(),</span> <span style="color: #000;">ws_chars_data</span><span style="color: #000; font-weight: bold;">);</span>

<span style="color: #8f5902; font-style: italic;">// load data</span>
<span style="color: #8f5902; font-style: italic;">// const char * input = ...</span>
<span style="color: #000;">svbool_t</span> <span style="color: #000;">pg</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">svptrue_pat_b8</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">SV_VL16</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000;">svuint8_t</span> <span style="color: #000;">d</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">svld1_u8</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">pg</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">input</span><span style="color: #000; font-weight: bold;">);</span>

<span style="color: #8f5902; font-style: italic;">// matching</span>
<span style="color: #000;">svbool_t</span> <span style="color: #000;">op</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">svmatch_u8</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">pg</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">d</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">op_chars</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000;">svbool_t</span> <span style="color: #000;">ws</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">svmatch_u8</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">pg</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">d</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">ws_chars</span><span style="color: #000; font-weight: bold;">);</span>
</code></pre>
</div>
<p>In this code snippet, <code>svuint8_t</code> is an SVE vector type containing unsigned 8-bit lanes (bytes). <code>svbool_t</code> is an SVE predicate (mask) type. <code>svptrue_b8()</code> builds a predicate where all 8-bit lanes are active, and <code>svld1_u8(pg, ptr)</code> loads bytes from memory into an SVE vector, using predicate <code>pg</code> to decide which lanes are actually read.</p>
<p>If you paid attention thus far, you might have noticed that my code is slightly wrong since I am including 0 in the character sets. But it is fine as long as I assume that the zero byte is not present in the input. In practice, I could just repeat one of the characters, or use a bogus character that I do not expect to see in my inputs (such as the byte value <code>0xFF</code>, which cannot appear in a valid UTF-8 string).</p>
<p>In standard SVE/SVE2, <code>op</code> and <code>ws</code> are predicates, not integer masks. A practical trick is to materialize each predicate as bytes (<code>0xFF</code> for true, <code>0x00</code> for false), for example with <code>svdup_n_u8_z</code>.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #000;">svuint8_t</span> <span style="color: #000;">opm</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">svdup_n_u8_z</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">op</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0xFF</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000;">svuint8_t</span> <span style="color: #000;">wsm</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">svdup_n_u8_z</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">ws</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0xFF</span><span style="color: #000; font-weight: bold;">);</span>
</code></pre>
</div>
<p>When SVE vectors are 128 bits, this byte vector maps naturally to a NEON <code>uint8x16_t</code> via <code>svget_neonq_u8</code>, and from there we can build scalar bitmasks efficiently with NEON operations (masking plus pairwise additions). Repeating this over four 16-byte chunks gives the two 64-bit masks needed for a 64-byte block.</p>
<p>I wanted to quickly run my benchmarks on an AWS Graviton 4. I used LLVM clang 20 which was readily available in the images that AWS makes available (I picked RedHat 10).</p>
<p>The AWS Graviton 4 processor is a Neoverse V2 processor. Google has its own Neoverse V2 processors in its cloud. In my tests, it ran at 2.8 GHz.</p>
<p>My benchmark generates a random string of 1 MiB and computes the bitmaps indicating the positions of the characters. <a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/04/17/benchmark">It is available on GitHub</a>. My results are as follow.</p>
<table style="width: 100%; border-collapse: collapse; margin: 1.2rem 0; border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<thead style="background: #f6f8fa; color: #24292f;">
<tr>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">method</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">GB/s</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">instructions/byte</th>
<th style="text-align: left; font-weight: 600; border-bottom: 2px solid #d0d7de; padding: 0.65rem 0.8rem; vertical-align: top;">instructions/cycle</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">simdjson (NEON)</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">15.5</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">0.75</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">4.16</td>
</tr>
<tr style="background: #fbfcfd;">
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">SVE/SVE2 (new!)</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">16.0</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;"> 0.55</td>
<td style="padding: 0.65rem 0.8rem; border-bottom: 1px solid #eaeef2; vertical-align: top;">3.13</td>
</tr>
</tbody>
</table>
<p>So the SVE/SVE2 approach is faster than the NEON equivalent and uses 25% fewer instructions, and that&#8217;s without any kind of fancy optimization. Importantly, the code is relatively simple thanks to the <code>match</code> instruction.</p>
<p>It might be that the SVE2 function <code>match</code> is the fastest way to match characters on ARM processors.</p>
<p><em>Credit</em>: This post was motivated by a sketch by user <code>liuyang-664</code> on GitHub.</p>
<h2>References</h2>
<p>Langdale, G., &amp; Lemire, D. (2019). <a href="https://doi.org/10.1002/spe.3396">Parsing gigabytes of JSON per second</a>. The VLDB Journal, 28(6), 941-960.</p>
<p>Koekkoek, J., &amp; Lemire, D. (2025). <a href="https://doi.org/10.1002/spe.3396">Parsing millions of DNS records per second</a>. Software: Practice and Experience, 55(4), 778-788.</p>
<p>Lemire, D. (2025). <a href="https://doi.org/10.1002/spe.3420">Scanning HTML at Tens of Gigabytes Per Second on ARM Processors</a>. Software: Practice and Experience, 55(7), 1256-1265.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/04/19/the-fastest-way-to-match-characters-on-arm-processors/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>A brief history of C/C++ programming languages</title>
		<link>https://lemire.me/blog/2026/04/09/a-brief-history-of-c-c-programming-languages/</link>
					<comments>https://lemire.me/blog/2026/04/09/a-brief-history-of-c-c-programming-languages/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Thu, 09 Apr 2026 14:58:53 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22574</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/04/Capture-decran-le-2026-04-09-a-10.54.35-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Initially, we had languages like Fortran (1957), Pascal (1970), and C (1972). Fortran was designed for number crunching and scientific computing. Pascal was restrictive with respect to low-level access (it was deliberately &#8220;safe&#8221;, as meant for teaching structured programming). So C won out as a language that allowed low-level/unsafe programming (pointer arithmetic, direct memory access) &#8230; <a href="https://lemire.me/blog/2026/04/09/a-brief-history-of-c-c-programming-languages/" class="more-link">Continue reading <span class="screen-reader-text">A brief history of C/C++ programming languages</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/04/Capture-decran-le-2026-04-09-a-10.54.35-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><div data-rbd-draggable-context-id="2" data-rbd-draggable-id="867nr">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="867nr-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="867nr-0-0"><span data-offset-key="867nr-0-0">Initially, we had languages like Fortran (1957), Pascal (1970), and C (1972). Fortran was designed for number crunching and scientific computing. Pascal was restrictive with respect to low-level access (it was deliberately &#8220;safe&#8221;, as meant for teaching structured programming). So C won out as a language that allowed low-level/unsafe programming (pointer arithmetic, direct memory access) while remaining general-purpose enough for systems work like Unix. To be fair, Pascal had descendants that are still around, but C clearly dominated.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="789jh">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="789jh-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="789jh-0-0"><span data-offset-key="789jh-0-0">Object-oriented programming became viewed as the future in the 1980s and 1990s. It turned into some kind of sect.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="coano">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="coano-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="coano-0-0"><span data-offset-key="coano-0-0">But C was not object-oriented.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="b8gkl">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="b8gkl-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="b8gkl-0-0"><span data-offset-key="b8gkl-0-0">So we got C++, which began as &#8220;C with Classes&#8221;. C++ had templates, enabling generic programming and compile-time metaprogramming. This part of the language makes C++ quite powerful, but somewhat difficult to master (with crazy error messages).</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="35kf6">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="35kf6-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="35kf6-0-0"><span data-offset-key="35kf6-0-0">Both C and C++ became wildly successful, but writing portable applications remained difficult — you often had to target Windows or a specific Unix variant. This was a problem for a company like Sun Microsystems that sold Unix boxes and wanted to compete against the juggernaut that Microsoft was becoming.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="4te26">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="4te26-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="4te26-0-0"><span data-offset-key="4te26-0-0">So Java came along in 1995. It was positioned as a safe, portable alternative to C++: it eliminated raw pointer arithmetic, added mandatory garbage collection, array bounds checking everywhere, and ran on a virtual machine (JVM) with just-in-time compilation for performance.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="3u5f8">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="3u5f8-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="3u5f8-0-0"><span data-offset-key="3u5f8-0-0">The &#8220;write once, run anywhere&#8221; promise addressed C/C++ portability pain points directly. To this day, Java remains a strong solution for writing portable enterprise and server-side code.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="cmrnd">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="cmrnd-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="cmrnd-0-0"><span data-offset-key="cmrnd-0-0">We also got JavaScript in 1995. Despite the name, it has almost nothing in common with Java semantically. It is best viewed as separate from the C/C++ branch. Python is similarly quite different.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="2kbg">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="2kbg-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="2kbg-0-0"><span data-offset-key="2kbg-0-0">Microsoft would eventually come up with C# in 2000. It belongs to the same C-family syntax tradition as C++ and Java, but with support for ahead-of-time compilation in modern .NET. It also allows guarded pointer access within explicitly marked unsafe scopes. At this point, C# can be seen as &#8220;C++ with garbage collection&#8221; in spirit. It even competes against C++ in the game industry thanks to Unity.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="3lqi1">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="3lqi1-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="3lqi1-0-0"><span data-offset-key="3lqi1-0-0">Google came up with Go. It is much like a simpler, modern C: garbage-collected, with built-in bounds checking on slices/arrays, and pointers allowed but without arbitrary arithmetic in safe code (the unsafe package exists for low-level needs).</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="79ppr">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="79ppr-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="79ppr-0-0"><span data-offset-key="79ppr-0-0">Later, Apple came up with Swift. It has C++-like performance and syntax goals but adds modern safety features (bounds checking by default, integer overflow panics in debug mode) and uses Automatic Reference Counting (ARC) for memory management. Swift replaced Objective-C but I still view it as a C++ successor.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="5b6nn">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="5b6nn-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="5b6nn-0-0"><span data-offset-key="5b6nn-0-0">At about the same time, we got Rust. Like Swift, it drops the generational garbage collection from Java, C# and Go. It relies instead on compile-time ownership and borrowing rules, with the tradeoff that you can leak memory with reference cycles. We also got Zig  which makes memory usage fully explicit.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="dhqmp">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="dhqmp-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="dhqmp-0-0"><span data-offset-key="dhqmp-0-0">I think that it is fairer to describe Rust and Zig as descendants of C rather than C++. Both are much more powerful than C, of course&#8230; and the evolution of programming languages is complex. Still. They are C-like programming languages.</span></div>
</div>
</div>
<div data-rbd-draggable-context-id="2" data-rbd-draggable-id="34ia5">
<div class="longform-unstyled" data-block="true" data-editor="fhuhe" data-offset-key="34ia5-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="34ia5-0-0"><span data-offset-key="34ia5-0-0">To this day, in much of the industry, the dominant programming languages for performance-critical, systems, enterprise, and infrastructure work remain C, C++, Java, and C#. By the Lindy effect (the longer something has survived, the longer it is likely to continue surviving), these languages, especially C, now over 50 years old, are still going to be around for a long time.</span></div>
</div>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/04/09/a-brief-history-of-c-c-programming-languages/feed/</wfw:commentRss>
			<slash:comments>13</slash:comments>
		
		
			</item>
		<item>
		<title>Can your AI rewrite your code in assembly?</title>
		<link>https://lemire.me/blog/2026/04/05/can-your-ai-rewrite-your-code-in-assembly/</link>
					<comments>https://lemire.me/blog/2026/04/05/can-your-ai-rewrite-your-code-in-assembly/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sun, 05 Apr 2026 21:16:14 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22568</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/04/Capture-decran-le-2026-04-05-a-17.13.58-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Suppose you have several strings and you want to count the number of instances of the character ! in your strings. In C++, you might solve the problem as follows if you are an old-school programmer. size_t c = 0; for (const auto &#38;str : strings) { c += std::count(str.begin(), str.end(), '!'); } You can &#8230; <a href="https://lemire.me/blog/2026/04/05/can-your-ai-rewrite-your-code-in-assembly/" class="more-link">Continue reading <span class="screen-reader-text">Can your AI rewrite your code in assembly?</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/04/Capture-decran-le-2026-04-05-a-17.13.58-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Suppose you have several strings and you want to count the number of instances of the character <code>!</code> in your strings. In C++, you might solve the problem as follows if you are an old-school programmer.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #204a87; font-weight: bold;">size_t</span> <span style="color: #000;">c</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span>
<span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">const</span> <span style="color: #204a87; font-weight: bold;">auto</span> <span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">str</span> <span style="color: #ce5c00; font-weight: bold;">:</span> <span style="color: #000;">strings</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
    <span style="color: #000;">c</span> <span style="color: #ce5c00; font-weight: bold;">+=</span> <span style="color: #000;">std</span><span style="color: #ce5c00; font-weight: bold;">::</span><span style="color: #000;">count</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">str</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">begin</span><span style="color: #000; font-weight: bold;">(),</span> <span style="color: #000;">str</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">end</span><span style="color: #000; font-weight: bold;">(),</span> <span style="color: #4e9a06;">'!'</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>You can also get fancier with ranges.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">const</span> <span style="color: #204a87; font-weight: bold;">auto</span> <span style="color: #ce5c00; font-weight: bold;">&amp;</span><span style="color: #000;">str</span> <span style="color: #ce5c00; font-weight: bold;">:</span> <span style="color: #000;">strings</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
    <span style="color: #000;">c</span> <span style="color: #ce5c00; font-weight: bold;">+=</span> <span style="color: #000;">std</span><span style="color: #ce5c00; font-weight: bold;">::</span><span style="color: #000;">ranges</span><span style="color: #ce5c00; font-weight: bold;">::</span><span style="color: #000;">count</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">str</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #4e9a06;">'!'</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>And so forth.</p>
<p>But what if you want to go faster? Maybe you&#8217;d want to rewrite this function in assembly. I decided to do so, and to have fun using both Grok and Claude as my AIs, setting up a friendly competition.</p>
<p>I started with my function and then I asked AIs to optimize it in assembly. Importantly, they knew which machine I was on, so they started to write ARM assembly.</p>
<p>By repeated prompting, I got the following functions.</p>
<ul>
<li><code>count_classic</code>: Uses C++ standard library <code>std::count</code> for reference.</li>
<li><code>count_assembly</code>: A basic ARM64 assembly loop (byte-by-byte comparison). Written by Grok.</li>
<li><code>count_assembly_claude</code>: Claude&#8217;s SIMD-optimized version using NEON instructions (16-byte chunks).</li>
<li><code>count_assembly_grok</code>: Grok&#8217;s optimized version (32-byte chunks).</li>
<li><code>count_assembly_claude_2</code>: Claude&#8217;s further optimized version (64-byte chunks with multiple accumulators).</li>
<li><code>count_assembly_grok_2</code>: Grok&#8217;s latest version (64-byte chunks with improved accumulator handling).</li>
<li><code>count_assembly_claude_3</code>: Claude&#8217;s most advanced version with additional optimizations.</li>
</ul>
<p>You get the idea.</p>
<p>So, how is the performance? I use random strings of up to 1 kilobyte. In all cases, I test that the functions provide the correct count. I did not closely examine the code, so it is possible that mistakes could be hiding in the code.</p>
<p>I record the average number of instructions per string.</p>
<table>
<thead>
<tr>
<th>name</th>
<th>instructions/string</th>
</tr>
</thead>
<tbody>
<tr>
<td>classic C++</td>
<td>1200</td>
</tr>
<tr>
<td>claude assembly</td>
<td>250</td>
</tr>
<tr>
<td>grok assembly</td>
<td>204</td>
</tr>
<tr>
<td>claude assembly 2</td>
<td>183</td>
</tr>
<tr>
<td>grok assembly 2</td>
<td>176</td>
</tr>
<tr>
<td>claude assembly 3</td>
<td>154</td>
</tr>
</tbody>
</table>
<p>By repeated optimization, I reduced the number of instructions by a factor of eight. The running time decreases similarly.</p>
<p>Can we get the AIs to rewrite the best option in C? Yes, although you need SIMD intrinsics. So there is no benefit to leaving the code in assembly in this instance.</p>
<p>An open question is whether the AIs could find optimizations that are not possible if we use a higher-level language like C or C++. It is an intriguing question that I will seek to answer later. For the time being, the AIs can beat my C++ compiler!</p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2026/04/02/benchmark/benchmarks/benchmark.cpp">My source code is available</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/04/05/can-your-ai-rewrite-your-code-in-assembly/feed/</wfw:commentRss>
			<slash:comments>19</slash:comments>
		
		
			</item>
		<item>
		<title>A Fast Immutable Map in Go</title>
		<link>https://lemire.me/blog/2026/03/29/a-fast-immutable-map-in-go/</link>
					<comments>https://lemire.me/blog/2026/03/29/a-fast-immutable-map-in-go/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sun, 29 Mar 2026 18:18:01 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22564</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/03/Capture-decran-le-2026-03-29-a-14.16.33-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Consider the following problem. You have a large set of strings, maybe millions. You need to map these strings to 8-byte integers (uint64). These integers are given to you. If you are working in Go, the standard solution is to create a map. The construction is trivial, something like the following loop. m := make(map[string]uint64, &#8230; <a href="https://lemire.me/blog/2026/03/29/a-fast-immutable-map-in-go/" class="more-link">Continue reading <span class="screen-reader-text">A Fast Immutable Map in Go</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/03/Capture-decran-le-2026-03-29-a-14.16.33-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Consider the following problem. You have a large set of strings, maybe millions. You need to map these strings to 8-byte integers (<code>uint64</code>). These integers are given to you.</p>
<p>If you are working in Go, the standard solution is to create a map. The construction is trivial, something like the following loop.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #000;">m</span> <span style="color: #ce5c00; font-weight: bold;">:=</span> <span style="color: #204a87;">make</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">map</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #204a87; font-weight: bold;">string</span><span style="color: #000; font-weight: bold;">]</span><span style="color: #204a87; font-weight: bold;">uint64</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">N</span><span style="color: #000; font-weight: bold;">)</span>
<span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">k</span> <span style="color: #ce5c00; font-weight: bold;">:=</span> <span style="color: #204a87; font-weight: bold;">range</span> <span style="color: #000;">keys</span> <span style="color: #000; font-weight: bold;">{</span>
    <span style="color: #000;">m</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">k</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #000; font-weight: bold;">=</span> <span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">]</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>One downside is that the map may use over 50 bytes per entry.</p>
<p>In important scenarios, we might have the following conditions. The map is large (a million of entries or more), you do not need to modify it dynamically (it is immutable), and all queried keys are in the set. In such conditions, you can reduce the memory usage down to almost the size of the keys, so about 8 bytes per entry. One fast technique is the <a href="https://arxiv.org/abs/2201.01174">binary fuse filters</a>.</p>
<p>I implemented it as a Go library called <a href="https://github.com/lemire/constmap">constmap</a> that provides an immutable map from strings to <code>uint64</code> values using binary fuse filters. This data structure is ideal when you have a fixed set of keys at construction time and need fast, memory-efficient lookups afterward. You can even construct the map once, save it to disk so you do not pay the cost of constructing the map each time you need it.</p>
<p>The usage is just as simple.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #204a87; font-weight: bold;">package</span> <span style="color: #000;">main</span>

<span style="color: #204a87; font-weight: bold;">import</span> <span style="color: #000; font-weight: bold;">(</span>
    <span style="color: #4e9a06;">"fmt"</span>
    <span style="color: #4e9a06;">"log"</span>

    <span style="color: #4e9a06;">"github.com/lemire/constmap"</span>
<span style="color: #000; font-weight: bold;">)</span>

<span style="color: #204a87; font-weight: bold;">func</span> <span style="color: #000;">main</span><span style="color: #000; font-weight: bold;">()</span> <span style="color: #000; font-weight: bold;">{</span>
    <span style="color: #000;">keys</span> <span style="color: #ce5c00; font-weight: bold;">:=</span> <span style="color: #000; font-weight: bold;">[]</span><span style="color: #204a87; font-weight: bold;">string</span><span style="color: #000; font-weight: bold;">{</span><span style="color: #4e9a06;">"apple"</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #4e9a06;">"banana"</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #4e9a06;">"cherry"</span><span style="color: #000; font-weight: bold;">}</span>
    <span style="color: #000;">values</span> <span style="color: #ce5c00; font-weight: bold;">:=</span> <span style="color: #000; font-weight: bold;">[]</span><span style="color: #204a87; font-weight: bold;">uint64</span><span style="color: #000; font-weight: bold;">{</span><span style="color: #0000cf; font-weight: bold;">100</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">200</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">300</span><span style="color: #000; font-weight: bold;">}</span>

    <span style="color: #000;">cm</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">err</span> <span style="color: #ce5c00; font-weight: bold;">:=</span> <span style="color: #000;">constmap</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">New</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">keys</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">values</span><span style="color: #000; font-weight: bold;">)</span>
    <span style="color: #204a87; font-weight: bold;">if</span> <span style="color: #000;">err</span> <span style="color: #ce5c00; font-weight: bold;">!=</span> <span style="color: #204a87; font-weight: bold;">nil</span> <span style="color: #000; font-weight: bold;">{</span>
        <span style="color: #000;">log</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Fatal</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">err</span><span style="color: #000; font-weight: bold;">)</span>
    <span style="color: #000; font-weight: bold;">}</span>

    <span style="color: #000;">fmt</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Println</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">cm</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">Map</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #4e9a06;">"banana"</span><span style="color: #000; font-weight: bold;">))</span> <span style="color: #8f5902; font-style: italic;">// 200</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>The construction time is higher (as expected for any compact data structure), but lookups are optimized for speed. I ran benchmarks on my Apple M4 Max processor to compare constmap lookups against Go&#8217;s built-in <code>map[string]uint64</code>. The test uses 1 million keys.</p>
<table>
<thead>
<tr>
<th>Data Structure</th>
<th>Lookup Time</th>
<th>Memory Usage</th>
</tr>
</thead>
<tbody>
<tr>
<td>ConstMap</td>
<td>7.4 ns/op</td>
<td>9 bytes/key</td>
</tr>
<tr>
<td>Go Map</td>
<td>20 ns/op</td>
<td>56 bytes/key</td>
</tr>
</tbody>
</table>
<p>ConstMap is nearly 3 times faster than Go&#8217;s standard map for lookups! And we reduced the memory usage by a factor of 6.</p>
<p>The ConstMap may not always be faster, but it should always use significantly less memory. If it can reside in CPU cache while the map cannot, then it will be significantly faster.</p>
<p><strong>Source Code</strong> The implementation is available on GitHub: <a href="https://github.com/lemire/constmap">github.com/lemire/constmap</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/03/29/a-fast-immutable-map-in-go/feed/</wfw:commentRss>
			<slash:comments>15</slash:comments>
		
		
			</item>
		<item>
		<title>JSON and C++26 compile-time reflection: a talk</title>
		<link>https://lemire.me/blog/2026/03/26/json-and-c26-compile-time-reflection-a-talk/</link>
					<comments>https://lemire.me/blog/2026/03/26/json-and-c26-compile-time-reflection-a-talk/#respond</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Thu, 26 Mar 2026 00:29:38 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22553</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/03/Capture-decran-le-2026-03-25-a-20.36.07-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />The next C++ standard (C++26) is getting exciting new features. One of these features is compile-time reflection. It is ideally suited to serialize and deserialize data at high speed. To test it out, we extended our fast JSON library (simdjson) and we gave a talk at CppCon 2025. The video is out on YouTube. Our &#8230; <a href="https://lemire.me/blog/2026/03/26/json-and-c26-compile-time-reflection-a-talk/" class="more-link">Continue reading <span class="screen-reader-text">JSON and C++26 compile-time reflection: a talk</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/03/Capture-decran-le-2026-03-25-a-20.36.07-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>The next C++ standard (C++26) is getting exciting new features. <a href="https://lemire.me/blog/2025/06/22/c26-will-include-compile-time-reflection-why-should-you-care/">One of these features is compile-time reflection</a>. It is ideally suited to serialize and deserialize data at high speed. To test it out, we extended our fast JSON library (<a href="https://github.com/simdjson/simdjson">simdjson</a>) and we gave a talk at CppCon 2025. The video is out on YouTube.</p>
<p><iframe loading="lazy" title="YouTube video player" src="https://www.youtube.com/embed/Mcgk3CxHYMs?si=NylINo46stKT8-SD&amp;start=26" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>
<p><a href="https://simdjson.github.io/simdjson_talks/cppcon2025/cppcon_2025_slides.html">Our slides are also available</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/03/26/json-and-c26-compile-time-reflection-a-talk/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How many branches can your CPU predict?</title>
		<link>https://lemire.me/blog/2026/03/18/how-many-branches-can-your-cpu-predict/</link>
					<comments>https://lemire.me/blog/2026/03/18/how-many-branches-can-your-cpu-predict/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Wed, 18 Mar 2026 21:52:53 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22543</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/03/Capture-decran-le-2026-03-18-a-17.52.22-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Modern processors have the ability to execute many instructions per cycle, on a single core. To be able to execute many instructions per cycle in practice, processors predict branches. I have made the point over the years that modern CPUs have an incredible ability to predict branches. It makes benchmarking difficult because if you test &#8230; <a href="https://lemire.me/blog/2026/03/18/how-many-branches-can-your-cpu-predict/" class="more-link">Continue reading <span class="screen-reader-text">How many branches can your CPU predict?</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/03/Capture-decran-le-2026-03-18-a-17.52.22-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Modern processors have the ability to execute many instructions per cycle, on a single core. To be able to execute many instructions per cycle in practice, processors predict branches. I have made the point over the years that modern CPUs have an incredible ability to <a href="https://lemire.me/blog/2019/10/16/benchmarking-is-hard-processors-learn-to-predict-branches/">predict branches</a>.</p>
<p>It makes benchmarking difficult because if you test on small datasets, you can get surprising results that might not work on real data.</p>
<p>My go-to benchmark is a function like so:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code>while (howmany != 0) {
    val = generate_random_value()
    if(val is odd) write to buffer
    decrement howmany
}
</code></pre>
</div>
<p>The processor tries to predict the branch (<code>if</code> clause). Because we use random values, the processor should mispredict one time out of two.</p>
<p>However, if we repeat multiple times the benchmark, always using the same random values, the processor learns the branches. How many can processors learn? I test using three recent processors.</p>
<ul>
<li>The AMD Zen 5 processor can predict perfectly 30,000 branches.</li>
<li>The Apple M4 processor can predict perfectly 10,000 branches.</li>
<li>Intel Emerald Rapids can predict perfectly 5,000 branches.</li>
</ul>
<p>Once more I am disappointed by Intel. AMD is doing wonderfully well on this benchmark.</p>
<p><a href="http://lemire.me/blog/wp-content/uploads/2026/03/branch_mispredictions-2.png"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-22549" src="http://lemire.me/blog/wp-content/uploads/2026/03/branch_mispredictions-2.png" alt="" width="1000" height="600" srcset="https://lemire.me/blog/wp-content/uploads/2026/03/branch_mispredictions-2.png 1000w, https://lemire.me/blog/wp-content/uploads/2026/03/branch_mispredictions-2-300x180.png 300w, https://lemire.me/blog/wp-content/uploads/2026/03/branch_mispredictions-2-768x461.png 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></a><img decoding="async" src="branch_mispredictions.png" alt="" /></p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/03/18/benchmark">My source code is available</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/03/18/how-many-branches-can-your-cpu-predict/feed/</wfw:commentRss>
			<slash:comments>22</slash:comments>
		
		
			</item>
		<item>
		<title>Prefix sums at tens of gigabytes per second with ARM NEON</title>
		<link>https://lemire.me/blog/2026/03/08/prefix-sums-at-tens-of-gigabytes-per-second-with-arm-neon/</link>
					<comments>https://lemire.me/blog/2026/03/08/prefix-sums-at-tens-of-gigabytes-per-second-with-arm-neon/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sun, 08 Mar 2026 20:09:48 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22530</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/03/Capture-decran-le-2026-03-08-a-16.07.29-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />Suppose that you have a record of your sales per day. You might want to get a running record where, for each day, you are told how many sales you have made since the start of the year. day sales per day running sales 1 10$ 10 $ 2 15$ 25 $ 3 5$ 30 &#8230; <a href="https://lemire.me/blog/2026/03/08/prefix-sums-at-tens-of-gigabytes-per-second-with-arm-neon/" class="more-link">Continue reading <span class="screen-reader-text">Prefix sums at tens of gigabytes per second with ARM NEON</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/03/Capture-decran-le-2026-03-08-a-16.07.29-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>Suppose that you have a record of your sales per day. You might want to get a running record where, for each day, you are told how many sales you have made since the start of the year.</p>
<table>
<thead>
<tr>
<th>day</th>
<th>sales per day</th>
<th>running sales</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>10$</td>
<td>10 $</td>
</tr>
<tr>
<td>2</td>
<td>15$</td>
<td>25 $</td>
</tr>
<tr>
<td>3</td>
<td>5$</td>
<td>30 $</td>
</tr>
</tbody>
</table>
<p>Such an operation is called a prefix sum or a scan.</p>
<p>Implementing it in C is not difficult. It is a simple loop.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code>  <span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">size_t</span> <span style="color: #000;">i</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">;</span> <span style="color: #000;">i</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">length</span><span style="color: #000; font-weight: bold;">;</span> <span style="color: #000;">i</span><span style="color: #ce5c00; font-weight: bold;">++</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
    <span style="color: #000;">data</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">+=</span> <span style="color: #000;">data</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #000;">i</span> <span style="color: #ce5c00; font-weight: bold;">-</span> <span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">];</span>
  <span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>How fast can this function be? We can derive a speed limit rather simply: to compute the current value, you must have computed the previous one, and so forth.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #000;">data</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">-&gt;</span> <span style="color: #000;">data</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">-&gt;</span> <span style="color: #000;">data</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">2</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">-&gt;</span> <span style="color: #000; font-weight: bold;">...</span>
</code></pre>
</div>
<p>At best, you require one CPU cycle per entry in your table. Thus, on a 4 GHz processor, you might process 4 billion integer values per second. It is an upper bound but you might be able to reach close to it in practice on many modern systems. Of course, there are other instructions involved such as loads, stores and branching, but our processors can execute many instructions per cycle and they can predict branches effectively. So you should be able to process billions of integers per second on most processors today.</p>
<p>Not bad! But can we do better?</p>
<p>We can use SIMD instructions. SIMD instructions are special instructions that process several values at once. All 64-bit ARM processors support NEON instructions. NEON instructions can process four integers at once, if they are packed in one SIMD register.</p>
<p>But how do you do the prefix sum on a 4-value register? You can do it with two shifts and two additions. In theory, it scales as log(N) where N is the number elements in a vector register.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code>input   = [A B   C     D]
shift1  = [0 A   B     C]
sum1    = [A A+B B+C   C+D]
shift2  = [0 0   A     B+A]
result  = [A A+B A+B+C A+B+C+D]
</code></pre>
</div>
<p>You can then extract the last value (<code>A+B+C+D</code>) and broadcast it to all positions so that you can add it to the next value.</p>
<p>Is this faster than the scalar approach? We have 4 instructions in sequence, plus at least one instruction if you want to use the total sum in the next block of four values.</p>
<p>Thus the SIMD approach might be worse. It is disappointing.</p>
<p>A solution might be the scale up over many more integer values.</p>
<p>Consider ARM NEON which has interleaved load and store instructions. If you can load 16 values at once, and get all of the first values together, all of the second values together, and so forth.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #000;">original</span> <span style="color: #000;">data</span> <span style="color: #000; font-weight: bold;">:</span> <span style="color: #000;">ABCD</span> <span style="color: #000;">EFGH</span> <span style="color: #000;">IJKL</span> <span style="color: #000;">MNOP</span>
<span style="color: #000;">loaded</span> <span style="color: #000;">data</span>   <span style="color: #000; font-weight: bold;">:</span> <span style="color: #000;">AEIM</span> <span style="color: #000;">BFJN</span> <span style="color: #000;">CGKO</span> <span style="color: #000;">DHLP</span>
</code></pre>
</div>
<p>Then I can do a prefix sum over the four blocks in parallel. It takes three instructions. At the end of the three instructions, we have one register which contains the local sums:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code>A+B+C+D E+F+G+H I+J+K+L M+N+O+P
</code></pre>
</div>
<p>And then we can apply our prefix sum recipe on this register (4 instructions). You might end up with something like 8 sequential instructions per block of 16 values.</p>
<p>It is theoretically twice as fast as the scalar approach.</p>
<p>In C with instrinsics, you might code it as follows.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #204a87; font-weight: bold;">void</span> <span style="color: #000;">neon_prefixsum_fast</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">uint32_t</span> <span style="color: #ce5c00; font-weight: bold;">*</span><span style="color: #000;">data</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #204a87; font-weight: bold;">size_t</span> <span style="color: #000;">length</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
  <span style="color: #000;">uint32x4_t</span> <span style="color: #000;">zero</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">{</span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">};</span>
  <span style="color: #000;">uint32x4_t</span> <span style="color: #000;">prev</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000; font-weight: bold;">{</span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">};</span>

  <span style="color: #204a87; font-weight: bold;">for</span> <span style="color: #000; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">size_t</span> <span style="color: #000;">i</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">;</span> <span style="color: #000;">i</span> <span style="color: #ce5c00; font-weight: bold;">&lt;</span> <span style="color: #000;">length</span> <span style="color: #ce5c00; font-weight: bold;">/</span> <span style="color: #0000cf; font-weight: bold;">16</span><span style="color: #000; font-weight: bold;">;</span> <span style="color: #000;">i</span><span style="color: #ce5c00; font-weight: bold;">++</span><span style="color: #000; font-weight: bold;">)</span> <span style="color: #000; font-weight: bold;">{</span>
    <span style="color: #000;">uint32x4x4_t</span> <span style="color: #000;">vals</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vld4q_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">data</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">16</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">);</span>

    <span style="color: #8f5902; font-style: italic;">// Prefix sum inside each transposed ("vertical") lane</span>
    <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vaddq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">],</span> <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">]);</span>
    <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">2</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vaddq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">2</span><span style="color: #000; font-weight: bold;">],</span> <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">]);</span>
    <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">3</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vaddq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">3</span><span style="color: #000; font-weight: bold;">],</span> <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">2</span><span style="color: #000; font-weight: bold;">]);</span>

    <span style="color: #8f5902; font-style: italic;">// Now vals.val[3] contains the four local prefix sums:</span>
    <span style="color: #8f5902; font-style: italic;">//   vals.val[3] = [s0=A+B+C+D, s1=E+F+G+H, </span>
    <span style="color: #8f5902; font-style: italic;">//                  s2=I+J+K+L, s3=M+N+O+P]</span>

    <span style="color: #8f5902; font-style: italic;">// Compute prefix sum across the four local sums </span>
    <span style="color: #000;">uint32x4_t</span> <span style="color: #000;">off</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vextq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">zero</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">3</span><span style="color: #000; font-weight: bold;">],</span> <span style="color: #0000cf; font-weight: bold;">3</span><span style="color: #000; font-weight: bold;">);</span>
    <span style="color: #000;">uint32x4_t</span> <span style="color: #000;">ps</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vaddq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">3</span><span style="color: #000; font-weight: bold;">],</span> <span style="color: #000;">off</span><span style="color: #000; font-weight: bold;">);</span>       
    <span style="color: #000;">off</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vextq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">zero</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">ps</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">2</span><span style="color: #000; font-weight: bold;">);</span>                      
    <span style="color: #000;">ps</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vaddq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">ps</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">off</span><span style="color: #000; font-weight: bold;">);</span>

    <span style="color: #8f5902; font-style: italic;">// Now ps contains cumulative sums across the four groups</span>
    <span style="color: #8f5902; font-style: italic;">// Add the incoming carry from the previous 16-element block</span>
    <span style="color: #000;">ps</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vaddq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">ps</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">prev</span><span style="color: #000; font-weight: bold;">);</span>

    <span style="color: #8f5902; font-style: italic;">// Prepare carry for next block: broadcast the last lane of ps</span>
    <span style="color: #000;">prev</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vdupq_laneq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">ps</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">3</span><span style="color: #000; font-weight: bold;">);</span>

    <span style="color: #8f5902; font-style: italic;">// The add vector to apply to the original lanes is the </span>
    <span style="color: #8f5902; font-style: italic;">// prefix up to previous group</span>
    <span style="color: #000;">uint32x4_t</span> <span style="color: #000;">add</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vextq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">prev</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">ps</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">3</span><span style="color: #000; font-weight: bold;">);</span>  

    <span style="color: #8f5902; font-style: italic;">// Apply carry/offset to each of the four transposed lanes</span>
    <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vaddq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">0</span><span style="color: #000; font-weight: bold;">],</span> <span style="color: #000;">add</span><span style="color: #000; font-weight: bold;">);</span>
    <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vaddq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">1</span><span style="color: #000; font-weight: bold;">],</span> <span style="color: #000;">add</span><span style="color: #000; font-weight: bold;">);</span>
    <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">2</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vaddq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">2</span><span style="color: #000; font-weight: bold;">],</span> <span style="color: #000;">add</span><span style="color: #000; font-weight: bold;">);</span>
    <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">3</span><span style="color: #000; font-weight: bold;">]</span> <span style="color: #ce5c00; font-weight: bold;">=</span> <span style="color: #000;">vaddq_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">.</span><span style="color: #000;">val</span><span style="color: #000; font-weight: bold;">[</span><span style="color: #0000cf; font-weight: bold;">3</span><span style="color: #000; font-weight: bold;">],</span> <span style="color: #000;">add</span><span style="color: #000; font-weight: bold;">);</span>

    <span style="color: #8f5902; font-style: italic;">// Store back the four lanes (interleaved)</span>
    <span style="color: #000;">vst4q_u32</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">data</span> <span style="color: #ce5c00; font-weight: bold;">+</span> <span style="color: #0000cf; font-weight: bold;">16</span> <span style="color: #ce5c00; font-weight: bold;">*</span> <span style="color: #000;">i</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">vals</span><span style="color: #000; font-weight: bold;">);</span>
  <span style="color: #000; font-weight: bold;">}</span>

  <span style="color: #000;">scalar_prefixsum_leftover</span><span style="color: #000; font-weight: bold;">(</span><span style="color: #000;">data</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #000;">length</span><span style="color: #000; font-weight: bold;">,</span> <span style="color: #0000cf; font-weight: bold;">16</span><span style="color: #000; font-weight: bold;">);</span>
<span style="color: #000; font-weight: bold;">}</span>
</code></pre>
</div>
<p>Let us try it out on an Apple M4 processor (4.5 GHz).</p>
<table>
<thead>
<tr>
<th>method</th>
<th>billions of values/s</th>
</tr>
</thead>
<tbody>
<tr>
<td>scalar</td>
<td>3.9</td>
</tr>
<tr>
<td>naive SIMD</td>
<td>3.6</td>
</tr>
<tr>
<td>fast SIMD</td>
<td>8.9</td>
</tr>
</tbody>
</table>
<p>So the SIMD approach is about 2.3 times faster than the scalar approach. Not bad.</p>
<p><a href="https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2026/03/06/benchmark">My source code is available on GitHub</a>.</p>
<p><strong>Appendix. Instrinsics</strong></p>
<table>
<thead>
<tr>
<th>Intrinsic</th>
<th>What it does</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>vld4q_u32</code></td>
<td>Loads 16 consecutive 32-bit unsigned integers from memory and deinterleaves them into 4 separate <code>uint32x4_t</code> vectors (lane 0 = elements 0,4,8,12,…; lane 1 = 1,5,9,13,… etc.).</td>
</tr>
<tr>
<td><code>vaddq_u32</code></td>
<td>Adds corresponding 32-bit unsigned integer lanes from two vectors (<code>a[i] + b[i]</code> for each of 4 lanes).</td>
</tr>
<tr>
<td><code>vextq_u32</code></td>
<td>Extracts (concatenates a and b, then takes 4 lanes starting from lane <code>n</code> of the 8-lane concatenation). Used to implement shifts/rotates by inserting zeros (when <code>a</code> is zero vector).</td>
</tr>
<tr>
<td><code>vdupq_laneq_u32</code></td>
<td>Broadcasts (duplicates) the value from the specified lane (0–3) of the input vector to all 4 lanes of the result.</td>
</tr>
<tr>
<td><code>vdupq_n_u32</code> (implied usage)</td>
<td>Sets all 4 lanes of the result to the same scalar value (commonly used for zero or broadcast).</td>
</tr>
</tbody>
</table>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/03/08/prefix-sums-at-tens-of-gigabytes-per-second-with-arm-neon/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Text formats are everywhere. Why?</title>
		<link>https://lemire.me/blog/2026/03/05/text-formats-are-everywhere-why/</link>
					<comments>https://lemire.me/blog/2026/03/05/text-formats-are-everywhere-why/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Thu, 05 Mar 2026 14:40:58 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22526</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/03/HCp4uWQX0AAhxh0-150x150.jpeg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />The Internet relies on text formats. Thus, we spend a lot of time producing and consuming data encoded in text. Your web pages are HTML. The code running in them is JavaScript, sent as text (JavaScript source), not as already-parsed code. Your emails, including their attachments, are sent as text (your binary files are sent &#8230; <a href="https://lemire.me/blog/2026/03/05/text-formats-are-everywhere-why/" class="more-link">Continue reading <span class="screen-reader-text">Text formats are everywhere. Why?</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/03/HCp4uWQX0AAhxh0-150x150.jpeg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>The Internet relies on text formats. Thus, we spend a lot of time producing and consuming data encoded in text.</p>
<p>Your web pages are HTML. The code running in them is JavaScript, sent as text (JavaScript source), not as already-parsed code. Your emails, including their attachments, are sent as text (your binary files are sent as text).</p>
<p>It does not stop there. The Python code that runs your server is stored as text. It queries data by sending text queries. It often gets back the answer as text that must then be decoded.</p>
<p>JSON is the universal data interchange format online today. We share maps as JSON (GeoJSON).</p>
<p>Not everything is text, of course. There is no common video or image format that is shared as text. Transmissions over the Internet are routinely compressed to binary formats. There are popular binary formats that compete with JSON.<br />
But why is text dominant?</p>
<p>It is not because, back in the 1970s, programmers did not know about binary formats.</p>
<p>In fact, we did not start with text formats. Initially, we worked with raw binary data. Those of us old enough will remember programming in assembly using raw byte values.</p>
<p>Why text won?</p>
<p>1.Text is efficient.</p>
<p>In the XML era, when everything had to be XML, there were countless proposals for binary formats. People were sometimes surprised to find that the binary approach was not much faster in practice. Remember that many text formats date back to an era when computers were much slower. Had text been a performance bottleneck, it would not have spread. Of course, there are cases where text makes things slower. You then have a choice: optimize your code further or transition to another format. Often, both are viable.</p>
<p>It is easy to make wrong assumptions about binary formats, such as that you can consume them without any parsing or validation. If you pick up data from the Internet, you must assume that it could have been sent by an adversary or someone who does not follow your conventions.</p>
<p>2.Text is easy to work with.</p>
<p>If you receive text from a remote source, you can often transform it, index it, search it, quote it, version it… with little effort and without in-depth knowledge of the format. Text is often self-documenting.</p>
<p>In an open world, when you will never speak with the person producing the data, text often makes everything easier and smoother.</p>
<p>If there is an issue to report and the data is in text, you can usually copy-paste the relevant section into a message. Things are much harder with a binary format.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/03/05/text-formats-are-everywhere-why/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>You can use newline characters in URLs</title>
		<link>https://lemire.me/blog/2026/02/28/you-can-use-newline-characters-in-urls/</link>
					<comments>https://lemire.me/blog/2026/02/28/you-can-use-newline-characters-in-urls/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sat, 28 Feb 2026 19:21:39 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22517</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/02/Capture-decran-le-2026-02-28-a-14.21.14-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />We locate web content using special addresses called URLs. We are all familiar with addresses like https://google.com. Sometimes, URLs can get long and they can become difficult to read. Thus, we might be tempted to format them like so in HTML using newline and tab characters, like so: &#60;a href="https://lemire.me/blog/2026/02/21/ how-fast-do-browsers-correct-utf-16-strings/"&#62;my blog post&#60;/a&#62; It will &#8230; <a href="https://lemire.me/blog/2026/02/28/you-can-use-newline-characters-in-urls/" class="more-link">Continue reading <span class="screen-reader-text">You can use newline characters in URLs</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/02/Capture-decran-le-2026-02-28-a-14.21.14-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>We locate web content using special addresses called URLs. We are all familiar with addresses like https://google.com. Sometimes, URLs can get long and they can become difficult to read. Thus, we might be tempted to format them<br />
like so in HTML using newline and tab characters, like so:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #000; font-weight: bold;">&lt;</span><span style="color: #204a87; font-weight: bold;">a</span> <span style="color: #c4a000;">href</span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #4e9a06;">"https://lemire.me/blog/2026/02/21/</span>
<span style="color: #4e9a06;">        how-fast-do-browsers-correct-utf-16-strings/"</span><span style="color: #000; font-weight: bold;">&gt;</span>my blog post<span style="color: #000; font-weight: bold;">&lt;/</span><span style="color: #204a87; font-weight: bold;">a</span><span style="color: #000; font-weight: bold;">&gt;</span>
</code></pre>
</div>
<p>It will work.</p>
<p>Let us refer to the WHATWG URL specification that browsers follow. It makes two statements in sequence.</p>
<ol>
<li>If input contains any ASCII tab or newline, invalid-URL-unit validation error.</li>
<li>Remove all ASCII tab or newline from input.</li>
</ol>
<p>Notice how it reports an error if there is a tab or newline character, but continues anyway? The specification says that <em>A validation error does not mean that the parser terminates</em> and it encourages systems to report errors <em>somewhere</em>. Effectively, the error is ignored although it might be logged. Thus our HTML is fine in practice.</p>
<p>The following is also fine:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #000; font-weight: bold;">&lt;</span><span style="color: #204a87; font-weight: bold;">a</span> <span style="color: #c4a000;">href</span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #4e9a06;">"https://go</span>
<span style="color: #4e9a06;">ogle.c</span>
<span style="color: #4e9a06;">om"</span> <span style="color: #c4a000;">class</span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #4e9a06;">"button"</span><span style="color: #000; font-weight: bold;">&gt;</span>Visit Google<span style="color: #000; font-weight: bold;">&lt;/</span><span style="color: #204a87; font-weight: bold;">a</span><span style="color: #000; font-weight: bold;">&gt;</span>
</code></pre>
</div>
<p>You can also use tabs. But you cannot arbitrarily insert any other whitespace.</p>
<p>Yet there are cases when you can use any ASCII whitespace character: data URLs. Data URLs (also called data URIs) embed small files—like images, text, or other content—directly inside a URL string, instead of linking to an external resource. Data URLs are a <em>special kind</em> of URL and they follow different rules.</p>
<p>A typical data URL might look like <code>data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...</code> where the string <code>iVBORw0KGgoAAAANSUhEUg...</code> is the binary data of the image that has been encoded with base64. Base64 is a text format that can represent any binary content: we use 64 ASCII characters so that each character encodes 6 bits. Your binary email attachments are base64 encoded.</p>
<p>On the web, when decoding a base64 string, you ignore all ASCII whitespaces (including the space character itself). Thus you can embed a PNG image in HTML as follows.</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #000; font-weight: bold;">&lt;</span><span style="color: #204a87; font-weight: bold;">img</span> <span style="color: #c4a000;">src</span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #4e9a06;">"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA
                                 QAAAAECAIAAAAmkwkpAAAAEUl
                                 EQVR4nG</span><span style="color: #4e9a06;">P8z4AATEhsPBwAM9EB
                                 BzDn4UwAAAAASUVORK5CYII="</span> <span style="color: #000; font-weight: bold;">/&gt;</span>
</code></pre>
</div>
<p>This HTML code is valid and will insert a tiny image in your page.</p>
<p>But there is more. A data URL can also be used to insert an SVG image. SVG (Scalable Vector Graphics) is an XML-based vector image format that describes 2D graphics using mathematical paths, shapes, and text instead of pixels.<br />
The following should draw a very simple sunset:</p>
<div class="highlight" style="background: #f8f8f8;">
<pre style="line-height: 125%;"><code><span style="color: #000; font-weight: bold;">&lt;</span><span style="color: #204a87; font-weight: bold;">img</span> <span style="color: #c4a000;">src</span><span style="color: #ce5c00; font-weight: bold;">=</span><span style="color: #4e9a06;">'data:image/svg+xml,</span>
<span style="color: #4e9a06;">&lt;svg width="200" height="200" 
     xmlns="http://www.w3.org/2000/svg"&gt;</span>
<span style="color: #4e9a06;">  &lt;rect width="100%" height="100%" fill="blue" /&gt; 
  &lt;!-- the sky --&gt;</span>
<span style="color: #4e9a06;">  &lt;circle cx="100" cy="110" r="50" fill="yellow" /&gt;  
  &lt;!-- the sun --&gt;</span>
<span style="color: #4e9a06;">  &lt;rect x="0" y="120" width="200" height="80" fill="brown" /&gt;  
  &lt;!-- the ground --&gt;</span>
<span style="color: #4e9a06;">&lt;/svg&gt;'</span> <span style="color: #000; font-weight: bold;">/&gt;</span>
</code></pre>
</div>
<p>Observe how I was able to format the SVG code so that it is readable.</p>
<p><strong>Further reading</strong>: Nizipli, Y., &amp; Lemire, D. (2024). <a href="https://doi.org/10.1002/spe.3296">Parsing millions of URLs per second</a>. <i>Software: Practice and Experience</i>, <i>54</i>(5), 744-758.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/02/28/you-can-use-newline-characters-in-urls/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>How fast do browsers correct UTF-16 strings?</title>
		<link>https://lemire.me/blog/2026/02/21/how-fast-do-browsers-correct-utf-16-strings/</link>
					<comments>https://lemire.me/blog/2026/02/21/how-fast-do-browsers-correct-utf-16-strings/#respond</comments>
		
		<dc:creator><![CDATA[Daniel Lemire]]></dc:creator>
		<pubDate>Sat, 21 Feb 2026 20:07:17 +0000</pubDate>
				<category><![CDATA[]]></category>
		<guid isPermaLink="false">https://lemire.me/blog/?p=22443</guid>

					<description><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/02/Capture-decran-le-2026-02-21-a-15.06.57-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" />JavaScript represents strings using Unicode, like most programming languages today. Each character in a JavaScript string is stored using one or two 16-bit words. The following JavaScript code might surprise some programmers because a single character becomes two 16-bit words. &#62; t="🧰" '🧰' &#62; t.length 2 &#62; t[0] '\ud83e' &#62; t[1] '\uddf0' The convention is &#8230; <a href="https://lemire.me/blog/2026/02/21/how-fast-do-browsers-correct-utf-16-strings/" class="more-link">Continue reading <span class="screen-reader-text">How fast do browsers correct UTF-16 strings?</span></a>]]></description>
										<content:encoded><![CDATA[<img width="150" height="150" src="https://lemire.me/blog/wp-content/uploads/2026/02/Capture-decran-le-2026-02-21-a-15.06.57-150x150.png" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin-bottom: 5px; clear:both;max-width: 100%;" link_thumbnail="" decoding="async" loading="lazy" /><p>JavaScript represents strings using Unicode, like most programming languages today. Each character in a JavaScript string is stored using one or two 16-bit words. The following JavaScript code might surprise some programmers because a single character becomes two 16-bit words.</p>
<pre>&gt; t="&#x1f9f0;"
'&#x1f9f0;'
&gt; t.length
2
&gt; t[0]
'\ud83e'
&gt; t[1]
'\uddf0'
</pre>
<p>The convention is that \uddf0 is the 16-bit value 0xDDF0 also written U+DDF0.</p>
<p>The UTF-16 standard is relatively simple. There are three types of values. high surrogates (the range U+D800 to U+DBFF), low surrogates (U+DC00 to U+DFFF), and all other code units (U+0000–U+D7FF together with U+E000–U+FFFF). A high surrogate must always be followed by a low surrogate, and a low surrogate must always be preceded by a high surrogate.</p>
<p>What happens if you break the rules and have a high surrogate followed by a high surrogate? Then you have an invalid string. We can correct the strings by patching them: we replace the bad values by the replacement character (\ufffd). The replacement character sometimes appears as a question mark.</p>
<p>To correct a broken string in JavaScript, you can call the toWellFormed method.</p>
<pre>&gt; t = '\uddf0\uddf0'
'\uddf0\uddf0'
&gt; t.toWellFormed()
'��'
</pre>
<p>How fast is it?</p>
<p>I wrote a s<a href="https://lemire.github.io/browserwellformed/">mall benchmark</a> that you can test online to measure its speed. I use broken strings of various sizes up to a few kilobytes. I run the benchmarks on my Apple M4 processor using different browsers.</p>
<table>
<tbody>
<tr>
<th>Browser</th>
<th>Speed</th>
</tr>
<tr>
<td>Safari 18.6</td>
<td>1 GiB/s</td>
</tr>
<tr>
<td>Firefox 147</td>
<td>3 GiB/s</td>
</tr>
<tr>
<td>Chrome 145</td>
<td>15 GiB/s</td>
</tr>
</tbody>
</table>
<p>Quite a range of performance! The speed of other chromium-based browsers (Brave and Edge) is much the same as Chrome.</p>
<p>I also tested with JavaScript runtimes.</p>
<table>
<tbody>
<tr>
<th>Engine</th>
<th>Speed</th>
</tr>
<tr>
<td>Node.js v25.5.0</td>
<td>16 GiB/s</td>
</tr>
<tr>
<td>Bun 1.3.9</td>
<td>8.4 GiB/s</td>
</tr>
</tbody>
</table>
<p>Usually Bun is faster than Node, but in this instance, Node is twice as far as Bun.</p>
<p>Thus, we can correct strings in JavaScript at over ten gigabytes per second if you use Chromium-based browsers.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lemire.me/blog/2026/02/21/how-fast-do-browsers-correct-utf-16-strings/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
