<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on icyveins7's blog</title><link>https://icyveins7.github.io/posts/</link><description>Recent content in Posts on icyveins7's blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright><lastBuildDate>Wed, 02 Sep 2026 20:00:00 +0800</lastBuildDate><atom:link href="https://icyveins7.github.io/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Large pinned host allocations in CUDA</title><link>https://icyveins7.github.io/posts/2026/09/large-pinned-host-allocations-in-cuda/</link><pubDate>Wed, 02 Sep 2026 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2026/09/large-pinned-host-allocations-in-cuda/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Sometimes, you just can&amp;rsquo;t fit everything in VRAM.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In most CUDA projects, the advice is usually to transfer all inputs, outputs, and temporary scratch space to the device. Working completely inside VRAM is &lt;em&gt;fast&lt;/em&gt;, and avoids both the complexity and the throughput hit that comes with over-PCIe transfers being scattered throughout your hot path.&lt;/p&gt;
&lt;p&gt;All of this is correct, and I adhere to this myself as much as possible. Sometimes, though, you simply just can&amp;rsquo;t do this, and recently I was left with no choice and had to start migrating stuff back to host memory.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Sometimes, you just can&rsquo;t fit everything in VRAM.</p>
</blockquote>
<p>In most CUDA projects, the advice is usually to transfer all inputs, outputs, and temporary scratch space to the device. Working completely inside VRAM is <em>fast</em>, and avoids both the complexity and the throughput hit that comes with over-PCIe transfers being scattered throughout your hot path.</p>
<p>All of this is correct, and I adhere to this myself as much as possible. Sometimes, though, you simply just can&rsquo;t do this, and recently I was left with no choice and had to start migrating stuff back to host memory.</p>
<h1 id="pinned-memory-and-when-to-use-resort-to-it">Pinned memory and when to <del>use</del> resort to it</h1>
<h2 id="do-less-more-times">Do less, more times</h2>
<p>Let&rsquo;s start with what is <em>not</em> a good reason to leave everything in pinned memory. In many processing scenarios, you can get away with slicing or tiling your data in some way:</p>
<ul>
<li>Process in sections of rows or columns</li>
<li>Process in tiles</li>
</ul>
<p>If your processing is element-wise, then you can effectively cut the data using any method and then treat each sub-unit as its own problem. Invoke the kernel(s) per sub-unit, and hide the latency of the H2D/D2H transfers by having a separate stream(s). This is all standard CUDA at this point.</p>
<pre class="mermaid">gantt
    title Tilewise CUDA Processing
    dateFormat x
    tickInterval 1millisecond
    axisFormat %L
    section Tile 1
        H2D                :active, h2d_1, 0, 5
        Processing kernel  :crit, k_1, 5, 15
        D2H                :active, d2h_1, 15, 20
    section Tile 2
        H2D                :active, h2d_2, 10, 15
        Processing kernel  :crit, k_2, 15, 25
        D2H                :active, d2h_2, 25, 30
</pre>
<h2 id="until-the-smallest-unit-is-simply-too-big">Until the smallest unit is simply too big</h2>
<p>However, when data dependencies across your array/image are non-trivial, then you&rsquo;re out of luck here. Maybe your input element index is just completely unknown until runtime, so for all intents and purposes it&rsquo;s a random lookup. You now have no way to know which row/column/tile to copy up for that particular kernel invocation. So you must have the entire thing available in global memory, query-able at all positions.</p>
<p>But what if your data is huge? 1000 x 1000 images is probably not a big deal, but maybe you&rsquo;re doing precise processing using double precision on 10000x10000 images? Then suddenly this is 800MB of VRAM. 20000x20000 becomes 3.2GB. That&rsquo;s just 1 array. What if you have 10 of these?</p>
<p>My point is that it&rsquo;s not a completely remote possibility if you work on data of this size, especially if the VRAM also needs to be shared with other processes.</p>
<h1 id="random-but-uncommon-lookups">Random but uncommon lookups</h1>
<p>In my case, my estimates for my arrays came out to be around 40GB in total. Not viable to hold all of them in VRAM. Okay, so then the first thing I thought was to transfer the data for each step to the GPU, only at that step, and then transfer it back down when finished. Remember, the whole point was that I was memory constrained, so I couldn&rsquo;t have everything on the GPU at the same time.</p>
<p>But this turned out to be pretty bad in my case. Each step took a very short time to compute, and only operated on a few pixels at each step. These pixels were randomly distributed (so I couldn&rsquo;t carve out a tile reliably) but they were also few and far between (so the total time for computation was small). I spent more time copying the array up and down than the kernel.</p>
<p><em>Duh, you should be doing more computations (more kernels) before copying it back down.</em> Again, remember that I was <strong>memory constrained</strong>. I <strong>could not</strong> do this because the next step would need a different set of arrays, so I would need to clear the first set out from VRAM in order to operate on the next step. It looked something like this:</p>
<ol>
<li>Uses arrays A and B. H2D A and B, invoke <code>step1_kernel</code>, D2H B because next step still uses A.</li>
<li>Uses arrays A and C. H2D C, invoke <code>step2_kernel</code>, D2H A and C.</li>
<li>Uses arrays B and D. H2D B and D, invoke <code>step3_kernel</code>, D2H B and D.</li>
<li>&hellip;</li>
</ol>
<h1 id="mapped-pinned-memory-to-the-rescue">Mapped pinned memory to the rescue</h1>
<p>It turns out that kernels can now (and have been able to for quite some time) <em>execute directly on pinned host memory pointers</em>. This comes with the caveat that the pinned host memory is <a href="https://docs.nvidia.com/cuda/cuda-programming-guide/02-basics/understanding-memory.html#mapped-memory"><em>mapped</em></a>, but from my testing on several machines with different (albeit fairly new) CUDA versions, the typical <code>cudaHostAlloc</code> and <code>cudaMallocHost</code> calls will <em>automatically</em> do this. In fact, I could not even turn it off when I tried to.</p>
<p>How this works is the kernel will pull memory from the host over PCIe as and when it is required. If a thread reads a particular address, then the kernel will transfer that memory up to the GPU (with some standard cache-line shenanigans, different from the GPU&rsquo;s global memory read transaction sizes, but that&rsquo;s not the main point here).</p>
<p>This is <strong>great</strong> for my use-case, since I don&rsquo;t touch the majority of my array. I only want it to read/write the few indices that actually get processed by the kernel. These are scattered around the image, so there&rsquo;s no reasonable clean way for me to do this myself unless I invoke another kernel or some host-side function to <code>cudaMemcpy</code> only those addresses up. Unnecessary, and in my opinion, very messy.</p>
<p>Instead, I just write the kernel as per normal, wrap the pinned host pointer in a <code>cudaHostGetDevicePointer()</code> and let the hardware do its thing. It turned out to be surprisingly effective in my case.</p>
<h1 id="the-kernels-really-just-work">The kernels really <em>just work</em></h1>
<p>There&rsquo;s absolutely nothing different in the way you have to write the kernel (aside from doing weird things like trying to atomically modify a mapped pinned host memory pointer, but why would you even do that).</p>
<p>In fact, most of my kernels were written for device memory at the beginning; when I did my testing, I used small image dimensions so everything fit in VRAM. It was only later, after I realised I didn&rsquo;t have enough VRAM, that I moved the allocations to mapped pinned host memory, and simply wrapped the host pointers:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// Original
</span></span></span><span style="display:flex;"><span>mykernel<span style="color:#f92672">&lt;&lt;&lt;</span>grid, blk<span style="color:#f92672">&gt;&gt;&gt;</span>(d_a, d_b, d_c);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// After
</span></span></span><span style="display:flex;"><span>mykernel<span style="color:#f92672">&lt;&lt;&lt;</span>grid, blk<span style="color:#f92672">&gt;&gt;&gt;</span>(d_a, d_b, cudaHostGetDevicePointer(h_c));
</span></span></code></pre></div><p>That&rsquo;s the beauty of this, in my opinion. The kernels work seamlessly. I can mix-and-match device and host mapped memory without having to worry about it (other than performance).</p>
<p>All in all, another thing to add to the CUDA toolbox.</p>
]]></content></item><item><title>Occupancy-maxxing is just Starcraft</title><link>https://icyveins7.github.io/posts/2026/08/occupancy-maxxing-is-just-starcraft/</link><pubDate>Wed, 12 Aug 2026 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2026/08/occupancy-maxxing-is-just-starcraft/</guid><description>&lt;blockquote&gt;
&lt;p&gt;If you can manage minerals and gas, you can manage CUDA threads and registers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;CUDA is just resource management.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Okay, of course there&amp;rsquo;s a lot more to squeezing CUDA kernel performance than &lt;em&gt;just resource management&lt;/em&gt;, but occupancy is very often the simplest thing to strive for, once you know how. And usually, if you&amp;rsquo;ve hit 100% occupancy, then unless you&amp;rsquo;ve done something heinous in your code, further optimizations are unlikely to budge your performance by large factors (aside from an entire algorithmic shift, but we won&amp;rsquo;t discuss that here).&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>If you can manage minerals and gas, you can manage CUDA threads and registers.</p>
</blockquote>
<blockquote>
<p>CUDA is just resource management.</p>
</blockquote>
<p>Okay, of course there&rsquo;s a lot more to squeezing CUDA kernel performance than <em>just resource management</em>, but occupancy is very often the simplest thing to strive for, once you know how. And usually, if you&rsquo;ve hit 100% occupancy, then unless you&rsquo;ve done something heinous in your code, further optimizations are unlikely to budge your performance by large factors (aside from an entire algorithmic shift, but we won&rsquo;t discuss that here).</p>
<h1 id="starcraft-and-unit-compositions">Starcraft and unit compositions</h1>
<p>For those who are too uncultured to have played Starcraft before, the goal of the game is essentially to construct a base, gather resources, and then build an assortment of units to destroy your enemy.</p>
<blockquote>
<p>I&rsquo;m going to use Protoss units and buildings here, because <em>Protoss master race</em>. Also because <strong>pylon</strong> sounds better than <strong>bunker</strong>(boring) and <strong>overlord</strong>(ew). Facts, not biased at all.</p>
</blockquote>
<p>There&rsquo;s a few things to keep in mind during a game:</p>
<ul>
<li><strong>Supply</strong>: this is the maximum <em>army count</em> you can have. You increase this by <em>building more pylons</em>.</li>
<li><strong>Minerals</strong>: this is the base resource. It&rsquo;s generally the more common resource, and you have more of it than gas.</li>
<li><strong>Vespene gas</strong>: this is the secondary resource. It&rsquo;s harder to get, but is used to build more <em>advanced units</em>.</li>
</ul>
<p>Imagine you have some limited amount of the above 3 at some point in the game. Your goal is to maximize the use of your resources. Let&rsquo;s say you have</p>
<ul>
<li>16 supply</li>
<li>400 gas</li>
<li>1500 minerals</li>
</ul>
<p>We&rsquo;ll choose from a small subset of possible units to make it simple:</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Unit Name</th>
					<th style="text-align: left">Mineral Cost</th>
					<th style="text-align: left">Gas Cost</th>
					<th style="text-align: left">Supply Cost</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><strong>Zealot</strong></td>
					<td style="text-align: left">100</td>
					<td style="text-align: left">0</td>
					<td style="text-align: left">2</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Stalker</strong></td>
					<td style="text-align: left">125</td>
					<td style="text-align: left">50</td>
					<td style="text-align: left">2</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Immortal</strong></td>
					<td style="text-align: left">275</td>
					<td style="text-align: left">100</td>
					<td style="text-align: left">4</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Colossus</strong></td>
					<td style="text-align: left">300</td>
					<td style="text-align: left">200</td>
					<td style="text-align: left">6</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Carrier</strong></td>
					<td style="text-align: left">350</td>
					<td style="text-align: left">250</td>
					<td style="text-align: left">6</td>
			</tr>
	</tbody>
</table>
<p>You could say: I just want carriers. So you derp your way over to your Stargate and just build one&hellip; and then you run out of gas. Obviously, this is a terrible army composition. You still have a lot of supply - 6/16 - along with a ton of minerals and gas left over.</p>
<p>Alright, so then you just want to make sure you at least use all your supply. Simple way is to mineral dump into zealots, so you max out with 5 more zealots. Total consumption is 350 + 5 * 100 = 850 minerals, and 250 gas. Obviously, this is still leaving a lot on the table.</p>
<p>Consider getting 2 stalkers, a colossus, an immortal and a zealot. You now use 2 * 125 + 300 + 275 + 100 = 925 minerals, and all 400 gas. This is a far better use of all your resources.</p>
<h1 id="what-theoretical-occupancy-is">What (theoretical) occupancy is</h1>
<p>The idea in GPU kernel occupancy is basically the same. You have the following to play with:</p>
<ul>
<li>Number of threads per SM</li>
<li>Number of blocks per SM</li>
<li>Number of registers per SM</li>
<li>Shared memory per SM</li>
</ul>
<p><a href="https://docs.nvidia.com/cuda/cuda-programming-guide/05-appendices/compute-capabilities.html#features-and-technical-specifications">https://docs.nvidia.com/cuda/cuda-programming-guide/05-appendices/compute-capabilities.html#features-and-technical-specifications</a></p>
<p>Technically there&rsquo;s a list of things, but honestly you can just focus on these few, because they&rsquo;re the ones that come up the most often.</p>
<p>The easiest one to think about is number of threads per SM. Let&rsquo;s consider a typical CC 86 card with 1536 threads per SM. The idea is that you essentially slot your blocks onto the SM one at a time, until the SM has insufficient resources to slot in the next block.</p>
<p>A simple example is the common novice GPU programmer gotcha of launching very large blocks; usually the maximum block size is 1024 threads. With 1536 threads per SM, the SM cannot fit in the second block after it already accepts one. SMs do not accept fractions of blocks. This essentially means that you <em>might</em> have 1/3 of the compute resources sitting unused; <code>nsys</code> will tell you your theoretical occupancy is 66%. If there is no strict reason for needing 1024 threads per block, then you would gain this compute back simply by lowering the block size.</p>
<p>Occupancy is thus a statement of how many threads you are using per SM.</p>
<p>The converse is also true. There is a maximum number of blocks that can be in flight per SM. That number usually corresponds to something less than 128 threads per block. This is one reason why many CUDA tutorials recommend starting with 128 thread blocks..</p>
<p>So for our CC 86 card with 1536 threads per SM that is 16 blocks. If you were to launch your kernel with 32-thread blocks, the SM <em>will still only hold up to 16 blocks</em>. Thus that means the total number of threads being handled per SM falls to a measly 16*32=512 threads, and your theoretical occupancy will rest at 33%.</p>
<h1 id="adjusting-your-cuda-army-composition">Adjusting your CUDA army composition</h1>
<p>In all the scenarios above, what we are really trying to do is to simply <em>ensure the SM is fully saturated with threads</em>. This is very easy to do with simple kernels; just change your block size to a number like 128 or 256, in most cases.</p>
<p>Two things to address here:</p>
<ol>
<li><strong>Why does this even matter?</strong> Performance. Internally, although the SM is likely not processing all its threads concurrently, using more threads here allows it to hide latency between memory, compute and whatever else. I agree that this isn&rsquo;t a convincing argument, so the best way to know is to try it yourself whenever possible.</li>
<li><strong>What if changing the block size isn&rsquo;t doing anything?</strong> Then you are likely dealing with the next 2: registers and shared memory. We discuss this now.</li>
</ol>
<h1 id="the-argument-against-fat-kernels">The argument against fat kernels</h1>
<p>You have probably heard of kernel fusion. The primary reason for this is to reduce global memory pressure by doing more work within one kernel; that way, you don&rsquo;t have to read and write back to global memory at what used to be the beginning/end of 2 separate kernels.</p>
<p>Now, I would go so far as to claim that this is essentially true all of the time. You should default to this mindset when looking for optimization avenues; however, we should always remain cognizant of what we give up by doing this. This is why I tend to write small kernels and then merge them, rather than write long ones and split them later. It&rsquo;s always far more maddening to try to disentangle a fat kernel.</p>
<p>So why fatshame kernels? Well, in general, the longer your kernel is, the more registers per thread it will use. The compiler does an excellent job of reducing and reusing registers, but it isn&rsquo;t perfect. It is unlikely that any non-trivial kernel will stay below 20 registers per thread.</p>
<p>The story here is the same as that of the thread count. If we exceed the total number of registers per SM, the SM will cut blocks until it fits. Consider the following configuration:</p>
<ul>
<li>Threads per block: 128</li>
<li>Registers per thread: 50</li>
<li>Maximum registers per SM: 65536</li>
<li>Maximum threads per SM: 1536</li>
<li>Maximum blocks per SM: 16</li>
</ul>
<p>As we saw earlier, under normal circumstances, you would easily fit 12 blocks ($12 \times 128 = 1536$) worth of threads on a single SM. But now the SM needs to ensure all the registers your blocks need are available. This comes out to $128 \times 50 \times 12 = 76800$, which exceeds the 65536 cap. So what happens? The SM simply refuses to house blocks that would exceed its registers, so it really only houses $65536/(128\times50) = 10.24$ blocks. No such thing as a fractional block, so we get 10 blocks in flight per SM. That&rsquo;s a theoretical occupancy of $\frac{10}{16} = 60\%$.</p>
<blockquote>
<p>In fact, the problem is actually worse than this. CUDA actually allocates registers at a warp granularity. In most cases, this is 256 registers per warp (or 8 registers per thread). What this means is you get <em>breakpoints</em>; 40 registers per thread and 41 registers per thread is a gigantic jump, since 41 registers per thread is effectively &rsquo;the same as 48 registers per thread'.</p>
</blockquote>
<p>The same goes for shared memory - static and dynamic alike. You may have heard that the maximum shared memory per thread block is 64KB (at least by default, yes I know you can increase it nowadays, but the point still stands).
You might then think it&rsquo;s ok to use as much of it as possible for each thread block; after all, we get to exploit the fast cache-like memory bandwidth right? Not quite. You have essentially the same maximum shared memory per block as maximum shared memory per SM. So if you use all 64KB of shared memory for 1 block, then the SM will have capacity to hold <em>only that one block</em>. If your block size is 128 threads, then your theoretical occupancy is tanking all the way down to $\frac{128}{1536}$ (or a similar calculation for your compute capability). That is an 8% to be ashamed of.</p>
<h1 id="occupancy-isnt-everything">Occupancy isn&rsquo;t everything</h1>
<p>After reading all this, it might sound like occupancy is the only thing you should care about. But in reality, this is usually not the case. Kernels are complicated, and just because you increase occupancy does <em>not</em> guarantee better performance.</p>
<p>The tradeoffs are the same:</p>
<ul>
<li>If you cut registers you usually end up doing more computations</li>
<li>If you cut shared memory you either store/load more from global memory, or you use more registers</li>
</ul>
<p>And these are just the technical reasons. You also often lose code clarity if you force your kernel to do awkward computations just to reduce some shared memory workspace or registers. Sadly, there is no magic bullet here; at the end of the day the correct thing to do is to profile your choices.</p>
<blockquote>
<p>Please profile your damn choices. With AI nowadays there&rsquo;s no reason not to try a bajillion different kernel configurations and just pick the best one.</p>
</blockquote>
]]></content></item><item><title>Your left-right boundary search is actually a CUDA-friendly single scan</title><link>https://icyveins7.github.io/posts/2026/06/your-left-right-boundary-search-is-actually-a-cuda-friendly-single-scan/</link><pubDate>Fri, 26 Jun 2026 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2026/06/your-left-right-boundary-search-is-actually-a-cuda-friendly-single-scan/</guid><description>&lt;blockquote&gt;
&lt;p&gt;At least, most of the time it is. As long as it fits the 1-2-1 pattern..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Many segments can be boiled down to what I call a 1-2-1 pattern. For an array of input elements (I&amp;rsquo;ll just call them pixels, it&amp;rsquo;s shorter for me to type), a naive segment detection is to loop forwards until you hit an &lt;em&gt;activation&lt;/em&gt; pixel. This can internally be any condition you&amp;rsquo;d like; that is, any &lt;code&gt;if (...)&lt;/code&gt; is possible here as long as you operate only on the pixel you&amp;rsquo;re currently at. For simplicity, I&amp;rsquo;ll assume this is the &lt;code&gt;2&lt;/code&gt; pixel:&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>At least, most of the time it is. As long as it fits the 1-2-1 pattern..</p>
</blockquote>
<p>Many segments can be boiled down to what I call a 1-2-1 pattern. For an array of input elements (I&rsquo;ll just call them pixels, it&rsquo;s shorter for me to type), a naive segment detection is to loop forwards until you hit an <em>activation</em> pixel. This can internally be any condition you&rsquo;d like; that is, any <code>if (...)</code> is possible here as long as you operate only on the pixel you&rsquo;re currently at. For simplicity, I&rsquo;ll assume this is the <code>2</code> pixel:</p>
<pre tabindex="0"><code>. . . . . 2
---------&gt; activated
</code></pre><p>The activation pixel is what defines us to be within a segment. This can happen several times within a segment, but it doesn&rsquo;t matter (activating an already active segment does nothing):</p>
<pre tabindex="0"><code>. . . . . 2 2
-----------&gt; still same segment
</code></pre><p>We now seek the boundaries of the segment, $[a,b]$ i.e. the (inclusive) pixels on the left and right where the segment is deemed to start/end. A naive next step is then to iterate from the first activation pixel leftwards and rightwards to look for the boundary conditions. Again, this can be defined by any <code>if(...)</code> statement, but here we will simplify it to be a <code>1</code> pixel:</p>
<pre tabindex="0"><code>. . 1 . . 2 2 . . 1 .
     &lt;---- ------&gt;
</code></pre><p>In this simplified scheme, anything other than 1 and 2 for the pixels are irrelevant (hence the 1-2-1 naming).</p>
<p>Note that under the above definition, a simple 1-1 boundary does not denote a segment, as it lacks an activation <code>2</code> pixel:</p>
<pre tabindex="0"><code>. . 1 . . . . 1
This is not a segment
</code></pre><h1 id="scan-when-there-are-many">Scan when there are many</h1>
<p>Now the astute leetcoder will probably realise that this is terribly inefficient. If you have multiple segments, you will end up doing a lot of backtracking. The fundamental idea of using a scan here is to <em>remember the last boundary</em>. In this case, we also need to remember if we are in an <em>active</em> segment.</p>
<blockquote>
<p>For the uninformed, a <em>prefix scan</em> is essentially a rolling operation with a binary functor. That is, you define an operation taking in two values - before, after - and return a new value. This new value is used in the next operation (instead of the original input). The most common variant is arguably a prefix sum, otherwise known as a cumulative sum or <code>cumsum</code> in many languages.</p>
</blockquote>
<p>This easily boils down to a few simple operations:</p>
<ul>
<li><strong>Reset</strong>: whenever we see a <code>1</code>, it means the current segment (active or not) has ended, and this is also the start of a new segment.</li>
<li><strong>Extend</strong>: in any other case, we simply copy the index we currently have. Importantly, we also need to keep track of the last <code>2</code> we saw, so we know at the end of a segment whether it was activated or not.</li>
</ul>
<p>In practice, we can couple the incrementing index with the original pixels, and perform the scan in the following way:</p>
<pre tabindex="0"><code> In 0 1 0 1 2 0 1 0
Idx 0 1 2 3 4 5 6 7

Sdx - 1 - 3 - - 6 -
</code></pre><p>Here, <code>Sdx</code> is simply a transform of the original incrementing index: set to -1 if it&rsquo;s not a boundary <code>1</code>,  otherwise keep the original index. Then, our scan operation looks something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>FlagAndIndex <span style="color:#a6e22e">operator</span>()(
</span></span><span style="display:flex;"><span>	FlagAndIndex a,
</span></span><span style="display:flex;"><span>	FlagAndIndex b
</span></span><span style="display:flex;"><span>){
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> (b.flag <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span>) <span style="color:#75715e">// reset
</span></span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> b; <span style="color:#75715e">// we &#39;forget&#39; everything before
</span></span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">else</span> <span style="color:#75715e">// extend
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// copy (&#39;drag&#39;) the earlier index,
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// take the larger flag (so we keep &#39;activations&#39;)
</span></span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> {max(a.flag, b.flag), a.idx};
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The scan then results in this:</p>
<pre tabindex="0"><code> In 0 1 0 1 2 0 1 0
Sdx - 1 - 3 - - 6 -

Out 0 1 1 1 2 2 1 1
Odx - 1 1 3 3 3 6 6
</code></pre><p>We can walk through this simple example:</p>
<ol>
<li>Index 0. We are performing an <em>inclusive</em> scan so the first input is just copied. No-op.</li>
<li>We hit the first <code>1</code> at index 1. This hits our <strong>Reset</strong> condition so output becomes the current index i.e., 1, and we use the input flag <code>1</code>.</li>
<li>Index 2: <strong>Extend</strong>. Simply copy the same value from before; <code>max(flagA, flagB)</code> is still just <code>1</code>.</li>
<li>Index 3 has hit our <strong>Reset</strong> again. We hence update to the current index, 3, and flag, <code>1</code>.</li>
<li>Index 4: <strong>Extend</strong>. This hits the first activation. The index is dragged up (still 3) but now the flag after the <code>max()</code> operation is <code>2</code>.</li>
<li>Index 5 is again an <strong>Extend</strong>. Everything is copied, flag is still <code>2</code>.</li>
<li>Index 6 hits a <strong>Reset</strong>. The output index is updated to 6, and our flag is reset to <code>1</code>.</li>
</ol>
<p>Hopefully this toy example makes it clear how a simple custom functor with a few operations maintains the one-pass scan.</p>
<h1 id="okay-but-wheres-the-bounds">Okay, but where&rsquo;s the bounds?</h1>
<p>Readouts should now be pretty obvious. Note that you can actually do this <em>during</em> the scan, but for now I will just explain it with another pass.</p>
<p>We simply need to read the inputs for <code>1</code>s, and then check the activation flag one step on the left. If it&rsquo;s true, the segment on the left is valid and you can then read the scan&rsquo;s output index, also one step on the left, to get the segment&rsquo;s starting index. Otherwise, it&rsquo;s invalid and you can skip it.</p>
<p>For the above example, we have three <code>1</code>s:</p>
<ol>
<li>Index 1. Index 0&rsquo;s flag is <code>0</code>, so skip.</li>
<li>Index 3. Index 2&rsquo;s flag is <code>1</code>, so skip again.</li>
<li>Index 6. Index 5&rsquo;s flag is <code>2</code> (active), so read the scan output index: 3. Segment bounds are $[3,6]$.</li>
</ol>
<h1 id="cuda-really-favours-this">CUDA <em>really</em> favours this</h1>
<p>If you&rsquo;ve read any of my other posts, you&rsquo;ll know I really like CUB. CUB&rsquo;s scans - like <code>DeviceScan::InclusiveScan</code> - are all <em>really</em> efficient, primarily because they do the scan via a tree. The result is that this is usually faster than anything I (you) will ever write, so we should just use it.</p>
<p>However, in my opinion, there&rsquo;s another more compelling reason to use a scan in this scenario: <em>simplicity</em>. Consider the original naive method; find a <code>2</code>, find surrounding <code>1</code>s, rinse and repeat.</p>
<p>A blind conversion of this into CUDA would probably look something like:</p>
<ol>
<li>Launch kernel to cover all pixels; simple grid-stride.</li>
<li>At every pixel, check if it&rsquo;s a <code>2</code>.</li>
<li>If <code>2</code>, search outwards for the <code>1</code>s.</li>
</ol>
<p>To do this search in step 3 from the kernel, you have a few options, all of which are terrible for occupancy and/or simplicity:</p>
<ul>
<li>Use the thread that found the <code>2</code>. If you think this is acceptable Jensen Huang will personally strangle you with his leather jacket.</li>
<li>Use the warp/block. Still unacceptable. Also complicates things because you will have to somehow gather the candidates and then iterate on them cooperatively.</li>
<li>Use a new kernel. Or use CDP from the thread that found the <code>2</code> (I will silently judge you for this).</li>
</ul>
<p>Maybe I&rsquo;m being subjective here, but all of these pale in simplicity and efficiency to the scan provided by CUB; do it once, <em>and</em> use all of the GPU&rsquo;s resources at all (kinda) times. We trade time spent implementing for time spent thinking, and get performance in the process.</p>
<h1 id="one-extra-requirement">One extra requirement</h1>
<p>Not all is fine and dandy with CUDA&rsquo;s scans though. They impose one further constraint: <strong>associativity</strong>.</p>
<p>For those who have forgotten your math, this implies the following:</p>
$$
f(f(a,b), c) = f(a, f(b,c))
$$<p>This is elegant to look at, but easy to stumble over. In the 1-2-1 scan I just described, it is possible to write a binary functor that <em>does not</em> obey this. By that I actually mean that the functor written above is a perfect example of one that violates associativity.</p>
<p>To see why, consider the following example, with the transform applied before the scan:</p>
<pre tabindex="0"><code> In 0 1 2
Sdx - 1 -
</code></pre><p>We can define these 3 scan-pairs - (flag, index) - mathematically as</p>
$$
a = (0, -1)
$$$$
b = (1, 1)
$$$$
c = (2, -1)
$$<p>We can test exactly what the previous functor did on these 3 points pair-wise</p>
$$
f(a, b) = (1, 1)
$$$$
f(b,c) = (2,1)
$$<p>and then test the associativity</p>
$$
f(f(a,b), c) = f((1,1), (2,-1)) = (2,1)
$$$$
f(a,f(b,c)) = f((0,-1),(2,1)) = (2,-1)
$$<p>Thus, in this case, the two are not equal, proving that the binary functor as defined above is actually <em>not associative</em>. This means that it is not guaranteed to produce the correct (intended) results when using <code>cub::DeviceScan</code>.</p>
<p>But what actually happened here? The fact is that using the flag to <em>decide</em> between the <code>if/else</code> paths for the functor in this case breaks associativity, because it gets mutated in one of the paths.</p>
<p>In the <strong>extend</strong> path, we perform the <code>max</code> operation and keep the larger value. In this case, that changes a <code>1</code> flag to a <code>2</code>, and that switches the path from the <code>if</code>(reset) to the <code>else</code>(extend).</p>
<p>The solution is hence to ensure that this does not happen, by using some other method to decide between the reset/extend paths:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>FlagAndIndex <span style="color:#a6e22e">operator</span>()(
</span></span><span style="display:flex;"><span>	FlagAndIndex a,
</span></span><span style="display:flex;"><span>	FlagAndIndex b
</span></span><span style="display:flex;"><span>){
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// our transform changed all non-reset indices to 
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// negative values, so we can use the index as
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// the decider instead
</span></span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> (b.idx <span style="color:#f92672">&gt;=</span> <span style="color:#ae81ff">0</span>) <span style="color:#75715e">// reset
</span></span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> b; <span style="color:#75715e">// we &#39;forget&#39; everything before
</span></span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">else</span> <span style="color:#75715e">// extend
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// copy (&#39;drag&#39;) the earlier index,
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// take the larger flag (so we keep &#39;activations&#39;)
</span></span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> {max(a.flag, b.flag), a.idx};
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>With this, associativity is restored! <del>And you won&rsquo;t get a nasty surprise when some scans suddenly don&rsquo;t work the way you expected.</del></p>
]]></content></item><item><title>Avoid being baited by your printf statements in CUDA kernels</title><link>https://icyveins7.github.io/posts/2026/03/avoid-being-baited-by-your-printf-statements-in-cuda-kernels/</link><pubDate>Mon, 23 Mar 2026 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2026/03/avoid-being-baited-by-your-printf-statements-in-cuda-kernels/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Behaviour of printf on device is not the same as on host!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you&amp;rsquo;re a &lt;code&gt;printf&lt;/code&gt; aficionado like me, then you use &lt;code&gt;printf&lt;/code&gt; for debugging. A lot. In fact I previously wrote a small logger that uses &lt;code&gt;printf&lt;/code&gt; called &lt;a href="https://github.com/icyveins7/spfLogger"&gt;spfLogger&lt;/a&gt;. I do enjoy the flexibility of tuning every single width/precision with a few characters, and it&amp;rsquo;s something I haven&amp;rsquo;t yet seen C++ be able to emulate with as little irritation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I haven&amp;rsquo;t gotten around to writing code using newer &lt;code&gt;std::format&lt;/code&gt; or &lt;code&gt;println&lt;/code&gt; yet, so the jury&amp;rsquo;s out on that.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Behaviour of printf on device is not the same as on host!</p>
</blockquote>
<p>If you&rsquo;re a <code>printf</code> aficionado like me, then you use <code>printf</code> for debugging. A lot. In fact I previously wrote a small logger that uses <code>printf</code> called <a href="https://github.com/icyveins7/spfLogger">spfLogger</a>. I do enjoy the flexibility of tuning every single width/precision with a few characters, and it&rsquo;s something I haven&rsquo;t yet seen C++ be able to emulate with as little irritation.</p>
<blockquote>
<p>I haven&rsquo;t gotten around to writing code using newer <code>std::format</code> or <code>println</code> yet, so the jury&rsquo;s out on that.</p>
</blockquote>
<p>But that&rsquo;s not the topic today. I just want to highlight something that can happen when you use <code>printf</code> in CUDA kernels, which is what I do very often.</p>
<h1 id="a-simple-example">A simple example</h1>
<p>Consider the following kernel and its equivalent host function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> T, <span style="color:#66d9ef">typename</span> U<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>__global__ <span style="color:#66d9ef">void</span> badprintfkernel(<span style="color:#66d9ef">const</span> T <span style="color:#f92672">*</span>a, <span style="color:#66d9ef">const</span> U <span style="color:#f92672">*</span>b) {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// i do 4 prints here, you&#39;ll see why below
</span></span></span><span style="display:flex;"><span>  printf(<span style="color:#e6db74">&#34;device: a = %d, b = %d, a = %d, a = %d</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>, <span style="color:#f92672">*</span>a, <span style="color:#f92672">*</span>b, <span style="color:#f92672">*</span>a, <span style="color:#f92672">*</span>a);
</span></span><span style="display:flex;"><span>  printf(<span style="color:#e6db74">&#34;device: a = %d, b (with ld) = %ld, a = %d</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>, <span style="color:#f92672">*</span>a, <span style="color:#f92672">*</span>b, <span style="color:#f92672">*</span>a);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> T, <span style="color:#66d9ef">typename</span> U<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">void</span> printfhost(<span style="color:#66d9ef">const</span> T <span style="color:#f92672">*</span>a, <span style="color:#66d9ef">const</span> U <span style="color:#f92672">*</span>b) {
</span></span><span style="display:flex;"><span>  printf(<span style="color:#e6db74">&#34;host: a = %d, b = %d, a = %d</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>, <span style="color:#f92672">*</span>a, <span style="color:#f92672">*</span>b, <span style="color:#f92672">*</span>a);
</span></span><span style="display:flex;"><span>  printf(<span style="color:#e6db74">&#34;host: a = %d, b (with ld) = %ld, a = %d</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>, <span style="color:#f92672">*</span>a, <span style="color:#f92672">*</span>b, <span style="color:#f92672">*</span>a);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>We invoke the kernel with 1 thread just to see the prints, and also the host function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>thrust<span style="color:#f92672">::</span>device_vector<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">int32_t</span><span style="color:#f92672">&gt;</span> d_32(<span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span>thrust<span style="color:#f92672">::</span>fill(d_32.begin(), d_32.end(), <span style="color:#ae81ff">0xFF332211</span>);
</span></span><span style="display:flex;"><span>thrust<span style="color:#f92672">::</span>device_vector<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">int64_t</span><span style="color:#f92672">&gt;</span> d_64(<span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span>thrust<span style="color:#f92672">::</span>fill(d_64.begin(), d_64.end(), <span style="color:#ae81ff">0xFF332211112233FF</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>thrust<span style="color:#f92672">::</span>host_vector<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">int32_t</span><span style="color:#f92672">&gt;</span> h_32 <span style="color:#f92672">=</span> d_32;
</span></span><span style="display:flex;"><span>thrust<span style="color:#f92672">::</span>host_vector<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">int64_t</span><span style="color:#f92672">&gt;</span> h_64 <span style="color:#f92672">=</span> d_64;
</span></span><span style="display:flex;"><span>badprintfkernel<span style="color:#f92672">&lt;&lt;&lt;</span><span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span><span style="color:#f92672">&gt;&gt;&gt;</span>(d_32.data().get(), d_64.data().get());
</span></span><span style="display:flex;"><span>printfhost(h_32.data(), h_64.data());
</span></span></code></pre></div><p>What do you expect to see?</p>
<h1 id="not-just-undefined-behaviour-but-unexpected-behaviour">Not just undefined behaviour, but unexpected behaviour</h1>
<p>This is what gets printed on the host:</p>
<pre tabindex="0"><code>host: a = -13426159, b = 287454207, a = -13426159
host: a = -13426159, b (with ld) = -57664913528441857, a = -13426159
</code></pre><p>Now this is what gets printed on the device:</p>
<pre tabindex="0"><code>device: a = -13426159, b = 0, a = 287454207, a = -13426159
device: a = -13426159, b (with ld) = -57664913528441857, a = -13426159
</code></pre><p>For reference, <code>0xFF332211</code> is -13426159 whereas <code>0xFF332211112233FF</code> is -57664913528441857.</p>
<p>So 2 things have happened on the device:</p>
<ol>
<li>For <code>int64_t</code>, the &lsquo;cast&rsquo; seems to have just completely bugged out, as it simply prints <code>0</code>.</li>
<li>It appears to have corrupted the subsequent argument&rsquo;s formatting. We see what we would expect to see (287454207) for <code>b</code> in the 3rd argument which prints <code>a</code>. Only on the 4th argument which prints <code>a</code> again is the printed value correct.</li>
</ol>
<p>Now, it is understood that <code>printf</code>&rsquo;s behaviour is technically undefined when the format specifier is invalid i.e. using <code>%d</code> for a 32-bit integer. However, in the CPU code, you can clearly see that what it prints is the least significant 32-bits of the number; 287454207 is <code>0x112233FF</code>, the lower 32-bits of the 64-bit number used as the input. This is in fact, what I have come to expect, which is why it can catch me off-guard when I don&rsquo;t pay attention to my <code>printf</code>s in kernels.</p>
<p>Usually, especially during development and basic unit tests, I would instantiate small numbers to test my kernels, but may possibly swap the types around to ensure things work (or to time the kernel with different types).</p>
<p>This quirk of <code>printf</code> in the kernel means that if I print some number the wrong way (format specifier), even without any risk of overflow, the number will be 0 (or gibberish - at least I think I&rsquo;ve encountered gibberish before).</p>
<blockquote>
<p>For example, if the result of some number is 5, since 5 is smaller than 32-bits, it would print the same way in CPU code whether I used <code>%d</code> or <code>%ld</code> or <code>%lld</code>, since the bits 32-63 don&rsquo;t matter anyway.</p>
</blockquote>
<p>Even worse, it will mess up other numbers in the same print statement, confusing me further, since it&rsquo;ll start to make me second guess the calculations involving the other numbers I am printing.</p>
<p>All in all, this is just a short post to warn CUDA <code>printf</code> users that you should be very careful when interpreting the results of your print statements, especially in templated scenarios where it is (or may be) difficult to change the format specifier based on the templated types. Refer to the <a href="https://docs.nvidia.com/cuda/cuda-programming-guide/05-appendices/cpp-language-support.html#printf">official docs</a> for other limitations, like the maximum of 32 printed arguments (which I have discovered for myself before).</p>
]]></content></item><item><title>Iterative algorithms with CUDA</title><link>https://icyveins7.github.io/posts/2025/11/iterative-algorithms-with-cuda/</link><pubDate>Sat, 29 Nov 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/11/iterative-algorithms-with-cuda/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Because not everything can be made un-iterative..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There&amp;rsquo;s a pretty large class of algorithms that have been developed for CPUs that involve, optimally, an iterative solver.&lt;/p&gt;
&lt;p&gt;Usually, this is defined by some notion of &lt;em&gt;convergence&lt;/em&gt;; some variable is updated, and then at the end of each iteration it is checked to determine whether further iterations are required.&lt;/p&gt;
&lt;p&gt;This is all fine and dandy in CPU-land, but in GPUs this almost always makes the flow awkward.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Because not everything can be made un-iterative..</p>
</blockquote>
<p>There&rsquo;s a pretty large class of algorithms that have been developed for CPUs that involve, optimally, an iterative solver.</p>
<p>Usually, this is defined by some notion of <em>convergence</em>; some variable is updated, and then at the end of each iteration it is checked to determine whether further iterations are required.</p>
<p>This is all fine and dandy in CPU-land, but in GPUs this almost always makes the flow awkward.</p>
<h1 id="the-copy-back">The copy-back</h1>
<p>The easiest way to do this is usually a <code>memcpy</code> back to the host. We can make this as efficient as possible by of course making the memory pinned (it&rsquo;s usually just a single variable after all).</p>
<p>So the flow would be something like</p>
<ol>
<li>Launch iteration kernel.</li>
<li>Pull <code>check</code> back to host pinned memory.</li>
<li>Decide if iterations should continue.</li>
<li>Return to 1, or break.</li>
</ol>
<p>If your iteration kernel takes fairly long, the pull back and CPU-side decision making probably be insignificant in comparison, so this method is actually decent.</p>
<h1 id="the-cooperative-grid">The cooperative grid</h1>
<p>However, there is another way to do this; if anything, the aspiring CUDA practitioner should know of all the ways, just for the sake of it.</p>
<p>This is via in-kernel <a href="https://docs.nvidia.com/cuda/cuda-c-programming-guide/#grid-synchronization">grid synchronization</a>. In essence, this is just another flavour of cooperative groups, but grid-wide instead of the more commonly known block-wide groups.</p>
<p>The idea is pretty simple, as shown in the link; slap on a <code>grid.sync()</code> at the end of a single iteration, and it would be <em>just like the kernel had returned</em>.</p>
<p>So the order of operations inside a cooperative kernel would now look like</p>
<ol>
<li>Declare the cooperative grid.</li>
<li>Place all your previous iteration kernel&rsquo;s code into a <code>while</code> or <code>for</code> loop, depending on whether you want to limit the maximum number of iterations (always a good idea).</li>
<li>At the end of the <em>iteration</em> <code>while</code>/<code>for</code> loop, issue a <code>grid.sync()</code>.</li>
<li>Now the line after this is guaranteed to have every thread in the grid arrive at the same time; hence all threads of the kernel can read the <code>check</code> variable together, without any race conditions.</li>
<li>Just like before, each thread will decide if the iterations should continue (and they will all make the same decision, by definition).</li>
<li>Break and exit, or go again.</li>
</ol>
<p>Now there is a major caveat to this, as also shown in the link: you cannot allocate a grid larger than the device (all SMs) can support at any given point. Ideally, this shouldn&rsquo;t matter <em>too much</em>, since if every thread in every SM is doing work then you aren&rsquo;t really going to get extra performance out of using a larger grid.</p>
<p>But ideal isn&rsquo;t the world we live in, and it very likely isn&rsquo;t the world your code lives in either. In reality, some threads may be doing nothing due to conditionals, and/or having more blocks waiting could hide some latency. This would imply that using more blocks (like allocating enough to cover all your data) can also be beneficial.</p>
<p><strong>As always, profile your own kernel</strong>; for my recent use-case, I found that it could be slower in some scenarios and similar in others (but never faster, at least not tangibly).</p>
]]></content></item><item><title>Build your own tools, gcc edition</title><link>https://icyveins7.github.io/posts/2025/10/build-your-own-tools-gcc-edition/</link><pubDate>Mon, 20 Oct 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/10/build-your-own-tools-gcc-edition/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Not complicated, but always good to know the process.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this series (?) I&amp;rsquo;ll cover something I always wanted to write down: compiling the toolchains/compilers I use from source. This means, specifically, no using of package managers i.e. not allowed to &lt;code&gt;sudo apt install&lt;/code&gt; dependencies. Of course, this has its limits (&lt;em&gt;you need a compiler to compile gcc, and some OSes don&amp;rsquo;t come pre-installed with one&lt;/em&gt;). However, the premise here is to at least be familiar with the minimal required dependencies, and where to get them (at the time of writing), and then what to do to build everything.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Not complicated, but always good to know the process.</p>
</blockquote>
<p>In this series (?) I&rsquo;ll cover something I always wanted to write down: compiling the toolchains/compilers I use from source. This means, specifically, no using of package managers i.e. not allowed to <code>sudo apt install</code> dependencies. Of course, this has its limits (<em>you need a compiler to compile gcc, and some OSes don&rsquo;t come pre-installed with one</em>). However, the premise here is to at least be familiar with the minimal required dependencies, and where to get them (at the time of writing), and then what to do to build everything.</p>
<p>While this seems like a pointless exercise, doing this at least once comes in handy when you have to</p>
<ul>
<li>install something without sudo rights (because let me compile things in my user space if I want to damn it)</li>
<li>install something on an offline machine</li>
<li>install a version that doesn&rsquo;t (easily) exist on your system&rsquo;s package manager (because of reasons like an outdated OS, which happens way more often than you think on workplace servers)</li>
<li>or any other equivalent reason</li>
</ul>
<p>For the above reasons, none of the steps described in this post or later posts will require administrator privileges; everything will be installed into your <code>home</code> directory (and with that you will have to set things up yourself after).</p>
<p>We begin, as we should, with the granddaddy of all tools: <code>gcc</code>.</p>
<h1 id="installing-gcc-115-on-ubuntu-2204">Installing gcc-11.5 on Ubuntu 22.04</h1>
<p>The default repositories for Ubuntu 22.04 do not contain gcc-11.5, so we begin by reading its <a href="https://gcc.gnu.org/install/">documentation</a>.</p>
<p>Here we are presented with the essential dependencies:</p>
<ul>
<li><a href="%5Bhttps://gmplib.org%5D(https://gmplib.org/)">GMP</a></li>
<li>[MPFR](<a href="https://www.mpfr.org/">https://www.mpfr.org</a></li>
<li>[MPC](<a href="https://www.multiprecision.org/">https://www.multiprecision.org</a></li>
</ul>
<p>After downloading all of them, we must install them in the above order. This is because they are recursively dependent i.e MPC depends on MPFR which depends on GMP.</p>
<h1 id="a-primer-on-autoconf-style-builds">A primer on autoconf-style builds</h1>
<blockquote>
<p>Feel free to skip this if you&rsquo;re already familiar with it.</p>
</blockquote>
<p>Every tool that is linux-based will almost certainly include an <code>autoconf</code> script. This comes in the form of a <code>configure</code>script.</p>
<p>For most scenarios, running it without any extra options would suffice. Hence, other than some variations with extra steps like for testing, almost everything will build via</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./configure
</span></span><span style="display:flex;"><span>make
</span></span><span style="display:flex;"><span>sudo make install
</span></span></code></pre></div><p>What each line does, very briefly, is</p>
<ol>
<li>Configures the build and generates (optimized) makefiles. This usually exists to somewhat tailor the compilation to your system and/or detect missing dependencies.</li>
<li>Compiles the thing.</li>
<li>Installs the thing; <em>read: this means it copies the compiled executables/libraries to the designated path, which by default is a system path like <code>/usr/local</code>, hence the requirement that you do it with <code>sudo</code>. It may also help you with setting up reasonable symlinks</em>.</li>
</ol>
<h1 id="a-bit-of-guidance">A bit of guidance</h1>
<p>While the instructions on the <code>gcc</code> site are pretty comprehensive, I&rsquo;ll tailor a few steps here to make sure we can build and use <code>gcc</code> without <code>sudo</code>.</p>
<ol>
<li>Install GMP to a specific subdirectory in home. Example:</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export GMPDIR<span style="color:#f92672">=</span>/home/username/gmp
</span></span><span style="display:flex;"><span>tar -xzvf gmp-...tar.gz
</span></span><span style="display:flex;"><span>cd gmp-...
</span></span><span style="display:flex;"><span>./configure --prefix<span style="color:#f92672">=</span>$GMPDIR
</span></span><span style="display:flex;"><span>make
</span></span><span style="display:flex;"><span>make install
</span></span></code></pre></div><ol start="2">
<li>Install MPFR to a specific subdirectory in home, pointing to our GMP install prefix. Example:</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export MPFRDIR<span style="color:#f92672">=</span>/home/username/mpfr
</span></span><span style="display:flex;"><span>tar -xzvf mpfr-...tar.gz
</span></span><span style="display:flex;"><span>cd mpfr-...
</span></span><span style="display:flex;"><span>./configure --prefix<span style="color:#f92672">=</span>$MPFRDIR --with-gmp<span style="color:#f92672">=</span>$GMPDIR
</span></span><span style="display:flex;"><span>make
</span></span><span style="display:flex;"><span>make install
</span></span></code></pre></div><ol start="3">
<li>Install MPC to a specific subdirectory in home, pointing to the two previous install suffixes. Example:</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export MPCDIR<span style="color:#f92672">=</span>/home/username/mpc
</span></span><span style="display:flex;"><span>tar -xzvf mpc-...tar.gz
</span></span><span style="display:flex;"><span>cd mpc-...
</span></span><span style="display:flex;"><span>./configure --prefix<span style="color:#f92672">=</span>$MPCDIR --with-gmp<span style="color:#f92672">=</span>$GMPDIR --with-mpfr<span style="color:#f92672">=</span>$MPFRDIR
</span></span><span style="display:flex;"><span>make
</span></span><span style="display:flex;"><span>make install
</span></span></code></pre></div><ol start="4">
<li>Now we can build gcc using these. Somehow, I wrote down that I had to define the library paths explicitly <em>before</em> this, so I&rsquo;ll include it here as well:</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export LD_LIBRARY_PATH<span style="color:#f92672">=</span>$GMPDIR/lib:$MPFRDIR/lib:$MPCDIR/lib:$LD_LIBRARY_PATH
</span></span><span style="display:flex;"><span><span style="color:#75715e"># gcc recommends you to</span>
</span></span><span style="display:flex;"><span>../configure --prefix<span style="color:#f92672">=</span>/opt/gcc-11.5.0 --with-gmp<span style="color:#f92672">=</span>$GMPDIR --with-mpfr<span style="color:#f92672">=</span>$MPFRDIR --with-mpc<span style="color:#f92672">=</span>$MPCDIR
</span></span></code></pre></div><ol start="5">
<li>After this it&rsquo;s simply building and installing as per usual:</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>make <span style="color:#75715e"># with -j whatever if you want</span>
</span></span><span style="display:flex;"><span>make install
</span></span></code></pre></div><ol start="6">
<li>The final step is to make sure it runs (instead of your system&rsquo;s default version):</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export PATH<span style="color:#f92672">=</span>/opt/gcc-11.5.0/bin:$PATH
</span></span><span style="display:flex;"><span>export LD_LIBRARY_PATH<span style="color:#f92672">=</span>/opt/gcc-11.5.0/lib:$LD_LIBRARY_PATH
</span></span><span style="display:flex;"><span><span style="color:#75715e"># I usually chuck these into my ~/.bashrc</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># and disable it if i want to switch back to the system version.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Kinda like a manual way of handling alternatives.</span>
</span></span></code></pre></div><p>And that&rsquo;s all.</p>
]]></content></item><item><title>Stumbling into a sweep line algorithm's edge case</title><link>https://icyveins7.github.io/posts/2025/09/stumbling-into-a-sweep-line-algorithms-edge-case/</link><pubDate>Wed, 10 Sep 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/09/stumbling-into-a-sweep-line-algorithms-edge-case/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Mistakes that you should learn from, lesson 1..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I recently wrote some code for interval merging. Every leetcoder can probably recite 15 algorithms to do this in their sleep, but I figured it out myself in this case, and in the process fell into an edge case.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s to writing down your mistakes.&lt;/p&gt;
&lt;h1 id="interval-merging"&gt;Interval merging&lt;/h1&gt;
&lt;p&gt;The generic interval merging problem provides you with a list of start/stop pairs which denote individual intervals. Each interval is then to be merged with any other overlapping intervals; an overlap is defined by at least 1 element being shared between 2 intervals.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Mistakes that you should learn from, lesson 1..</p>
</blockquote>
<p>I recently wrote some code for interval merging. Every leetcoder can probably recite 15 algorithms to do this in their sleep, but I figured it out myself in this case, and in the process fell into an edge case.</p>
<p>Here&rsquo;s to writing down your mistakes.</p>
<h1 id="interval-merging">Interval merging</h1>
<p>The generic interval merging problem provides you with a list of start/stop pairs which denote individual intervals. Each interval is then to be merged with any other overlapping intervals; an overlap is defined by at least 1 element being shared between 2 intervals.</p>
<p>There are several variants of this problem, depending on how the data structure of the input pairs, but I&rsquo;ll focus on the one I ended up with:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Event</span> {
</span></span><span style="display:flex;"><span>  size_t idx;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">uint8_t</span> flag; <span style="color:#75715e">// 1: start, 0: stop
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h1 id="always-be-sorting">Always be sorting</h1>
<p>The first step in all interval merge solutions is to sort the intervals. This is, of course, to put adjacent and possibly overlapping intervals next to each other so they can be compared over a single pass.</p>
<p>In my case, the disparate <code>Event</code>s need to be sorted, so this is as simple as using <code>std::sort</code> with a defined comparison operator:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">bool</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">&lt;</span>(<span style="color:#66d9ef">const</span> Event<span style="color:#f92672">&amp;</span> rhs) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> idx <span style="color:#f92672">&lt;</span> rhs.idx;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h1 id="energy-states-determine-closure"><em>Energy</em> states determine closure</h1>
<p>The sweep line algorithm now essentially <em>sweeps</em> through the sorted list of on (1) and off (0) events. At each event, it does the following:</p>
<ol>
<li>If the current counter is 0 and the event flag is 1, add a new open interval with an unknown ending.</li>
<li>If the current counter is more than 0 and the event flag is 1, simply increment the counter.</li>
<li>If the event flag is 0, decrement the counter.</li>
<li>If the counter reaches 0, close the current interval.</li>
</ol>
<pre tabindex="0"><code># Evolution of counter over events
X-----X  X-----X
|   X-------X  |
|   | |  |  |  |
1   2 1  2  1  0
</code></pre><p>The physicist in me likes to see this as going up and down energy states (<em>no? just me?</em>).</p>
<h1 id="the-mistake-and-the-fix">The mistake (and the fix)</h1>
<p>There is one special case that causes this to fall apart, and it has to do with the way we defined (or failed to properly define) the sorting order.</p>
<p>Because we deal only with events, and the above comparison operator only checks the index, there is no guarantee of whether an <strong>on</strong> event will come before an <strong>off</strong> event if both of them have the same index, <em>and this is specifically what we require</em>.</p>
<p>To see why, simply consider the below scenario, which should result in a single merged interval.</p>
<pre tabindex="0"><code>X----X
     X-----X
</code></pre><p>We necessarily have the following events:</p>
<ul>
<li>Index $a$, flag 1</li>
<li>Index $b$, flag 0</li>
<li>Index $b$, flag 1</li>
<li>Index $c$, flag 0</li>
</ul>
<p>Ideally, after sorting, due to the counter mechanism outlined above, we would need the two events with the flag 1 to be next to each other. This would result in the counter changing as $1 \rightarrow 2 \rightarrow 1 \rightarrow 0$ as intended, merging the two intervals.</p>
<p>However, since we did not check for this, the above order may remain unchanged, causing the counter to do $1 \rightarrow 0$ twice, leaving the two intervals separated, with an illogical start/stop on the same index.</p>
<p><del>In fact, this may result in very odd size-less intervals when there more overlaps occur. Consider the following 4 intervals:</del></p>
<pre tabindex="0"><code>X-----X
  X---X
      X------X
      X----X
</code></pre><p><del>If we just focus on the 4-way overlap in the centre, we have two 1s and two 0s as possible event flags. In the worst case, this may be ordered as $0,1,0,1$, which would result in an interval that starts and stops on the same index (from the centre 1 and 0).</del></p>
<blockquote>
<p>Edit: I previously thought the above would result in a 0 size interval, but on second thought I was wrong.</p>
</blockquote>
<p>The fix is then, of course, to use flags to sort when indices match:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">bool</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">&lt;</span>(<span style="color:#66d9ef">const</span> Event<span style="color:#f92672">&amp;</span> rhs) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (idx <span style="color:#f92672">==</span> rhs.idx)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> idx.flag <span style="color:#f92672">&gt;</span> rhs.flag; <span style="color:#75715e">// 1s come before 0s
</span></span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> idx <span style="color:#f92672">&lt;</span> rhs.idx;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content></item><item><title>Some tips for integrating small bits of CUDA code into larger codebases</title><link>https://icyveins7.github.io/posts/2025/08/some-tips-for-integrating-small-bits-of-cuda-code-into-larger-codebases/</link><pubDate>Wed, 13 Aug 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/08/some-tips-for-integrating-small-bits-of-cuda-code-into-larger-codebases/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Some lessons from general C++ come in handy here..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In some recent work I integrated some CUDA code I developed into a larger existing codebase, written for the CPU. Essentially, my module(s) would accelerate and replace some existing functionality, but was only a small cog in the machine.&lt;/p&gt;
&lt;p&gt;This is likely applicable to many others, so hopefully the lessons I document here will be concisely useful to those who chance upon this post.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Some lessons from general C++ come in handy here..</p>
</blockquote>
<p>In some recent work I integrated some CUDA code I developed into a larger existing codebase, written for the CPU. Essentially, my module(s) would accelerate and replace some existing functionality, but was only a small cog in the machine.</p>
<p>This is likely applicable to many others, so hopefully the lessons I document here will be concisely useful to those who chance upon this post.</p>
<h1 id="the-obvious-bits">The obvious bits</h1>
<p>During my kernel development, I pulled out the necessary functionality and experimented on it in a small standalone project, like any sane person. Like most of my personal projects, this lets me use CMake, but the larger codebase was built on (mostly handmade) raw Makefiles.</p>
<p>Inspecting <code>compile_commands.json</code> produced by CMake will reveal two common compiler options used for all CUDA code:</p>
<ol>
<li><code>-forward-unknown-to-host-compiler</code>, <a href="https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/#forward-unknown-to-host-compiler-forward-unknown-to-host-compiler">which does what it says on the tin</a>. You can also use the <a href="https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/#forward-unknown-opts-forward-unknown-opts">lazier form of this</a>, <code>-forward-unknown-opts</code>, though I don&rsquo;t usually bother until linker errors start showing up.</li>
<li><code>-x cu</code> for every source file. This isn&rsquo;t technically required for <code>.cu</code> files since <code>nvcc</code> will recognise them as CUDA files, but becomes important when you place your eventual code into existing <code>.cpp</code> files. <a href="https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/#x-c-c-cu-x">This ensures they are compiled as CUDA</a>.</li>
</ol>
<h1 id="relocatable-device-code-requirements">Relocatable device code requirements</h1>
<p>The codebase is also partitioned into many separate distinct libraries, compiled individually and then linked together at the end to create one final executable. Of course, my standalone project with its multiple small individual executables and unit tests didn&rsquo;t need any extra compiler flags, but integrating the code as it was made <code>nvcc</code> cry at me.</p>
<p>This may have been partly due to the structure in the codebase itself, but the solution was fairly simple:</p>
<blockquote>
<p>Add <code>-dc</code> to the <code>nvcc</code>&rsquo;s compiler flags, turning on <a href="https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/#device-c-dc">relocatable device code with the expectation of linkage later</a>.</p>
</blockquote>
<p>We will be returning to the docs for <code>nvcc</code> often; get used to it, because that&rsquo;s how I (you) will figure out what flags you need to add.</p>
<h1 id="macro-pollution">Macro pollution</h1>
<p>This compiler error is strictly due to existing code in the existing codebase, so you may not experience anything similar. In fact, it is not strictly a CUDA related issue, but could occur with any code.</p>
<p>The observed error was something like this, upon inclusion of anything that used <code>cub</code>, either directly or indirectly, like via <code>thrust</code> headers:</p>
<pre tabindex="0"><code>C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.9\bin/../include\cub/warp/specializations/warp_scan_shfl.cuh(408): error: identifier &#34;input&#34; is undefined
    __declspec(__device__) __forceinline  InclusiveScanStep( input, ScanOpT scan_op, int first_lane, int offset)    
                                                             ^
</code></pre><p>Note that this never occurred in my standalone project, so it was odd that these headers would suddenly cause problems here.</p>
<p>As mentioned above, this isn&rsquo;t specifically a CUDA issue, but I&rsquo;ll show how I investigated it in the context of using <code>nvcc</code>.</p>
<h2 id="read-the-preprocessor-output">Read the preprocessor output</h2>
<p>We can <a href="https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/#keep-keep">dump all intermediate output from <code>nvcc</code></a> via <code>-keep</code>. This lets you see, among many other things, the preprocessor output in a file usually suffixed with <code>.ii</code>.</p>
<p>Opening it in my case showed that the input argument&rsquo;s template argument, <code>_T</code>, had somehow been stripped away.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// note: not the exact same function as the above one, but obvious that the first template argument is gone
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> , <span style="color:#66d9ef">typename</span> ScanOpT<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">__declspec</span>(__device__) <span style="color:#66d9ef">__forceinline</span> 
</span></span><span style="display:flex;"><span>InclusiveScanStep( input, ScanOpT scan_op, <span style="color:#66d9ef">int</span> first_lane, <span style="color:#66d9ef">int</span> offset, <span style="color:#f92672">::</span>cuda<span style="color:#f92672">::</span>std<span style="color:#f92672">::</span>true_type )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">InclusiveScanStep</span>(input, scan_op, first_lane, offset);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This smells of macro replacement, but where is it coming from? Searching the original preprocessor output file for <code>#define _T</code> didn&rsquo;t seem to yield anything.</p>
<p>I had to enable the <code>gcc</code>-specific option <code>-dD</code> in order to <a href="https://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html#index-dD">enable the <code>define</code> directives</a>. Then I was able to track down an include file several levels up which happened to do exactly <code>#define _T</code>. Welp, there goes all the <code>cub</code> headers I guess.</p>
<p>A simple <code>#undef _T</code> in my translation unit and I was back on track.</p>
<h1 id="hiding-cuda-from-the-rest-of-the-codebase">Hiding CUDA from the rest of the codebase</h1>
<p>Congrats for making it this far down the post. Below lies the meat of the problems I had. Almost all my issues stemmed from having to do this, simply because it would be too gargantuan a task to refactor everything else.</p>
<p>I&rsquo;ve already mentioned above that compiling <code>.cpp</code> files as CUDA requires the <code>nvcc</code> option <code>-x cu</code>; in fact, this is what CMake does overtly, even for <code>.cu</code> files, once you use</p>
<pre tabindex="0"><code>set_source_files_properties(sourcefile.cu PROPERTIES LANGUAGE CUDA)
</code></pre><p>as seen in many CUDA examples.</p>
<h2 id="why-you-should-specify-source-file-compilation-explicitly">Why you should specify source file compilation explicitly</h2>
<p>Aside from the fact that your code probably won&rsquo;t compile, let&rsquo;s go into exactly why that actually happens, and why using your host compiler doesn&rsquo;t work:</p>
<ol>
<li><em>Device code will not be parsable.</em> This should be obvious, but it is important to note that this will also happen if you have a header-only template calling a kernel, having some <code>__device__</code> function, or any other CUDA-specific syntax. This will become important later.</li>
<li><em>CUDA headers and libraries will not get automatically found</em>. Now this is arguably fixable when the project is small by explicitly adding CUDA header and library paths to the Makefiles (or equivalent), but this quickly gets unwieldy once the project starts getting larger. There is also no guarantee that all the CUDA headers will just work; some may include some <code>__device__</code> code, for example.</li>
<li><em>Hyper-aggressive inlining of code.</em> This is, of course, preferable in most instances of device code, but can cause issues in some CPU-side code sometimes. Fairly rare, but this was observed for one file in my case.</li>
</ol>
<p>The above probably isn&rsquo;t a definitive list, but hopefully it&rsquo;s convincing enough. They are all underscored by a general software idea though: <strong>separation of concerns</strong>. We shouldn&rsquo;t use <code>nvcc</code> for things unless strictly necessary.</p>
<h2 id="but-including-cuda-headers-implies-parsing-them">But including CUDA headers implies parsing them</h2>
<p>Here&rsquo;s the problem. You&rsquo;ve already developed some CUDA code: some kernels, and some encapsulated classes to help call them and/or manage some device-side states and workspaces. Perhaps you also want some of the external code to hold on to some memory through <code>thrust::host_vector</code> or <code>thrust::device_vector</code>.</p>
<p>Normally, it wouldn&rsquo;t matter much (unless you are extremely pedantic) where you included headers like <code>thrust/device_vector.h</code>. However, with our <code>nvcc</code>-specific constraints, this becomes a bit more nuanced.</p>
<blockquote>
<p><em>You can no longer make these includes in the headers of your external code, or else all other translation units which transitively include the header will need to be compiled by <code>nvcc</code></em>.</p>
</blockquote>
<p>This is probably not a good idea, even if you think you are okay with the maintenance.</p>
<h2 id="a-transitive-inclusion-example">A transitive inclusion example</h2>
<p>I can give a MWO of my scenario. Consider the following implementation for a class in the larger codebase:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// codebaseclass.h
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;thrust/device_vector.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CodebaseClass</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">private</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span>  thrust<span style="color:#f92672">::</span>device_vector<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">int</span><span style="color:#f92672">&gt;</span> m_vec;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// codebaseclass.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;codebaseclass.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// all the implementation details for CodebaseClass in here
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// ...
</span></span></span></code></pre></div><p>Now we set our build system to <code>nvcc</code> for <code>codebaseclass.cpp</code>, and we should be good to go, right?</p>
<p>No, but you forgot that somewhere else in the codebase, something else uses this header:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// othercodebase.h
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;codebaseclass.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// othercodebase.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;othercodebase.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// ...
</span></span></span></code></pre></div><p>Now when the compiler sees the translation unit for <code>othercodebase.cpp</code>, it pulls in <code>codebaseclass.h</code> as well, and doesn&rsquo;t know how to deal with the <code>thrust</code> vector.</p>
<h1 id="pimpl-ing-our-way-through">pImpl-ing our way through</h1>
<p>The pImpl pattern isn&rsquo;t new, nor is it technically meant for purposes like this; it is meant to hide functionality from a client interface. Here, we&rsquo;re using it to hide the functionality from our compilers.</p>
<p>The idea is to simply forward declare our CUDA-classes (including any that we have defined that use CUDA things), and only store pointers to our objects, rather than the objects themselves.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// codebaseclass.h
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// forward declaration
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> thrust{
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> T<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">device_vector</span><span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CodebaseClass</span>{
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span>  CodebaseClass();
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">~</span>CodebaseClass();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">private</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span>  std<span style="color:#f92672">::</span>unique_ptr<span style="color:#f92672">&lt;</span>thrust<span style="color:#f92672">::</span>device_vector<span style="color:#f92672">&gt;</span> m_vec;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// codebaseclass.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;codebaseclass.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;thrust/device_vector.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// ... use the device vector as per normal, just that it&#39;s via a pointer
</span></span></span></code></pre></div><p>Now, transitively including <code>codebaseclass.h</code> into <code>othercodebase.h</code> does not cause problems, because a pointer is simply an address to the compiler. It expects that if you <em>actually need to use it</em>, you will link it in yourself later.</p>
<blockquote>
<p>Yes, technically pImpl is a lot more than this, and usually involves a class-internal forward declaration which gets inherited from. We don&rsquo;t really need all that here, and pImpl-ing has a nicer ring to it than &lsquo;forward-declaring&rsquo;.</p>
</blockquote>
<h2 id="why-this-works-and-when-it-doesnt">Why this works, and when it doesn&rsquo;t</h2>
<p>This goes back to compiler fundamentals, and essentially boils down to what information the compiler needs when a full type is specified.</p>
<p>When the entire class object is a direct member of another class, like I wrote originally, the compiler must know how to construct and destroy it. This is because its inclusion as a member variable implies that it must be default constructed. Without the class header/implementation like <code>thrust/device_vector.h</code> in this case, it will be unable to do this.</p>
<p>Similarly, you wouldn&rsquo;t be able to call any methods directly without first having its definition e.g. having an inline function that calls <code>m_vec-&gt;reserve()</code>. In my above example, we just store the pointer to it, which is basically saying &ldquo;<em>store an address first, I&rsquo;ll tell you what it does later, don&rsquo;t worry about it</em>&rdquo;.</p>
<p>Now there are a few additional things to note when we implement this pattern. Notice how I <em>did not define the destructor inline within the header</em>. This isn&rsquo;t an arbitrary decision; the code will not compile if we had done so i.e. written the innocuous-looking</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#f92672">~</span>CodebaseClass(){}
</span></span></code></pre></div><p><strong>even if we were going to do this anyway in the source file!</strong></p>
<p>Why does this happen? Because the compiler will emit full code to destroy all objects at the point where the destructor is <em>defined</em>. So if you define it inline in the header, it <em>must know how to destroy <code>thrust::device_vector</code>, and since we only forward declared it, it will not know how to</em>.</p>
<p>The two options in this case are to:</p>
<ol>
<li>Define the destructor out-of-line i.e. in the associated <code>.cpp</code>, by which point the compiler should have full knowledge of the <code>thrust::device_vector</code> type. This is <em>particularly important for polymorphic classes</em> since the compiler does not auto-generate a virtual destructor, so you must always declare it in the header at least, but <em>not</em> define it there.</li>
<li>Simply not defining it at all. The compiler will emit it after it has the full definition of the encompassing class, which occurs in the <code>.cpp</code>, same as the above.</li>
</ol>
<blockquote>
<p>Note that the second version is <em>not</em> equivalent to writing <code>~CodebaseClass() = default</code> in the header, as the compiler will then attempt to emit it in the header again, and the same problem will arise. You can, however, declare it in the header <em>and then write <code>~CodebaseClass() = default;</code></em> in the source <code>.cpp</code> file.</p>
</blockquote>
<h2 id="if-you-really-want-the-full-pimpl-experience">If you really want the full pImpl experience..</h2>
<p>Then you can use constructor inheritance to get you most of the way there. In these scenarios you would actually forward declare a <code>Impl</code> class or struct for whatever it is you want to hide.</p>
<p>A lot of the time, you don&rsquo;t really need to care about the constructor for this hidden implementation, and you just want to use your hidden class&rsquo;s constructor directly. Then something like this in the <code>.cpp</code> would work:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Impl</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> RealClass{
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">using</span> RealClass<span style="color:#f92672">::</span>RealClass; <span style="color:#75715e">// this inherits all the constructors
</span></span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Again, this isn&rsquo;t a strictly CUDA thing, but it&rsquo;s useful to remember it in this context.</p>
]]></content></item><item><title>Quickly sketching out a BlockQuickSelect</title><link>https://icyveins7.github.io/posts/2025/07/quickly-sketching-out-a-blockquickselect/</link><pubDate>Sun, 20 Jul 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/07/quickly-sketching-out-a-blockquickselect/</guid><description>&lt;blockquote&gt;
&lt;p&gt;I wrote a blockwide quickselect. That&amp;rsquo;s the post.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As of today, NVIDIA&amp;rsquo;s &lt;code&gt;cub&lt;/code&gt; library has a few block-wide primitives to do sorting - like &lt;code&gt;BlockRadixSort&lt;/code&gt; - but none that do the equivalent for the $k$&amp;lsquo;th order statistic i.e the $k$-th smallest element.&lt;/p&gt;
&lt;p&gt;This would be functionally equivalent to the CPU&amp;rsquo;s &lt;code&gt;std::nth_element&lt;/code&gt;, but that &lt;a href="https://en.cppreference.com/w/cpp/algorithm/nth_element.html"&gt;apparently uses Introselect&lt;/a&gt;, which is a tad too much for me to want to implement. I&amp;rsquo;m going to stick with the simpler &lt;a href="https://en.m.wikipedia.org/wiki/Quickselect"&gt;quickselect&lt;/a&gt;.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>I wrote a blockwide quickselect. That&rsquo;s the post.</p>
</blockquote>
<p>As of today, NVIDIA&rsquo;s <code>cub</code> library has a few block-wide primitives to do sorting - like <code>BlockRadixSort</code> - but none that do the equivalent for the $k$&lsquo;th order statistic i.e the $k$-th smallest element.</p>
<p>This would be functionally equivalent to the CPU&rsquo;s <code>std::nth_element</code>, but that <a href="https://en.cppreference.com/w/cpp/algorithm/nth_element.html">apparently uses Introselect</a>, which is a tad too much for me to want to implement. I&rsquo;m going to stick with the simpler <a href="https://en.m.wikipedia.org/wiki/Quickselect">quickselect</a>.</p>
<h1 id="what-we-need">What we need</h1>
<p>Similar to <code>cub</code>&rsquo;s block-wide primitives, we probably want to just get some shared memory and work on the data inside it. I&rsquo;m at a crossroads here: using statically allocated shared memory like <code>cub</code> via templates makes it easy to implement, but doesn&rsquo;t let you handle differing sizes per block (at least not explicitly).</p>
<p>But dynamic allocation may take a bit more time to implement?</p>
<p><del>Meh, let&rsquo;s just go with static for now.</del> I ended up doing some half baked static and dynamic nonsense, but whatever.</p>
<h1 id="the-flow">The flow</h1>
<ol>
<li>Load block&rsquo;s array from global to shared memory.</li>
<li>Sync threads.</li>
<li>Select pivot; just use the first element(?)</li>
<li>All threads other than pivot compare against pivot.</li>
<li>Warp-wide counters for <code>&lt; pivot</code> and <code>&gt; pivot</code> get warp-aggregated atomically added to the two counters in shared memory.</li>
<li>The previous warp aggregated counters let us update a second workspace with the left-side and right-side elements. The way I thought to do this would basically be writing all left-side elements from index 0, forwards. Right-side elements would then be written from index N-1 backwards.</li>
</ol>
<pre tabindex="0"><code># example input
4 5 6 3 2
|
pivot

# example output
3 2 X 6 5
    |
    not used
</code></pre><ol start="7">
<li>Sync threads.</li>
<li>Check counters and find which side to recurse into, or end if the pivot is the answer.</li>
<li>Go back to 3.</li>
</ol>
<p>Sounds easy enough. I&rsquo;m sure I can do this in <del>half an hour</del> <del>1 hour</del> 2 hours, right?</p>
<h2 id="why-not-just-swap-rather-than-use-a-second-workspace">Why not just swap rather than use a second workspace?</h2>
<p>Because it&rsquo;s easy. Honestly, maybe doing the swaps would be faster <em>and</em> more memory-efficient, but this was quicker to implement (read the title).</p>
<p>Here I just allocate enough shared memory for two workspaces of equal length, and swap them after every iteration; no need to care about handling races across the blocks.</p>
<h1 id="how-fast-is-it">How fast is it?</h1>
<p>As it turns out, for my test with 10000 rows of randomized lengths (up to a max of 100), with each block taking one row, this was about 50 % faster (33% reduction in time) than <code>cub::BlockRadixSort</code>! I compared it to selecting the median element here (which is what I was concerned with):</p>
<pre tabindex="0"><code> ** CUDA GPU Kernel Summary (cuda_gpu_kern_sum):

 Time (%)  Total Time (ns)  Instances  Avg (ns)  Med (ns)  Min (ns)  Max (ns)  StdDev (ns)                                                  Name
 --------  ---------------  ---------  --------  --------  --------  --------  -----------  ----------------------------------------------------------------------------------------------------
     59.2            62336          1   62336.0   62336.0     62336     62336          0.0  void blockwise_median_kernel&lt;unsigned short, (int)128, (int)1, (bool)1&gt;(const T1 *, int, int, const.
     38.0            40000          1   40000.0   40000.0     40000     40000          0.0  void blockwise_quickselect_kernel&lt;unsigned short&gt;(const T1 *, int, int, int *, int *, T1 *)
</code></pre><p><em>Above timings from my 5080. Also, what a timing - exactly 40000 lol.</em></p>
<p>Not too bad for 2 hours of work I guess? Code is <a href="https://github.com/icyveins7/gpu_benchmarks/blob/master/proj_median/median.cuh">here</a> for those who want to <code>nsys profile</code> it yourself on your GPU. <em>And check out the rest of my repo while you&rsquo;re at it!</em></p>
<p>The struct works fairly similarly to the <code>BlockRadixSort</code>, in the sense that you instantiate it at the thread level and then pass it some shared memory. Everything else is taken care of inside the <code>__device__</code> methods, and the result should be valid for the first thread, as seen in the example.</p>
<p>Like previously mentioned, it assumes a maximum length for a row input, but only utilises a row-specific length when performing the calculation.</p>
]]></content></item><item><title>Old dog, old C struct tricks</title><link>https://icyveins7.github.io/posts/2025/07/old-dog-old-c-struct-tricks/</link><pubDate>Mon, 14 Jul 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/07/old-dog-old-c-struct-tricks/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Look at this code snippet and tell me with a straight face you didn&amp;rsquo;t think it was a memory leak at first either..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A while ago I worked on some code which contained a container that looked like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;S&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; size_t sz;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; data[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Set&lt;/span&gt;(size_t _sz, &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;_data){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sz &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _sz;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; memcpy(data, _data, &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;)&lt;span style="color:#f92672"&gt;*&lt;/span&gt;sz);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re a modern programmer like me, you might be looking at this and thinking: &lt;em&gt;there&amp;rsquo;s no way this isn&amp;rsquo;t a flagrant memory violation in almost every case&lt;/em&gt;. Right? Well, it turns out this is something them old boys before C99 used to do.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Look at this code snippet and tell me with a straight face you didn&rsquo;t think it was a memory leak at first either..</p>
</blockquote>
<p>A while ago I worked on some code which contained a container that looked like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">S</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  size_t sz;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">int</span> data[<span style="color:#ae81ff">1</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Set</span>(size_t _sz, <span style="color:#66d9ef">int</span> <span style="color:#f92672">*</span>_data){
</span></span><span style="display:flex;"><span>    sz <span style="color:#f92672">=</span> _sz;
</span></span><span style="display:flex;"><span>    memcpy(data, _data, <span style="color:#66d9ef">sizeof</span>(<span style="color:#66d9ef">int</span>)<span style="color:#f92672">*</span>sz);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If you&rsquo;re a modern programmer like me, you might be looking at this and thinking: <em>there&rsquo;s no way this isn&rsquo;t a flagrant memory violation in almost every case</em>. Right? Well, it turns out this is something them old boys before C99 used to do.</p>
<h1 id="cool-kids-are-manual-and-contiguous">Cool kids are manual and contiguous</h1>
<p>The above <code>struct</code> works because it is meant to be manually managed, and because the array is <em>the last field of the struct</em>.</p>
<p>The idea was that you would heap-allocate the struct with <em>padded bytes</em> according to your required array length; this was the poor man&rsquo;s dynamic array allocation back then.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">S</span><span style="color:#f92672">*</span> sptr <span style="color:#f92672">=</span> (<span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">S</span><span style="color:#f92672">*</span>)malloc(<span style="color:#66d9ef">sizeof</span>(<span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">S</span>) <span style="color:#f92672">+</span> <span style="color:#66d9ef">sizeof</span>(<span style="color:#66d9ef">int</span>) <span style="color:#f92672">*</span> (N<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>));
</span></span><span style="display:flex;"><span><span style="color:#75715e">// we have -1 because the struct already has room for 1 int
</span></span></span></code></pre></div><p>Now, when you access <code>sptr.data[i]</code>, you are simply looking farther into the memory block you have already allocated. You could easily range-protect this access with an <code>std::vector</code>-like method; the cool thing here is that we get to have <strong>all the memory in one contiguous block</strong>.</p>
<p>This is unlike <code>std::vector</code> (and other containers) where the parameters like the size and capacity live on the stack, whereas the data is heap-allocated somewhere else.</p>
<p>Nowadays, post-C99, the syntax is a bit less misleading:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">S</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  size_t sz;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">int</span> data[]; <span style="color:#75715e">// this now indicates dynamic length. Still needs to be the last member though, and still needs manual management
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Set</span>(size_t _sz, <span style="color:#66d9ef">int</span> <span style="color:#f92672">*</span>_data){
</span></span><span style="display:flex;"><span>    sz <span style="color:#f92672">=</span> _sz;
</span></span><span style="display:flex;"><span>    memcpy(data, _data, <span style="color:#66d9ef">sizeof</span>(<span style="color:#66d9ef">int</span>)<span style="color:#f92672">*</span>sz);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The benefits are still the same though! Parameters and data, living happily next to each other!</p>
<h1 id="premature-optimization-is-the">Premature optimization is the..</h1>
<p>Yeah yeah, but I looked into it anyway. A quick chat with our favourite GPT brought up a topic I expected: cache-lines. But it was somewhat opposite to my intuition, which was interesting.</p>
<p>I thought that having things contiguous would benefit small data the most, but it turns out that the cache changes things (<em>surprise surprise</em>).</p>
<p>The crucial idea here is the <strong>total size of all the structs</strong>. Assuming you have to iterate/read all of them at least once, there shouldn&rsquo;t be any significant difference between the small struct and the large struct if all of them fit in L1, because you pull data in cache lines anyway.</p>
<p>The real driver of costs comes from reaching further out to L2, L3 and RAM. This is where contiguity - and the associated prefetching - plays a huge role. Having them all read one after another will assist hardware prefetching, which is present in most CPUs these days.</p>
<p>I will be the first to admit that I didn&rsquo;t test or benchmark this myself, because this is out of my concern. I merely wanted to highlight this interesting old C trick, and hope someone else recognizes that sometimes, boomers&rsquo; code isn&rsquo;t the trash you think it is.</p>
]]></content></item><item><title>Interesting Tidbits from GTC 2025: CUDA Graphs</title><link>https://icyveins7.github.io/posts/2025/03/interesting-tidbits-from-gtc-2025-cuda-graphs/</link><pubDate>Tue, 25 Mar 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/03/interesting-tidbits-from-gtc-2025-cuda-graphs/</guid><description>&lt;blockquote&gt;
&lt;p&gt;In the 2nd post of this series, I give a short introduction on something that is also not particularly new, but new to me - CUDA Graphs!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As stated in my first post of this series, this topic isn&amp;rsquo;t particularly new per-se. Indeed, it looks like it has been out since 2019. But maybe CUDA graphs have increased in relevance now that GPUs are more powerful. As usual, a good starting reference is NVIDIA&amp;rsquo;s own &lt;a href="https://developer.nvidia.com/blog/cuda-graphs/"&gt;blogpost&lt;/a&gt;.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>In the 2nd post of this series, I give a short introduction on something that is also not particularly new, but new to me - CUDA Graphs!</p>
</blockquote>
<p>As stated in my first post of this series, this topic isn&rsquo;t particularly new per-se. Indeed, it looks like it has been out since 2019. But maybe CUDA graphs have increased in relevance now that GPUs are more powerful. As usual, a good starting reference is NVIDIA&rsquo;s own <a href="https://developer.nvidia.com/blog/cuda-graphs/">blogpost</a>.</p>
<p>Note that CUDA graphs require a minimum toolkit version of 12.4, with some additional features being present in the newest (as of this writing) version, 12.8.</p>
<h1 id="why-use-graphs">Why Use Graphs?</h1>
<p>Perfectly reasonable question. The problem we are trying to combat when using graphs is <em>overhead</em>. This is really only present when our kernels are <em>short</em>, <em>repeated</em> or both.</p>
<p>In those scenarios, the overhead of the kernel launch becomes <strong>comparable</strong> to the duration of the kernel itself. This is often observed in an Nsight Systems timeline where there are visible gaps between the kernels after zooming in (yes, of course there are always gaps, but its the relative size of the gap to the size of the kernel that matters).</p>
<h1 id="how-it-works-in-a-nutshell">How It Works in a Nutshell</h1>
<p>I liken this process to the distinction between <em>compiled</em> and <em>interpreted</em> languages. Interpreted languages are usually slower - yes, I know many nowadays have JIT and can be optimized pretty well, but bear with me for the comparison - because each command or instruction has to be submitted individually. This is necessary since all code is parsed only at runtime, so to balance start-up time with actual run duration, some assumptions are made and/or not all optimizations are performed. <em>Of course, this is a vast simplification, but the idea still stands.</em></p>
<p>Compiled languages don&rsquo;t suffer from this, since you as the programmer get to choose what occurs at runtime and what occurs at compile time. This affords the compiler a lot of flexibility to optimize as much as possible.</p>
<p>In the same vein, CUDA graphs offer the compiler a way to &lsquo;compile&rsquo; multiple kernels together, and also submitting the work in a more &lsquo;compressed, single call&rsquo;. This is, in fact, how it appears in a standard Nsight Systems profiler timeline if no additional settings are specified.</p>
<p>In plain English, CUDA graphs allows you to tell the device to</p>
<blockquote>
<p>do these N kernels in a row</p>
</blockquote>
<p>rather than</p>
<blockquote>
<p>do this kernel, then do this kernel, then do this kernel &hellip;.</p>
</blockquote>
<h1 id="method-1-record-your-kernels">Method 1: Record Your Kernels</h1>
<p>This is the simpler method to convert existing code to the graph format. As seen from the blog post, you can do the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">bool</span> graphCreated<span style="color:#f92672">=</span>false;
</span></span><span style="display:flex;"><span>cudaGraph_t graph;
</span></span><span style="display:flex;"><span>cudaGraphExec_t instance;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span>(<span style="color:#66d9ef">int</span> istep<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>; istep<span style="color:#f92672">&lt;</span>NSTEP; istep<span style="color:#f92672">++</span>){
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Create graph only on first iteration
</span></span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span>(<span style="color:#f92672">!</span>graphCreated){
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Start &#39;recording&#39;
</span></span></span><span style="display:flex;"><span>    cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span>(<span style="color:#66d9ef">int</span> ikrnl<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>; ikrnl<span style="color:#f92672">&lt;</span>NKERNEL; ikrnl<span style="color:#f92672">++</span>){
</span></span><span style="display:flex;"><span>      shortKernel<span style="color:#f92672">&lt;&lt;&lt;</span>blocks, threads, <span style="color:#ae81ff">0</span>, stream<span style="color:#f92672">&gt;&gt;&gt;</span>(out_d, in_d);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Stop &#39;recording&#39;
</span></span></span><span style="display:flex;"><span>    cudaStreamEndCapture(stream, <span style="color:#f92672">&amp;</span>graph);
</span></span><span style="display:flex;"><span>    cudaGraphInstantiate(<span style="color:#f92672">&amp;</span>instance, graph, NULL, NULL, <span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>    graphCreated<span style="color:#f92672">=</span>true;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Launch graph on every iteration
</span></span></span><span style="display:flex;"><span>  cudaGraphLaunch(instance, stream);
</span></span><span style="display:flex;"><span>  cudaStreamSynchronize(stream);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>In fact, I think you can probably just do a warm-up recording before any of the actual iterations, as long as the data pointers and all other parameters are valid. This is, of course, to avoid the much more expensive graph instantiation in your actual hot loops (even if it&rsquo;s only the first iteration).</p>
<h1 id="method-2-explicit-api-calls">Method 2: Explicit API Calls</h1>
<p>This method requires a definition of a graph in the more traditional sense: creating and connecting nodes to define dependencies.</p>
<p>As seen in the <a href="https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#creating-a-graph-using-graph-apis">documentation</a>, this looks like the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">  A
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> / \
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">B   C
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> \ /
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">  D
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">*/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Create the graph - it starts out empty
</span></span></span><span style="display:flex;"><span>cudaGraphCreate(<span style="color:#f92672">&amp;</span>graph, <span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// For the purpose of this example, we&#39;ll create
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// the nodes separately from the dependencies to
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// demonstrate that it can be done in two stages.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Note that dependencies can also be specified
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// at node creation.
</span></span></span><span style="display:flex;"><span>cudaGraphAddKernelNode(<span style="color:#f92672">&amp;</span>a, graph, NULL, <span style="color:#ae81ff">0</span>, <span style="color:#f92672">&amp;</span>nodeParams);
</span></span><span style="display:flex;"><span>cudaGraphAddKernelNode(<span style="color:#f92672">&amp;</span>b, graph, NULL, <span style="color:#ae81ff">0</span>, <span style="color:#f92672">&amp;</span>nodeParams);
</span></span><span style="display:flex;"><span>cudaGraphAddKernelNode(<span style="color:#f92672">&amp;</span>c, graph, NULL, <span style="color:#ae81ff">0</span>, <span style="color:#f92672">&amp;</span>nodeParams);
</span></span><span style="display:flex;"><span>cudaGraphAddKernelNode(<span style="color:#f92672">&amp;</span>d, graph, NULL, <span style="color:#ae81ff">0</span>, <span style="color:#f92672">&amp;</span>nodeParams);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// All the parameters go into `nodeParams`, including
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// kernel launch parameters, the kernel function pointer,
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// kernel input arguments etc.
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Now set up dependencies on each node
</span></span></span><span style="display:flex;"><span>cudaGraphAddDependencies(graph, <span style="color:#f92672">&amp;</span>a, <span style="color:#f92672">&amp;</span>b, <span style="color:#ae81ff">1</span>);     <span style="color:#75715e">// A-&gt;B
</span></span></span><span style="display:flex;"><span>cudaGraphAddDependencies(graph, <span style="color:#f92672">&amp;</span>a, <span style="color:#f92672">&amp;</span>c, <span style="color:#ae81ff">1</span>);     <span style="color:#75715e">// A-&gt;C
</span></span></span><span style="display:flex;"><span>cudaGraphAddDependencies(graph, <span style="color:#f92672">&amp;</span>b, <span style="color:#f92672">&amp;</span>d, <span style="color:#ae81ff">1</span>);     <span style="color:#75715e">// B-&gt;D
</span></span></span><span style="display:flex;"><span>cudaGraphAddDependencies(graph, <span style="color:#f92672">&amp;</span>c, <span style="color:#f92672">&amp;</span>d, <span style="color:#ae81ff">1</span>);     <span style="color:#75715e">// C-&gt;D
</span></span></span></code></pre></div><p>You might be wondering, why would you want to do this? Well, using the API to create graphs opens up a solution to 1 particular issue I suspect every CUDA programmer will eventually reach..</p>
<h2 id="conditional-nodes">Conditional Nodes</h2>
<p>There comes a time where your overarching logic must diverge based on some condition. Common examples include: iterative loop stops after convergence, check for thresholds etc.</p>
<p>In these cases, you might have some chain of logic which would be different depending on (effectively) some boolean. Now you have 2 options:</p>
<ol>
<li>Combine all the divergent logic into the kernel itself, then launch just 1 kernel, within which the divergent logic is executed at a thread level. This can get very unwieldy very quickly, and may not even be possible.</li>
<li>Copy the boolean out to the host (ugly, and also likely to be slow). Read the boolean in the host, and then decide which kernel or chain of kernels to execute.</li>
</ol>
<p>Refer to <a href="https://developer.nvidia.com/blog/dynamic-control-flow-in-cuda-graphs-with-conditional-nodes/">this blogpost</a> for an excellent introduction to this, but I&rsquo;ll summarise here.</p>
<p>Using CUDA graphs, you can now define a conditional handle via</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>cudaGraphConditionalHandle handle;
</span></span><span style="display:flex;"><span>cudaGraphConditionalHandleCreate(<span style="color:#f92672">&amp;</span>handle, graph);
</span></span></code></pre></div><p>You would then use this inside a kernel like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>__global__ <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setHandle</span>(cudaGraphConditionalHandle handle)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// as long as value is non-zero it is &#39;true&#39;
</span></span></span><span style="display:flex;"><span>    cudaGraphSetConditional(handle, value);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The graph will now execute the conditional node without returning the boolean to the host, and you have the freedom to define (potentially) an entire graph of nodes in the <code>if true</code> expression.</p>
]]></content></item><item><title>Interesting Tidbits from GTC 2025: Asynchronicity Beyond Streams</title><link>https://icyveins7.github.io/posts/2025/03/interesting-tidbits-from-gtc-2025-asynchronicity-beyond-streams/</link><pubDate>Mon, 24 Mar 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/03/interesting-tidbits-from-gtc-2025-asynchronicity-beyond-streams/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Some notes for CUDA programmers who haven&amp;rsquo;t kept up with the times; this first post covers in-kernel pipelining..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I recently had the privilege of being sponsored to attend the March 2025 GTC in person. While the sessions were very largely dominated by AI-related things, I generally selected the CUDA-related ones, since they were more relevant to my work (and interests).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll try to encapsulate some of the new things I learnt while I was there into a few major concepts. Note that most, if not all of these, are not shiny new CUDA features - some of them have been out for several years, but I just didn&amp;rsquo;t know about them, so I learnt about them there.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Some notes for CUDA programmers who haven&rsquo;t kept up with the times; this first post covers in-kernel pipelining..</p>
</blockquote>
<p>I recently had the privilege of being sponsored to attend the March 2025 GTC in person. While the sessions were very largely dominated by AI-related things, I generally selected the CUDA-related ones, since they were more relevant to my work (and interests).</p>
<p>I&rsquo;ll try to encapsulate some of the new things I learnt while I was there into a few major concepts. Note that most, if not all of these, are not shiny new CUDA features - some of them have been out for several years, but I just didn&rsquo;t know about them, so I learnt about them there.</p>
<p>Edit: it seems like each section is getting a bit lengthy, so I&rsquo;m going to split them into separate posts. This is the first one.</p>
<h1 id="youve-heard-of-async-stream-execution-now-get-ready-for-async-in-kernel-execution">You&rsquo;ve Heard of Async Stream Execution, Now Get Ready for Async In-Kernel Execution</h1>
<p>Old CUDA programmers already know about asynchronicity via memory-copy overlaps, latency hiding etc. The story is pretty simple:</p>
<ul>
<li>Enable and use non-default streams</li>
<li>Within a stream, all commands are processed serially</li>
<li>Across streams, all kernels are processed in parallel (as far as the launch is concerned, execution is still up to the SMs)</li>
<li>For memory copies to and from the host, you also have to make sure the memory is pinned</li>
</ul>
<p>CUDA stream concepts are pretty old. They are analogous (in my opinion) to CPU threads, where you simply fire off a bunch of functions/instructions that you&rsquo;d like to execute in parallel, and let the scheduler handle it; if there are SMs available to service your kernels, they will do so, similar to how if there are active CPU cores to execute CPU functions, they will do so.</p>
<h2 id="bytes-in-flight-and-memory-prefetching">Bytes In-Flight and Memory Prefetching</h2>
<p>CPUs nowadays do a pretty fantastic job of prefetching both required memory and required instructions (which has also led to hardware-level security problems but that&rsquo;s another story..).</p>
<p>Traditionally, GPUs have not done this, but we can now quite easily perform this with CUDA&rsquo;s new pipeline functions. A good resource is <a href="https://developer.nvidia.com/blog/boosting-application-performance-with-gpu-memory-prefetching/">this blog post</a>, but I&rsquo;ll try to summarise very quickly.</p>
<h2 id="conceptual-understanding">Conceptual Understanding</h2>
<p>The first concept to understand is what is known as <em>bytes in-flight</em>. This wasn&rsquo;t in the blog post link but it was mentioned many times across several sessions in GTC. The idea is that, as much as possible, you want to maximise the uptime of global memory reads or writes.</p>
<p>We already know that global memory access is slow, so what we want is to ensure we can do it as quickly as possible, and hide it as much as possible. The old ideas of global memory coalesced access and vectorised loads still hold true today, and contribute to this thought process.</p>
<p>The new idea is to then overlap memory access inside the kernel with compute. When a warp tries to access global memory (a load instruction), it takes several cycles before the values are actually ready to be operated on. If there are no other instructions that can be performed in the meantime, this results in a <em>stall</em>. Effectively, the SM <em>cannot proceed</em> because it needs the loaded data to be ready before it can continue.</p>
<p>As mentioned, this isn&rsquo;t new to GPUs, and is why prefetching was invented for CPUs. To solve this, CUDA introduced the pipeline interfaces. Let&rsquo;s assume that a load instruction that we are executing requires 5 cycles to complete (latency). We can split our kernel&rsquo;s work into batches. For each batch with index $n$, we do the following:</p>
<ol>
<li>Load the data required for batch $n+1$ asynchronously using the pipeline mechanisms. This will not block and will allow the kernel to continue executing.</li>
<li>Block/wait for data loads for batch $n$ to complete; this would have started in the previous iteration.</li>
<li>Run computations for batch $n$.</li>
</ol>
<p>A more graphical representation might look like this:</p>
<pre tabindex="0"><code>Loads   :  X---- n+1 ----&gt; X---- n+2 ----&gt; ...
Computes:  X---- n   --&gt;   X---- n+1 --&gt;   ...
</code></pre><p>I deliberately made the computes take a shorter amount of time to demonstrate the wait on the data loading. The reverse - computes take longer than loads - still warrants this method; the idea is now we are bound by whichever operation takes longer, <strong>but not by their sum</strong>.</p>
<p>It is important to remember that all of the above is taking place <em>inside the kernel</em>; although the timeline view is similar to how one might have pipelined batches previously with compute and copy streams, this is a different, <em>deeper level of asynchronicity</em>. <strong>Do not confuse this with streams!</strong></p>
<h2 id="libcudacxx-pipelines"><code>libcudacxx</code> pipelines</h2>
<p>Since the blog post covers the C-like primitives, I&rsquo;ll try to cover how you would do this via the C++ APIs provided inside <code>libcudacxx</code>. See the <a href="https://nvidia.github.io/cccl/libcudacxx/extended_api/synchronization_primitives/pipeline.html?highlight=pipeline">original API</a> for more details.</p>
<p>We&rsquo;ll look at the <em>unified</em> pipeline for now - this is the simpler case where all threads are producers and consumers. You can do fancy things with the <em>partitioned</em> pipeline where you carve out some threads to do either one, but it&rsquo;s probably not necessary for most cases.</p>
<p>The pipeline mechanism is a template construct that uses compile time constants where you define the number of stages and the scope of cooperation, similar to CUDA&rsquo;s cooperative groups. Let&rsquo;s look at a more annotated version of the example code:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;cuda/pipeline&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;cooperative_groups.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> T<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>__global__ <span style="color:#66d9ef">void</span> example_kernel(T<span style="color:#f92672">*</span> global0, T<span style="color:#f92672">*</span> global1, cuda<span style="color:#f92672">::</span>std<span style="color:#f92672">::</span>size_t subset_count) {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">extern</span> __shared__ T s[];
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">auto</span> group <span style="color:#f92672">=</span> cooperative_groups<span style="color:#f92672">::</span>this_thread_block();
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Split our shared memory into 2, one for each stage
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Here we have 2 inputs we want to load, and we load 1 element for each
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// thread, so we use 2 * group.size() for each stage
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// | stage0, input0 | stage0, input1 |
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// | stage1, input0 | stage1, input1 |
</span></span></span><span style="display:flex;"><span>	T<span style="color:#f92672">*</span> shared[<span style="color:#ae81ff">2</span>] <span style="color:#f92672">=</span> { s, s <span style="color:#f92672">+</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> group.size() };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Create a 2-stage pipeline, synchronised at the block level.
</span></span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">constexpr</span> <span style="color:#66d9ef">auto</span> scope <span style="color:#f92672">=</span> cuda<span style="color:#f92672">::</span>thread_scope_block;
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">constexpr</span> <span style="color:#66d9ef">auto</span> stages_count <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>;
</span></span><span style="display:flex;"><span>	__shared__ cuda<span style="color:#f92672">::</span>pipeline_shared_state<span style="color:#f92672">&lt;</span>scope, stages_count<span style="color:#f92672">&gt;</span> shared_state;
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">auto</span> pipeline <span style="color:#f92672">=</span> cuda<span style="color:#f92672">::</span>make_pipeline(group, <span style="color:#f92672">&amp;</span>shared_state);
</span></span><span style="display:flex;"><span>	
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Prime the pipeline.
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Here we are submitting things that we want to load asynchronously
</span></span></span><span style="display:flex;"><span>	pipeline.producer_acquire();
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// NOTE: these functions DO NOT start the memcpy. Think of it like an
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// SQL transaction; we are just queueing the things we want to happen
</span></span></span><span style="display:flex;"><span>	cuda<span style="color:#f92672">::</span>memcpy_async(group, shared[<span style="color:#ae81ff">0</span>],
</span></span><span style="display:flex;"><span>					 <span style="color:#f92672">&amp;</span>global0[<span style="color:#ae81ff">0</span>], <span style="color:#66d9ef">sizeof</span>(T) <span style="color:#f92672">*</span> group.size(), pipeline);
</span></span><span style="display:flex;"><span>	cuda<span style="color:#f92672">::</span>memcpy_async(group, shared[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">+</span> group.size(),
</span></span><span style="display:flex;"><span>					 <span style="color:#f92672">&amp;</span>global1[<span style="color:#ae81ff">0</span>], <span style="color:#66d9ef">sizeof</span>(T) <span style="color:#f92672">*</span> group.size(), pipeline);
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// This is what actually fires off the load instructions!
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Think of it as now having &#39;Producer State: 0&#39;
</span></span></span><span style="display:flex;"><span>	pipeline.producer_commit();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Question: Why did we have to do the above?
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Answer: same as with other pipeline mechanisms. We need to seed
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// the first batch before our iterations begin. Otherwise, the first
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// batch wouldn&#39;t be ready when we first need it in the loop.
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Pipelined copy/compute.
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Notice that we start on iteration 1 because that&#39;s the next batch
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// of data we are going to load
</span></span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> (cuda<span style="color:#f92672">::</span>std<span style="color:#f92672">::</span>size_t subset <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; subset <span style="color:#f92672">&lt;</span> subset_count; <span style="color:#f92672">++</span>subset) {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// The next section is identical to above;
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// we are loading the n&#39;th batch of data
</span></span></span><span style="display:flex;"><span>		pipeline.producer_acquire();
</span></span><span style="display:flex;"><span>		cuda<span style="color:#f92672">::</span>memcpy_async(group, shared[subset <span style="color:#f92672">%</span> <span style="color:#ae81ff">2</span>],
</span></span><span style="display:flex;"><span>						   <span style="color:#f92672">&amp;</span>global0[subset <span style="color:#f92672">*</span> group.size()],
</span></span><span style="display:flex;"><span>						   <span style="color:#66d9ef">sizeof</span>(T) <span style="color:#f92672">*</span> group.size(), pipeline);
</span></span><span style="display:flex;"><span>		cuda<span style="color:#f92672">::</span>memcpy_async(group, shared[subset <span style="color:#f92672">%</span> <span style="color:#ae81ff">2</span>] <span style="color:#f92672">+</span> group.size(),
</span></span><span style="display:flex;"><span>						   <span style="color:#f92672">&amp;</span>global1[subset <span style="color:#f92672">*</span> group.size()],
</span></span><span style="display:flex;"><span>						   <span style="color:#66d9ef">sizeof</span>(T) <span style="color:#f92672">*</span> group.size(), pipeline);
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Again, loading doesn&#39;t start until we commit
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// In the first iteration, we would now have &#39;Producer State: 1&#39;
</span></span></span><span style="display:flex;"><span>		pipeline.producer_commit();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// In the first iteration, at this point, we would potentially have
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// both Producer State: 0 and Producer State: 1 in flight.
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Note that we haven&#39;t done anything with the consumer yet so
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// the consumer would be at State 0.
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Here we are waiting on the PREVIOUS batch of data
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// In the first iteration, this would entail having the consumer
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// wait on State 0.
</span></span></span><span style="display:flex;"><span>		pipeline.consumer_wait();
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// After this it is hanging on to State 0 while it computes.
</span></span></span><span style="display:flex;"><span>		compute(shared[(subset <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">%</span> <span style="color:#ae81ff">2</span>]);
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// And finally when we release, the consumer increments to State 1,
</span></span></span><span style="display:flex;"><span>		<span style="color:#75715e">// in preparation for the next iteration.
</span></span></span><span style="display:flex;"><span>		pipeline.consumer_release();
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Drain the pipeline.
</span></span></span><span style="display:flex;"><span>	<span style="color:#75715e">// This is to compute the last iteration.
</span></span></span><span style="display:flex;"><span>	pipeline.consumer_wait();
</span></span><span style="display:flex;"><span>	compute(shared[(subset_count <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">%</span> <span style="color:#ae81ff">2</span>]);
</span></span><span style="display:flex;"><span>	pipeline.consumer_release();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="notes-and-guidelines-to-follow">Notes and guidelines to follow</h2>
<p>The above makes clear that the pipeline mechanism suffers from 1 obvious downside:</p>
<blockquote>
<p>Pipelining requires $n$ times the memory for $n$ stages.</p>
</blockquote>
<p>This isn&rsquo;t unique to this particular mechanism; all pipeline concepts usually trade extra memory for more throughput. Note that the pipelining does not necessarily have to be done via shared memory - registers (stack variables or arrays) can also be used, but these assume that the lengths required are compile-time constants as well.</p>
<p>As always, premature optimization is evil and should be avoided. You can and should use Nsight Compute to check whether latency hiding within the kernel will be beneficial (and the degree to which it will be beneficial).</p>
<blockquote>
<p>Don&rsquo;t do this at the beginning, but rather at the end when the kernel is already <em>&lsquo;ready&rsquo;</em>, because the pipeline mechanism can make things messy if the computations are not organised in a simple manner.</p>
</blockquote>
<p>As a side-note, <code>thrust</code> and <code>cub</code> libraries are actually built to do this internally for the templates where it can. This is why if your operation can be pipelined and you write your own custom CUDA kernel where you <em>don&rsquo;t perform pipelining</em>, you will often be <strong>slower</strong> than the equivalent <code>thrust</code> functor call.</p>
<blockquote>
<p>If you can use <code>thrust</code> (or <code>cub</code>) to express your computation, do so, because <code>thrust</code> will internally pipeline the kernel for you.</p>
</blockquote>
<h1 id="for-those-with-money-programmatic-dependent-launches-pdl-and-tensor-memory-accelerators-tma">For those with money: Programmatic-Dependent Launches (PDL) and Tensor Memory Accelerators (TMA)</h1>
<p>These are both features introduced in compute capability 9.0 and above i.e. Hopper cards like H100 and newer (yes, that&rsquo;s the previous generation since Blackwell just released, but I don&rsquo;t have access to one either). Since I&rsquo;m stuck in the Ampere days, I&rsquo;ll only touch on these 2 very briefly. The interested reader can simply search these 2 keywords for more information.</p>
<h2 id="pdl">PDL</h2>
<p>This allows you to overlap kernels <em>within a stream</em>. Essentially, what you do is to mark points inside consequent kernels where dependence is absent. A common example is pre-loading some constant data at the start of the kernel:</p>
<pre tabindex="0"><code>| --- Kernel 1 --- |
             | xxx | -- Rest of kernel 2 -- |
             ^
             |
             Kernel 2 can start here to read data that doesn&#39;t
             depend on kernel 1&#39;s output. Then it can wait on
             kernel 1 to complete before it does the rest.
</code></pre><h2 id="tma">TMA</h2>
<p>This is somewhat like a further hardware extension of the pipeline mechanism. There is now a separate unit called the TMA that performs the data copies from global memory. There are several pros to this:</p>
<ul>
<li>No/less registers used; since a separate unit is doing the reads, the threads don&rsquo;t need to read into registers first</li>
<li>Skips the entire cache hierarchy; goes straight from global to shared</li>
</ul>
<p>But there are also cons:</p>
<ul>
<li>The code (in my opinion) is a lot more complex</li>
<li>There are far stricter memory alignment requirements</li>
<li>No option to go to registers directly (maybe not necessarily a con, depending on use-case)</li>
</ul>
]]></content></item><item><title>AtomicMinFloat; overloading integer-only atomics for floating-point numbers in CUDA</title><link>https://icyveins7.github.io/posts/2025/03/atomicminfloat-overloading-integer-only-atomics-for-floating-point-numbers-in-cuda/</link><pubDate>Sun, 16 Mar 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/03/atomicminfloat-overloading-integer-only-atomics-for-floating-point-numbers-in-cuda/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Convincing you (and myself) that with some minor edits, we can still use atomicMin for floats in CUDA..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;CUDA has a set of atomic functions for &lt;em&gt;safely&lt;/em&gt; updating the same memory address with different threads. There&amp;rsquo;s a whole list of them in the programming guide &lt;a href="https://docs.nvidia.com/cuda/cuda-c-programming-guide/#atomic-functions"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, for &lt;code&gt;atomicMin&lt;/code&gt; (and also &lt;code&gt;atomicMax&lt;/code&gt;), there isn&amp;rsquo;t an overload that works with &lt;code&gt;float&lt;/code&gt;s (or &lt;code&gt;double&lt;/code&gt;s , but who uses those in CUDA anyway..). For this discussion we&amp;rsquo;ll just focus on &lt;code&gt;atomicMin&lt;/code&gt;, but all the points we discuss can be inverted to explain &lt;code&gt;atomicMax&lt;/code&gt;.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Convincing you (and myself) that with some minor edits, we can still use atomicMin for floats in CUDA..</p>
</blockquote>
<p>CUDA has a set of atomic functions for <em>safely</em> updating the same memory address with different threads. There&rsquo;s a whole list of them in the programming guide <a href="https://docs.nvidia.com/cuda/cuda-c-programming-guide/#atomic-functions">here</a>.</p>
<p>However, for <code>atomicMin</code> (and also <code>atomicMax</code>), there isn&rsquo;t an overload that works with <code>float</code>s (or <code>double</code>s , but who uses those in CUDA anyway..). For this discussion we&rsquo;ll just focus on <code>atomicMin</code>, but all the points we discuss can be inverted to explain <code>atomicMax</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">int</span> <span style="color:#a6e22e">atomicMin</span>(<span style="color:#66d9ef">int</span><span style="color:#f92672">*</span> address, <span style="color:#66d9ef">int</span> val);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">atomicMin</span>(<span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">int</span><span style="color:#f92672">*</span> address,
</span></span><span style="display:flex;"><span>                       <span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">int</span> val);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">long</span> <span style="color:#66d9ef">long</span> <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">atomicMin</span>(<span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">long</span> <span style="color:#66d9ef">long</span> <span style="color:#66d9ef">int</span><span style="color:#f92672">*</span> address,
</span></span><span style="display:flex;"><span>                                 <span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">long</span> <span style="color:#66d9ef">long</span> <span style="color:#66d9ef">int</span> val);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">long</span> <span style="color:#66d9ef">long</span> <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">atomicMin</span>(<span style="color:#66d9ef">long</span> <span style="color:#66d9ef">long</span> <span style="color:#66d9ef">int</span><span style="color:#f92672">*</span> address,
</span></span><span style="display:flex;"><span>                                <span style="color:#66d9ef">long</span> <span style="color:#66d9ef">long</span> <span style="color:#66d9ef">int</span> val);
</span></span></code></pre></div><p>As you can see, they all deal with integers. Okay, so how would you deal with floats? You could do some of the following:</p>
<ol>
<li>Rework the code to not use atomics. This probably involves an additional kernel to do comparisons and/or some additional scratch memory. I used to do this until cooperative groups came out and the compiler started optimising warp-aggregated atomics very well.</li>
<li>Use <code>atomicCAS</code> to define the atomic operation with some funky <code>do-while</code> loop. I found this to be very inelegant.</li>
<li>Use some scaling function to turn your floating point values into an integer, preserving the order i.e. $x > y \rightarrow f(x) > f(y)$. Then just use the existing integer-based <code>atomicMin</code> calls, which would benefit from hardware acceleration.</li>
</ol>
<p>The last method sounds tricky, but it doesn&rsquo;t really need to be.</p>
<h1 id="comparing-floats-is-almost-the-same-as-comparing-ints">Comparing <code>float</code>s is (almost) the same as comparing <code>int</code>s</h1>
<p>First off, I&rsquo;m not the one that thought of this. See the answer <a href="https://stackoverflow.com/a/72461459">here</a>.  I&rsquo;m going to explain why this works. To do this, we need to recap how the 32 bits in a <code>float</code>, <code>int</code> or <code>unsigned int</code> are laid out.</p>
<h2 id="ieee-754-integers-32-bit">IEEE-754 integers (32-bit)</h2>
<p>This should be obvious but we&rsquo;ll restate it for completeness.</p>
<p>Unsigned integers are simply a list of bits representing the powers of 2, where the leading bit is $2^{31}$ and the ending bit is $2^0 = 1$.</p>
<p>Signed integers are identical to unsigned integers, but with the leading bit used for the sign bit.</p>
<h2 id="ieee-754-single-precision-floating-point">IEEE-754 single-precision floating point</h2>
<p>Refer to this concise graphic from the <a href="https://en.wikipedia.org/wiki/Single-precision_floating-point_format">wiki page</a>:</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Float_example.svg/1180px-Float_example.svg.png" alt="single precision bit layout"></p>
<p>Here you can easily observe the following:</p>
<ul>
<li>First (leading, leftmost) bit is the sign bit</li>
<li>Next 8 bits define the exponent $2^{E - 127}$</li>
<li>Final 23 bits define the mantissa or fraction (with implicit offset), $1 + x$, with $0 \leq x < 1$</li>
</ul>
<h3 id="exponent-and-mantissa-sections-can-be-compared-like-unsigned-integers">Exponent and mantissa sections can be compared like unsigned integers</h3>
<p>The above is the definition, but the first important property here is that both the exponent and the mantissa sections are still <em>ordered</em>, in the sense that <em>leading bits represent larger numbers than trailing bits</em>.</p>
<p>This is important, because then we can treat each section like its own unsigned integer space i.e. if we treat the exponent as an 8-bit unsigned integer and compare it like an 8-bit unsigned integer for two numbers with all other bits equal, then the <code>float</code> with the <em>&rsquo;larger 8-bit unsigned integer exponent&rsquo;</em> is indeed the larger <code>float</code>! The same goes for the mantissa section&rsquo;s 23 bits.</p>
<p>This also holds for the combination of the 2 sections; to see why, simply consider whether the following is possible: given fraction $x$ and exponent $E$, can we write a number where $y_1 = (1+x_1) 2^{E_1} > (1+x_2) 2^{E_2} = y_2$ but one of the following occurs:</p>
<ol>
<li>$x_1 \leq x_2$</li>
<li>$E_2 \leq E_1$</li>
</ol>
<p>Again, this isn&rsquo;t particularly complicated when you realise you only need to consider the smallest trailing bit of the exponent. Flipping this final, rightmost exponent bit engineers a factor of 2 change to the entire number. <strong>Since the other 23 bits represents a number $1 \leq 1 + x < 2$, it is impossible for this number to double and create a number that overcomes this factor of 2</strong>.</p>
<p>Let&rsquo;s use the above number in the graphic as an example. The number is</p>
$$
2^{124-127} \times (1 + 2^{-2}) = 2^{-3} \times 1.25 = 0.15625
$$<p>
We can increment the exponent by 1 in the trailing bit to make this</p>
$$
2^{125-127} \times (1 + 2^{-2}) = 2^{-2} \times 1.25 = 0.3125
$$<p>which is double the original number, as discussed above. It should be obvious by now - since we&rsquo;ve already established that the mantissa number has an upper bound of 2 - but let&rsquo;s finish up the example for completeness. What&rsquo;s the maximum number we can make in the fraction (turning on all the bits)? Well this is just the glorious number</p>
$$
1 + \sum_{i=1}^{23} 2^{-i} = 1.99999988079071044921875
$$<p>Clearly, this number multiplied by $2^{-3}$ is still smaller by definition than 0.3125. Thus, it is possible to conclude that we can compare both mantissa and exponent sections as if it were one long 31-bit unsigned number when we are simply concerned with magnitude of numbers.</p>
<p>In other words, we could simply reinterpret the trailing 31 bits of a <code>float</code> as an unsigned integer and then call <code>atomicMin</code> for our purpose! But now we need to handle the leading sign bit..</p>
<h1 id="sign-bit-implies-a-flip-of-minmax-operations">Sign bit implies a flip of min/max operations</h1>
<p>Let&rsquo;s just look at a bunch of examples of floating point values after they are reinterpreted as unsigned or signed integers (shown in row-pairs). In each pair, the smaller number will be in bold.</p>
<blockquote>
<p>It is important to note that in our <code>atomicMin</code> case, the access pattern is not <em>symmetric</em>, in the sense that the <code>val</code> is usually on-chip/local, whereas <code>address</code> usually refers to some global memory which is way more expensive to access.</p>
<p>This means that we should consider our information to be limited to <code>val</code> alone - we <em>observe</em> what <code>val</code> is and then we make a decision - and we will consider both the (negative <code>val</code>, positive <code>address</code>) and (positive <code>val</code>, negative <code>address</code>) cases separately.</p>
</blockquote>
<table>
	<thead>
			<tr>
					<th></th>
					<th>floating point value</th>
					<th>reinterpreted unsigned int value</th>
					<th>reinterpreted signed int value</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>val_1</code></td>
					<td><strong>0.1f</strong></td>
					<td><strong>1036831949</strong></td>
					<td><strong>1036831949</strong></td>
			</tr>
			<tr>
					<td><code>addr_1</code></td>
					<td>0.2f</td>
					<td>1045220557</td>
					<td>1045220557</td>
			</tr>
			<tr>
					<td>&hellip;</td>
					<td>&hellip;</td>
					<td>&hellip;</td>
					<td>&hellip;</td>
			</tr>
			<tr>
					<td><code>val_2</code></td>
					<td>0.2f</td>
					<td><strong>1045220557</strong></td>
					<td>1045220557</td>
			</tr>
			<tr>
					<td><code>addr_2</code></td>
					<td><strong>-0.2f</strong></td>
					<td>3192704205</td>
					<td><strong>-1102263091</strong></td>
			</tr>
			<tr>
					<td>&hellip;</td>
					<td>&hellip;</td>
					<td>&hellip;</td>
					<td>&hellip;</td>
			</tr>
			<tr>
					<td><code>val_3</code></td>
					<td><strong>-0.2f</strong></td>
					<td>3192704205</td>
					<td><strong>-1102263091</strong></td>
			</tr>
			<tr>
					<td><code>addr_3</code></td>
					<td>0.2f</td>
					<td><strong>1045220557</strong></td>
					<td>1045220557</td>
			</tr>
			<tr>
					<td>&hellip;</td>
					<td>&hellip;</td>
					<td>&hellip;</td>
					<td>&hellip;</td>
			</tr>
			<tr>
					<td><code>val_4</code></td>
					<td><strong>-0.2f</strong></td>
					<td>3192704205</td>
					<td>-1102263091</td>
			</tr>
			<tr>
					<td><code>addr_4</code></td>
					<td>-0.1f</td>
					<td><strong>3184315597</strong></td>
					<td><strong>-1110651699</strong></td>
			</tr>
	</tbody>
</table>
<p>In the first 2 cases - where <code>val</code> is positive - we see that reinterpreting as a signed integer and then taking the <em>minimum</em> matches our floating point minimum i.e. here we take the <code>atomicMin</code>.</p>
<p>In the next 2 cases - where <code>val</code> is negative - the above is inconsistent, and we should instead reinterpret as unsigned integers, where the floating point minimum now corresponds to the unsigned <em>maximum</em> i.e. here we take the <code>atomicMax</code> of the integers, even though we were looking for the <code>atomicMin</code> of the floats!</p>
<p>Remember, in all our cases above, we are making the decision for the reinterpreted type <em>based solely on the sign of <code>val</code></em>.</p>
<h2 id="why-this-happens">Why this happens</h2>
<p>This should be apparent when considering unsigned versus signed integers. For signed integers, we compare the trailing 31 bits (magnitude) in a way identical to unsigned integers, but if the sign bit is on (negative number) then a smaller magnitude implies a larger number, and vice versa.</p>
<p>When we first observe the sign of <code>val</code>, we don&rsquo;t know what the sign of the other operand at <code>address</code> is. But we can infer the following:</p>
<ol>
<li>If <code>val</code> is positive and
<ol>
<li><code>address</code> is positive then taking the minimum of the signed or unsigned reinterpretation makes no difference. <em>Taking the maximum in either reinterpretation gives the wrong answer</em>.</li>
<li><code>address</code> is negative then we must take either the minimum of the signed reinterpretation or the maximum of the unsigned interpretation.</li>
<li><strong>The consistent logic is thus taking the minimum of the signed int reinterpretation</strong>.</li>
</ol>
</li>
<li>If <code>val</code> is negative and
<ol>
<li><code>address</code> is positive then we must take either the minimum of the signed reinterpretation or the maximum of the unsigned interpretation.</li>
<li><code>address</code> is negative then taking the maximum of the signed or unsigned reinterpretation makes no difference. <em>Taking the minimum in either reinterpretation gives the wrong answer, since we need to look for the bigger magnitude in order to be more negative</em>.</li>
<li><strong>The consistent logic is thus taking the maximum of the unsigned int reinterpretation</strong>.</li>
</ol>
</li>
</ol>
<h2 id="a-final-note-on-signed-zeroes">A final note on signed zeroes</h2>
<p>In the StackOverflow link, there is an older answer that used a simple <code>val &gt;= 0</code> comparison to determine which logic branch to use. This works, except in some cases where <code>val == -0.0f</code>.</p>
<p>For example, if <code>val == -0.0f</code> and <code>*address == -1.0f</code>, then since <code>val &gt;= 0</code> evaluates to <code>true</code>, one might attempt to use the minimum of the signed int reinterpretation as discussed above, evaluating <code>val</code> as <code>-2147483648</code>. This would, however, mark <code>-0.0f</code> as the minimum erroneously since <code>-1.0f</code> is <code>-1082130432</code>; indeed, since <code>val</code> is &rsquo;negative&rsquo; we should have used the maximum of the unsigned int reinterpretation.</p>
<p>This is entirely due to zero being a special number in the exponent section (<code>0.0f</code> is just <code>0x00000000</code> and <code>-0.0f</code> is just <code>0x80000000</code>). The correction, as stated in the link, is to just read the bit directly, either via custom bit twiddling or CUDA&rsquo;s <code>signbit()</code>.</p>
]]></content></item><item><title>Gotta go (randomly) fast, thrust vs cuRAND</title><link>https://icyveins7.github.io/posts/2025/03/gotta-go-randomly-fast-thrust-vs-curand/</link><pubDate>Mon, 10 Mar 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/03/gotta-go-randomly-fast-thrust-vs-curand/</guid><description>&lt;blockquote&gt;
&lt;p&gt;How fast can you generate (pseudo-)random numbers on the GPU?
How fast can you generate (pseudo-)random numbers on the GPU?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I recently had to answer this question. Well, not exactly this question, but this was a key part of it.&lt;/p&gt;
&lt;p&gt;A quick google search will bring up two common methods (libraries) when trying to do this in CUDA: thrust and cuRAND. Thrust is known to be a lot easier to set up; no need to write the kernel code and nitty gritty details, so I started with that.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>How fast can you generate (pseudo-)random numbers on the GPU?
How fast can you generate (pseudo-)random numbers on the GPU?</p>
</blockquote>
<p>I recently had to answer this question. Well, not exactly this question, but this was a key part of it.</p>
<p>A quick google search will bring up two common methods (libraries) when trying to do this in CUDA: thrust and cuRAND. Thrust is known to be a lot easier to set up; no need to write the kernel code and nitty gritty details, so I started with that.</p>
<p>Before I begin though, <a href="https://github.com/icyveins7/gpu_benchmarks">here&rsquo;s a link to my growing GPU-related codebase</a>. Some of the following snippets come from there.</p>
<h1 id="the-thrust-way">The <code>thrust</code> way</h1>
<p>Generating random numbers in <code>thrust</code> is somewhat similar to how you would do so in <code>std</code> C++.</p>
<ol>
<li>Initialize an RNG engine.</li>
<li>Initialize a distribution, like <code>uniform_real_distribution</code>.</li>
<li>Call the distribution on the engine via <code>operator()</code> to generate a random number.</li>
</ol>
<p>This is usually done, as seen in the examples, via a custom <code>struct</code> with an overloaded <code>operator()</code>, which is passed to a <code>thrust::generate</code> call, like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// The following is adapted from
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// cuda-samples/Samples/3_CUDA_Features/cdpQuadtree/cdpQuadtree.cu
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Engine <span style="color:#f92672">=</span> thrust<span style="color:#f92672">::</span>random<span style="color:#f92672">::</span>default_random_engine<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Random_generator2d</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">int</span> count;
</span></span><span style="display:flex;"><span>  __host__ __device__ <span style="color:#a6e22e">Random_generator2d</span>() <span style="color:#f92672">:</span> count(<span style="color:#ae81ff">0</span>) {}
</span></span><span style="display:flex;"><span>  __host__ __device__ <span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">hash</span>(<span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">int</span> a) {
</span></span><span style="display:flex;"><span>    a <span style="color:#f92672">=</span> (a <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x7ed55d16</span>) <span style="color:#f92672">+</span> (a <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">12</span>);
</span></span><span style="display:flex;"><span>    a <span style="color:#f92672">=</span> (a <span style="color:#f92672">^</span> <span style="color:#ae81ff">0xc761c23c</span>) <span style="color:#f92672">^</span> (a <span style="color:#f92672">&gt;&gt;</span> <span style="color:#ae81ff">19</span>);
</span></span><span style="display:flex;"><span>    a <span style="color:#f92672">=</span> (a <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x165667b1</span>) <span style="color:#f92672">+</span> (a <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">5</span>);
</span></span><span style="display:flex;"><span>    a <span style="color:#f92672">=</span> (a <span style="color:#f92672">+</span> <span style="color:#ae81ff">0xd3a2646c</span>) <span style="color:#f92672">^</span> (a <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">9</span>);
</span></span><span style="display:flex;"><span>    a <span style="color:#f92672">=</span> (a <span style="color:#f92672">+</span> <span style="color:#ae81ff">0xfd7046c5</span>) <span style="color:#f92672">+</span> (a <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">3</span>);
</span></span><span style="display:flex;"><span>    a <span style="color:#f92672">=</span> (a <span style="color:#f92672">^</span> <span style="color:#ae81ff">0xb55a4f09</span>) <span style="color:#f92672">^</span> (a <span style="color:#f92672">&gt;&gt;</span> <span style="color:#ae81ff">16</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> a;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  __host__ __device__ __forceinline__ thrust<span style="color:#f92672">::</span>tuple<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">float</span>, <span style="color:#66d9ef">float</span><span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">operator</span>()() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">unsigned</span> seed <span style="color:#f92672">=</span> hash(blockIdx.x <span style="color:#f92672">*</span> blockDim.x <span style="color:#f92672">+</span> threadIdx.x <span style="color:#f92672">+</span> count);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// thrust::generate may call operator() more than once per thread.
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Hence, increment count by grid size to ensure uniqueness of seed
</span></span></span><span style="display:flex;"><span>    count <span style="color:#f92672">+=</span> blockDim.x <span style="color:#f92672">*</span> gridDim.x;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    Engine <span style="color:#a6e22e">rng</span>(seed);
</span></span><span style="display:flex;"><span>    thrust<span style="color:#f92672">::</span>random<span style="color:#f92672">::</span>uniform_real_distribution<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">float</span><span style="color:#f92672">&gt;</span> distrib;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> thrust<span style="color:#f92672">::</span>make_tuple(distrib(rng), distrib(rng));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Random_generator2d<span style="color:#f92672">&lt;</span>thrust<span style="color:#f92672">::</span>random<span style="color:#f92672">::</span>default_random_engine<span style="color:#f92672">&gt;</span> rnd;
</span></span><span style="display:flex;"><span>thrust<span style="color:#f92672">::</span>generate(
</span></span><span style="display:flex;"><span>    thrust<span style="color:#f92672">::</span>make_zip_iterator(
</span></span><span style="display:flex;"><span>        thrust<span style="color:#f92672">::</span>make_tuple(d_x.begin(), d_y.begin())),
</span></span><span style="display:flex;"><span>    thrust<span style="color:#f92672">::</span>make_zip_iterator(thrust<span style="color:#f92672">::</span>make_tuple(d_x.end(), d_y.end())),
</span></span><span style="display:flex;"><span>    rnd);
</span></span><span style="display:flex;"><span>    
</span></span></code></pre></div><p>In the above example, the generator functor has been modified to output 2 random values per call. There is also a custom hash function in the sample.</p>
<p>This is all well and good, but I had several issues with this:</p>
<ul>
<li><code>thrust</code> documentation is nearly non-existent. I had to search for stack overflow articles and look at the samples directly to really figure out what to do.</li>
<li>Because of the above, I wasn’t really sure whether I could remove the engine instantiation from within the functor <code>operator()</code>. Not being able to do so would imply that every time the functor is called, it would restart from the beginning i.e you wouldn’t be able to use this in a loop, since the 2nd call would generate the same numbers as the 1st call. <em>It could probably be done - by returning the engine and taking it in as an input? - but there was no reason to experiment since cuRAND already had this well-documented</em>.</li>
<li>Like all other <code>thrust</code> things, there was no way to finely control the grid and block dimensions.</li>
<li>The <code>thrust::generate</code> and functor way of doing things, while simple if trivially generating straightforward data structures, started looking uglier - in my opinion - when more complicated indexing was required.</li>
</ul>
<h1 id="this-curand-is-the-way">This (cuRAND) is the way</h1>
<p>The NVIDIA mandalorians have declared it so.</p>
<p>Seriously though, it seems pretty obvious that NVIDIA <em>wants</em> you to use cuRAND for HPC implementations. <a href="https://developer.nvidia.com/curand">And the performance looks pretty sick to be honest.</a></p>
<p>The library by design forces you to initialise a bunch of RNG states with <code>curand_init</code>. Each of these states are used in function calls like <code>curand_uniform</code> to generate random numbers, after which the states are updated so that the next call will generate the next random number in the sequence.</p>
<p>The beauty of this is that there is a lot of choice for how to use the states:</p>
<ol>
<li>The most basic way: use one state per thread, per element. You&rsquo;d save the updated states back into global memory.</li>
<li>A bit more complex: use one state per thread, per N elements. You&rsquo;d load the states from global memory, generate N elements, then write back to global memory at the end.</li>
</ol>
<p>You also get to tweak the index to start at in the subsequence, just from either the <code>curand_init</code> arguments (subsequence is usually the thread index, offset is what you&rsquo;re looking for), or via a <code>skipahead</code> function call (which would be the same as if you had init-ed at that offset, but you don&rsquo;t want to init again).</p>
<p>Since all of these calls are done using the device API - inside a normal kernel function - you get to play around with all the standard block/grid choices you would with a kernel as well; this is unlike in <code>thrust</code> functor calls where the grid and block dimensions are chosen for you.</p>
<p>I ended up writing a simple class to wrap these operations, which could be easily extended for other use-cases, either by explicit inheritance or just a simple rewrite:</p>
<ol>
<li>Class constructor initialises the number of states; how the states are used is internal to the implementation, but what is important is that it is constant i.e. used in the same way for every invocation later. This essentially allocates device memory and performs the <code>curand_init</code> calls appropriately.</li>
<li>Simple public methods to call the kernel that does the RNG (and any other pre/post-computations). Again, this is implicitly understood to be constant every call (the shape of the data, and the grid/block dimensions operating on it are the same and part of the design for that use-case).</li>
<li>Skipahead function calls are also defined, similar to the previous point.</li>
<li>Destructor doesn&rsquo;t need to be defined since RAII <code>thrust</code> vectors are used to store the states.</li>
</ol>
<p>You can take a look at some of the test implementations I made <a href="https://github.com/icyveins7/gpu_benchmarks/blob/master/proj_rng/curand_extensions.h">here</a> and <a href="https://github.com/icyveins7/gpu_benchmarks/blob/master/proj_rng/curandrng.cu">here</a>. I originally wanted to try out <code>curand</code>&rsquo;s float4 generators that <a href="https://docs.nvidia.com/cuda/curand/group__DEVICE.html#group__DEVICE_1ge214aeb22edf5f523258487f6a8ae78b">only work with Philox</a>, but I didn&rsquo;t bother because this part didn&rsquo;t end up being a bottleneck in my final code.</p>
]]></content></item><item><title>When circumstances don’t allow you to use unique_ptr</title><link>https://icyveins7.github.io/posts/2025/02/when-circumstances-dont-allow-you-to-use-unique_ptr/</link><pubDate>Sun, 23 Feb 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/02/when-circumstances-dont-allow-you-to-use-unique_ptr/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Some background on my single header file memory manager and why I made it..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Picture this. You are working on a codebase with some &lt;del&gt;lazy&lt;/del&gt; &lt;del&gt;inept&lt;/del&gt; questionable decisions from external forces. You are given a class to work on. For the purposes of this post let’s call it &lt;code&gt;MyClass&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The class will have its methods invoked in the following order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;MyClass()&lt;/code&gt; i.e. the constructor.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setup(…)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run(…)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;teardown()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Repeat 2-4 many times.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~MyClass()&lt;/code&gt; i.e. the destructor.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This class will be reused in multiple different scenarios repeatedly, with different input data.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Some background on my single header file memory manager and why I made it..</p>
</blockquote>
<p>Picture this. You are working on a codebase with some <del>lazy</del> <del>inept</del> questionable decisions from external forces. You are given a class to work on. For the purposes of this post let’s call it <code>MyClass</code>.</p>
<p>The class will have its methods invoked in the following order:</p>
<ol>
<li><code>MyClass()</code> i.e. the constructor.</li>
<li><code>setup(…)</code></li>
<li><code>run(…)</code></li>
<li><code>teardown()</code></li>
<li>Repeat 2-4 many times.</li>
<li><code>~MyClass()</code> i.e. the destructor.</li>
</ol>
<p>This class will be reused in multiple different scenarios repeatedly, with different input data.</p>
<p>Here’s the problem: you also need to allocate different things/sizes of arrays depending on the data. Hence, there is effectively no way to pre-allocate this in the constructor. You will have to do this in <code>setup()</code>, and then destroy or free these in <code>teardown()</code>.</p>
<h1 id="over-dramatisation">(Over-)dramatisation</h1>
<p>Them: You must pre-allocate your data in <code>setup</code> and remember to free it in <code>teardown</code>.</p>
<p>Me: Sure, but I’d like to use a <code>unique_ptr</code> or similar smart pointers to do it so that my colleagues and I don’t have to spend extra time searching for memory leak bugs. I can’t do this if we don’t call the destructor for automatic cleanups.</p>
<p>Them: We cannot keep destroying and reinstantiating the objects. It will be too much overhead.</p>
<p>Me: But we are allocating <em>gigabytes</em> of data anyway, the constructor overhead is going to be negligible in the midst of all this.</p>
<p>Them: …</p>
<p>Me: ??? (Your brain is the overhead)</p>
<h1 id="a-compromise">A compromise</h1>
<p>With no other solution, I decided to write my own. Well, I had an idea of a memory manager, and then technically I prompted ChatGPT to write one for me.</p>
<p>Specifically, I needed to make sure it would handle the following:</p>
<ol>
<li>Ability to handle arbitrary POD type heap allocations. Stuff like <code>new arr[size]</code> would need to be replaceable.</li>
<li>Ability to handle custom class constructors. Stuff like <code>new SomeClass(arg1, arg2)</code> would need to be replaceable.</li>
</ol>
<p>A few prompts, and some manual edits later, <a href="https://github.com/icyveins7/memory-manager">I had what I wanted</a>.</p>
<p>The premise is pretty simple: have a memory block structure and hide your allocations inside them.</p>
<p>Hold them all in an <code>std::vector</code>, and then <code>.clear()</code> it when finished with them.</p>
<p>All the references disappear when the vector is cleared, and each memory block will call its own functor to free the internal memory appropriately i.e. <code>delete</code> or <code>delete[]</code>.</p>
<h1 id="why-not-just-hold-a-vector-of-memoryblocks">Why not just hold a vector of <code>MemoryBlock</code>s?</h1>
<p>Great question. ChatGPT reminded me; I’ll paraphrase it here with the following important question:</p>
<blockquote>
<p>What happens when the vector is resized?</p>
</blockquote>
<p>If we had taken the code I linked above and simply changed <code>vector&lt;unique_ptr&lt;MemoryBlock&gt;&gt;</code> to <code>vector&lt;MemoryBlock&gt;</code>, the resize would have done the following:</p>
<ol>
<li>Copied the MemoryBlocks over. This is just a copy of each pointer we allocated. This is fine.</li>
<li>Freed the existing memory holding the original memory blocks. This would call each memory block’s destructor, which would call each functor, which would free the internally kept pointer. <em>This is not fine.</em></li>
<li>The pointers we copied over now point to nothing.</li>
<li>Freeing the new resized vector would result in double frees (or another resize would also do this).</li>
</ol>
<p>The typical solution is to redesign the <code>MemoryBlock</code> so that the copy constructors, assignment operators etc are well-defined. The rule of 5 strikes again.</p>
<p>In this case, we really just want to make sure that the internal pointer (which implies the <code>MemoryBlock</code> itself) is move-only, so that when the vector resizes it will not delete the internal pointers at any point. But this is exactly what using <code>unique_ptr</code> achieves! Hence, no need to reinvent the wheel.</p>
<h1 id="jokes-on-me">Jokes on me..</h1>
<p>In the end, they still didn’t want to implement this. ‘Unnecessary code changes’..</p>
]]></content></item><item><title>Cross-platform trigonometric SIMD and how the C ABI confused me</title><link>https://icyveins7.github.io/posts/2025/01/cross-platform-trigonometric-simd-and-how-the-c-abi-confused-me/</link><pubDate>Thu, 30 Jan 2025 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2025/01/cross-platform-trigonometric-simd-and-how-the-c-abi-confused-me/</guid><description>&lt;blockquote&gt;
&lt;p&gt;My small journey in discovering libmvec functions and the C problems I encountered..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I was recently working on making an existing Windows-only library of SIMD functions cross-platform i.e. making everything work in Linux. Here I&amp;rsquo;m going to highlight some problems I encountered in the process; hopefully it will help someone if they encounter something similar too.&lt;/p&gt;
&lt;h1 id="the-windows-msvc-function"&gt;The Windows (MSVC) function&lt;/h1&gt;
&lt;p&gt;Intrinsics are usually tied to compiler implementations. The one that gave me issues was a bunch of trigonometric functions. Let&amp;rsquo;s use the SSE version that works on floats; in MSVC this was implemented as a simple&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>My small journey in discovering libmvec functions and the C problems I encountered..</p>
</blockquote>
<p>I was recently working on making an existing Windows-only library of SIMD functions cross-platform i.e. making everything work in Linux. Here I&rsquo;m going to highlight some problems I encountered in the process; hopefully it will help someone if they encounter something similar too.</p>
<h1 id="the-windows-msvc-function">The Windows (MSVC) function</h1>
<p>Intrinsics are usually tied to compiler implementations. The one that gave me issues was a bunch of trigonometric functions. Let&rsquo;s use the SSE version that works on floats; in MSVC this was implemented as a simple</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">__m128</span> _mm_cos_ps(<span style="color:#66d9ef">__m128</span> a)
</span></span></code></pre></div><p>This <a href="https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_cos_ps&amp;ig_expand=1388">function</a>, and others like it, simply do not exist in GCC implementations, at least not explicitly. As such, there is no way to direct a call an SIMD vector to a <em>simple</em> <code>cos()</code> function for evaluation. Why I highlighted <em>simple</em> will become clear in a minute.</p>
<h1 id="how-gcc-handles-it">How GCC handles it</h1>
<p>GCC does, of course, have a way to vectorise these operations through its <code>-ffast-math</code> flag. Using this and a standard <code>-O3</code>, we can see that a simple function like</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">manyCos</span>(<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">float</span> <span style="color:#f92672">*</span>x, <span style="color:#66d9ef">float</span><span style="color:#f92672">*</span> y)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; i <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">4000</span>; <span style="color:#f92672">++</span>i)
</span></span><span style="display:flex;"><span>		y[i] <span style="color:#f92672">=</span> cosf(x[i]);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>gets turned into assembly that looks like</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-asm" data-lang="asm"><span style="display:flex;"><span><span style="color:#a6e22e">lea</span> <span style="color:#66d9ef">rdx</span>, [<span style="color:#66d9ef">rdi</span><span style="color:#960050;background-color:#1e0010">+</span><span style="color:#ae81ff">4</span>]
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">mov</span> <span style="color:#66d9ef">rax</span>, <span style="color:#66d9ef">rsi</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">push</span> <span style="color:#66d9ef">r12</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">mov</span> <span style="color:#66d9ef">r12</span>, <span style="color:#66d9ef">rsi</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">sub</span> <span style="color:#66d9ef">rax</span>, <span style="color:#66d9ef">rdx</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">push</span> <span style="color:#66d9ef">rbp</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">mov</span> <span style="color:#66d9ef">rbp</span>, <span style="color:#66d9ef">rdi</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">push</span> <span style="color:#66d9ef">rbx</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">xor</span> <span style="color:#66d9ef">ebx</span>, <span style="color:#66d9ef">ebx</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">cmp</span> <span style="color:#66d9ef">rax</span>, <span style="color:#ae81ff">8</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">jbe</span> <span style="color:#66d9ef">.L2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.L3:
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">movups</span> <span style="color:#66d9ef">xmm0</span>, <span style="color:#66d9ef">XMMWORD</span> <span style="color:#66d9ef">PTR</span> [<span style="color:#66d9ef">rbp</span><span style="color:#960050;background-color:#1e0010">+</span><span style="color:#ae81ff">0</span><span style="color:#960050;background-color:#1e0010">+</span><span style="color:#66d9ef">rbx</span>]
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">call</span> <span style="color:#66d9ef">_ZGVbN4v_cosf</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">movups</span> <span style="color:#66d9ef">XMMWORD</span> <span style="color:#66d9ef">PTR</span> [<span style="color:#66d9ef">r12</span><span style="color:#960050;background-color:#1e0010">+</span><span style="color:#66d9ef">rbx</span>], <span style="color:#66d9ef">xmm0</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">add</span> <span style="color:#66d9ef">rbx</span>, <span style="color:#ae81ff">16</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">cmp</span> <span style="color:#66d9ef">rbx</span>, <span style="color:#ae81ff">16000</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">jne</span> <span style="color:#66d9ef">.L3</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.L1:
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">pop</span> <span style="color:#66d9ef">rbx</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">pop</span> <span style="color:#66d9ef">rbp</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">pop</span> <span style="color:#66d9ef">r12</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ret</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.L2:
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">movss</span> <span style="color:#66d9ef">xmm0</span>, <span style="color:#66d9ef">DWORD</span> <span style="color:#66d9ef">PTR</span> [<span style="color:#66d9ef">rbp</span><span style="color:#960050;background-color:#1e0010">+</span><span style="color:#ae81ff">0</span><span style="color:#960050;background-color:#1e0010">+</span><span style="color:#66d9ef">rbx</span>]
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">call</span> <span style="color:#66d9ef">cosf</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">movss</span> <span style="color:#66d9ef">DWORD</span> <span style="color:#66d9ef">PTR</span> [<span style="color:#66d9ef">r12</span><span style="color:#960050;background-color:#1e0010">+</span><span style="color:#66d9ef">rbx</span>], <span style="color:#66d9ef">xmm0</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">add</span> <span style="color:#66d9ef">rbx</span>, <span style="color:#ae81ff">4</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">cmp</span> <span style="color:#66d9ef">rbx</span>, <span style="color:#ae81ff">16000</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">jne</span> <span style="color:#66d9ef">.L2</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">jmp</span> <span style="color:#66d9ef">.L1</span>
</span></span></code></pre></div><p>Now we can see this calls a particular intrinsic that looks like</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-asm" data-lang="asm"><span style="display:flex;"><span><span style="color:#a6e22e">_ZGVbN4v_cosf</span>
</span></span></code></pre></div><p>This is a special routine that comes from <code>libmvec</code>, which is <a href="https://sourceware.org/glibc/wiki/libmvec">a vector library that comes coupled inside glibc</a>. GCC automatically uses this library when it is asked to vectorise more involved mathematical functions like <code>cosf</code>, <code>sinf</code>, <code>logf</code> etc (and their double variants of course).</p>
<h1 id="but-how-do-we-manually-call-libmvec-functions-then">But how do we <em>manually</em> call <code>libmvec</code> functions then?</h1>
<p>The link above should make it clear that there is no intended use-case to manually call stuff in <code>libmvec</code>. Indeed, the vector ABI it uses also doesn’t correspond to the typical C++ name-mangling of symbol names.</p>
<p>Googling it doesn’t really come up with much information either. I have found that the best way in my opinion is to simply disassemble code compiled with <code>-ffast-math</code>, like above, and look for the function called. I can easily do this in <a href="godbolt.org">godbolt.org</a> so that’s usually where I do it (and it doesn’t seem like the function name has/will change in the foreseeable future).</p>
<h1 id="okay-we-have-the-function-name-so-how-do-we-call-it">Okay, we have the function name, so how do we call it?</h1>
<p>The first thing to do is to include the header. This is simply in <code>math.h</code>, which is where you would find the normal math functions like <code>cosf()</code> anyway.</p>
<p>Now the next thing that is required is to make it <code>extern &quot;C&quot;</code>, because the library is written as a C library. <strong>If you don&rsquo;t do this and you&rsquo;re compiling with <code>g++</code>, then C++ mangling is going to mangle the name again and it will result in undefined references.</strong></p>
<p>Alright, but what about the input argument types? It&rsquo;s meant to be used on <code>gcc</code>&rsquo;s in-built vector type definitions like <code>__m128</code>, so that&rsquo;s what we will use. But here is where my problem began.</p>
<h1 id="type-qualifiers-reference">Type qualifiers? Reference?</h1>
<p>In a typical C++ function nowadays, it is usually recommended that input functions that are not modified in the function be declared as <code>const</code>, and that where size may matter, references (or pointers) be used. As such, my declaration looked like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">extern</span> <span style="color:#e6db74">&#34;C&#34;</span> <span style="color:#66d9ef">__m128</span> _ZGVbN4v_cosf(<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">__m128</span><span style="color:#f92672">&amp;</span>);
</span></span></code></pre></div><p>As it turns out, using a reference via <code>&amp;</code> is completely incorrect, and very dangerous; this will compile without error, but will then produce nonsensical output at runtime. <em>The <code>const</code> is technically harmless, but is also irrelevant to this discussion anyway (since we are not the ones writing the function implementation)</em>.</p>
<p>The correct declaration is simply</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">extern</span> <span style="color:#e6db74">&#34;C&#34;</span> <span style="color:#66d9ef">__m128</span> _ZGVbN4v_cosf(<span style="color:#66d9ef">__m128</span>);
</span></span></code></pre></div><p>where the <code>const</code> is irrelevant.</p>
<h1 id="the-c-abi-and-the-compiler-cannot-know-your-mistake">The C ABI (and the compiler) cannot know your mistake</h1>
<p>I&rsquo;ll leave a <a href="https://stackoverflow.com/questions/79364070/output-errors-when-using-libmvec-intrinsics-for-trigo-functions-manually-like-c">link here to my question in StackOverflow, with the fantastic answer by the assembly master Peter Cordes</a>. I&rsquo;ll try to summarise his answer here.</p>
<p>Effectively, telling the compiler that a reference is input is the same as telling it to load a pointer address into the input registers of the function. Doing so, and then <em>wrongly</em> calling the function with the C++-like syntax for references (where you just write the variable name), would result in the compiler taking the variable&rsquo;s value as the address.</p>
<p>I also asked this question to the experts in the Compiler Explorer discord, and @dragonmux was kind enough to respond. The explanation given to me there helped to clarify some things.</p>
<p>I first asked why the compiler couldn&rsquo;t help to catch it as I was still in my C++ mindset. For example, having two files with</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// # a.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">extern</span> <span style="color:#66d9ef">float</span> <span style="color:#a6e22e">func</span>(<span style="color:#66d9ef">float</span> x){
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> x<span style="color:#f92672">*</span><span style="color:#ae81ff">2.0f</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">0.1f</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// # b.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Forward declaration with &#39;wrong&#39; input qualifier (added &amp;)
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">extern</span> <span style="color:#66d9ef">float</span> <span style="color:#a6e22e">func</span>(<span style="color:#66d9ef">float</span><span style="color:#f92672">&amp;</span> x);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// create a variable and explicit ref
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">float</span> x <span style="color:#f92672">=</span> <span style="color:#ae81ff">2.5f</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">float</span><span style="color:#f92672">&amp;</span> xref <span style="color:#f92672">=</span> x;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// pass explicit ref to func
</span></span></span><span style="display:flex;"><span>func(xref);
</span></span></code></pre></div><p>will fail to compile as <code>g++</code> will correctly identify that the reference is undefined (because it has retained the type qualifier information in the name-mangling).</p>
<p>If I instead now qualify everything with <code>extern &quot;C&quot;</code> instead,</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// # a.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">extern</span> <span style="color:#e6db74">&#34;C&#34;</span> <span style="color:#66d9ef">float</span> func(<span style="color:#66d9ef">float</span> x){
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> x<span style="color:#f92672">*</span><span style="color:#ae81ff">2.0f</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">0.1f</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// # b.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Forward declaration with &#39;wrong&#39; input qualifier (added &amp;)
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">extern</span> <span style="color:#e6db74">&#34;C&#34;</span> <span style="color:#66d9ef">float</span> func(<span style="color:#66d9ef">float</span><span style="color:#f92672">&amp;</span> x);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// create a variable and explicit ref
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">float</span> x <span style="color:#f92672">=</span> <span style="color:#ae81ff">2.5f</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">float</span><span style="color:#f92672">&amp;</span> xref <span style="color:#f92672">=</span> x;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// pass explicit ref to func
</span></span></span><span style="display:flex;"><span>func(xref);
</span></span></code></pre></div><p>this now <em>fully compiles</em>, since the C ABI discards the input types, so the symbols are identical in both translation units. But then it <strong>WILL</strong> crash (or in my contrived experiment it just outputs the wrong value <code>0.1f</code>), since we are effectively passing a pointer to something that expects a value.</p>
<p>Somehow, even though I was aware of name-mangling in C++, I didn&rsquo;t stop to think how it was protecting me from things like this.</p>
<p>Essentially, in the C++, non-<code>extern &quot;C&quot;</code> case, we have</p>
<ol>
<li>A symbol for <code>func</code> in <code>a.cpp</code>, name-mangled to indicate the pure value argument.</li>
<li>A symbol for <code>func</code> in <code>b.cpp</code>, name-mangled to indicate the reference argument.</li>
<li>Linker attempts to find definition for symbol used in (2) from (1) and fails.</li>
</ol>
<p>In the C version, everything links, because both symbols just look like <code>func</code>. Then at runtime, undefined behaviour results.</p>
<h1 id="final-question-is-it-badslow-that-the-library-is-using-simd-register-values-instead-of-references">Final question: is it bad/slow that the library is using SIMD register values instead of references?</h1>
<p>In my same StackOverflow question above, Peter Cordes explained the nuances behind this pretty well, so go back to his answer directly if you want.</p>
<p>TL; DR, it&rsquo;s important to note that it&rsquo;s</p>
<blockquote>
<p>cheap to pass/return by value in a single register.</p>
</blockquote>
<p>Think of it just like any other POD like an <code>int</code>.</p>
<p>There are other details on how the vector registers are handled in cases like this, where the SVML (which includes things like the trigonometric functions for SIMD) aren&rsquo;t really hardware intrinsics, so they have to respect the</p>
<blockquote>
<p>x86-64 System V calling convention: there are no call-preserved vector registers.</p>
</blockquote>
<p>and that means that</p>
<blockquote>
<p>only the return-value register can be relied on to have a useful value</p>
</blockquote>
<p>but I think that&rsquo;s not really necessary to understand deeply from a library maintainer&rsquo;s perspective. You probably just want to know that the way you&rsquo;re calling the equivalent <code>cosf</code> intrinsic is the best way possible. <strong>The answer is yes, doing it by value is indeed the best way possible.</strong></p>
]]></content></item><item><title>Esoteric Errors: 'hidden symbol is referenced by DSO'</title><link>https://icyveins7.github.io/posts/2024/10/esoteric-errors-hidden-symbol-is-referenced-by-dso/</link><pubDate>Fri, 18 Oct 2024 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2024/10/esoteric-errors-hidden-symbol-is-referenced-by-dso/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Hidden linkage isn&amp;rsquo;t something I&amp;rsquo;d encountered until now, so maybe this will help someone else too..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="a-new-series-of-posts"&gt;A new series of posts&lt;/h1&gt;
&lt;p&gt;I recently started work on a new codebase - one that is very large and has multiple moving components, from a frontend desktop UI (not my business) to the hardware interface (also not my business) and the backend processing (this one&amp;rsquo;s my business).&lt;/p&gt;
&lt;p&gt;The standard build process had been setup on a remote server, and most people were ok with the process of connecting to it with tangible amounts of latency, but I was not. So my stubborn ass decided to figure out how to build it myself on a local machine. In the process, I encountered a ridiculous number of build errors; they were using custom Makefiles, but they had been generated by some other tool (possibly from Windows, even though it was being built in Linux, since there was a Windows VS solution as well).&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Hidden linkage isn&rsquo;t something I&rsquo;d encountered until now, so maybe this will help someone else too..</p>
</blockquote>
<h1 id="a-new-series-of-posts">A new series of posts</h1>
<p>I recently started work on a new codebase - one that is very large and has multiple moving components, from a frontend desktop UI (not my business) to the hardware interface (also not my business) and the backend processing (this one&rsquo;s my business).</p>
<p>The standard build process had been setup on a remote server, and most people were ok with the process of connecting to it with tangible amounts of latency, but I was not. So my stubborn ass decided to figure out how to build it myself on a local machine. In the process, I encountered a ridiculous number of build errors; they were using custom Makefiles, but they had been generated by some other tool (possibly from Windows, even though it was being built in Linux, since there was a Windows VS solution as well).</p>
<p>This will be the beginning of a series where I will document these errors for myself (and hopefully for others!).</p>
<h1 id="topic-for-today">Topic for today</h1>
<p>Alright enough backstory. The error being discussed here goes something like this:</p>
<pre tabindex="0"><code>hidden symbol X in Y is referenced by DSO
</code></pre><p>GCC (or rather, the linker) spat this out at me when it was trying to link in a Boost static library.</p>
<blockquote>
<p>As a note, DSO stands for Dynamic Shared Object, which is just Linux&rsquo;s name for shared libraries.</p>
</blockquote>
<h1 id="its-not-an-undefined-reference">It&rsquo;s <em>not</em> an undefined reference</h1>
<p>You&rsquo;ll be forgiven for thinking so, because that&rsquo;s what came to mind at first. But no, it&rsquo;s in the description; the symbol was there in the <code>.a</code> archive (an <code>nm</code> and <code>objdump</code> confirmed this).</p>
<p>This was a different kind of error, and one that I think resulted from the choice to build multiple parts of the code into separate shared library objects (<code>.so</code>s).</p>
<p>If you want to go ahead and read the stackoverflow links that helped me, go ahead and click <a href="https://stackoverflow.com/questions/23696585/what-does-exactly-the-warning-mean-about-hidden-symbol-being-referenced-by-dso">here</a>.</p>
<h1 id="its-also-not-a-link-order-issue">It&rsquo;s also not a link order issue</h1>
<p>You&rsquo;ll again be forgiven for thinking this might have been the cause. This was what I tried at first to reproduce the problem in a simple scenario.</p>
<p>For those who don&rsquo;t know, <code>ld</code> resolves symbol references in a very specific order:</p>
<ol>
<li>A compilation unit needs a particular symbol.</li>
<li>Libraries specified <em><strong>after</strong></em> that compilation unit provide this symbol.</li>
<li>Linker does its thing.</li>
</ol>
<p>This is why specifying libraries in the right order matters, because if you provide the library &lsquo;<em>before</em>&rsquo; you make the &lsquo;request&rsquo;, the <code>ld</code> will not go back and get it for you (but I think MSVC will, so 1 point to Microsoft I guess?).</p>
<p>I&rsquo;ll leave this excellent writeup on the topic <a href="https://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking">here</a>, as I think it explains the topic far better than I ever could (he even talks about the circular dependencies!)</p>
<h2 id="a-little-more-in-depth-what-does-the-linker-actually-pull-in">A little more in-depth: what does the linker actually pull in?</h2>
<p>What I tried to do to reproduce the <code>hidden symbol</code> issue at first was to create the following scenario:</p>
<ol>
<li>Compile two separate source files separately: we&rsquo;ll call them <code>static.cpp</code> and <code>static_exclude.cpp</code> for reasons that will become apparent. They will share the same header, <code>static.h</code>, and we will place them all into the same static library.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// static.h
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> <span style="color:#a6e22e">addstatic</span>(<span style="color:#66d9ef">int</span> a, <span style="color:#66d9ef">int</span> b);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> <span style="color:#a6e22e">notinshared</span>(<span style="color:#66d9ef">int</span> a, <span style="color:#66d9ef">int</span> b);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">float</span> <span style="color:#a6e22e">exclude</span>(<span style="color:#66d9ef">float</span> a, <span style="color:#66d9ef">float</span> b);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// static.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;static.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> <span style="color:#a6e22e">addstatic</span>(<span style="color:#66d9ef">int</span> a, <span style="color:#66d9ef">int</span> b){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> a<span style="color:#f92672">+</span>b;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> <span style="color:#a6e22e">notinshared</span>(<span style="color:#66d9ef">int</span> a, <span style="color:#66d9ef">int</span> b){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> a<span style="color:#f92672">-</span>b;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// static_exclude.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;static.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">float</span> <span style="color:#a6e22e">exclude</span>(<span style="color:#66d9ef">float</span> a, <span style="color:#66d9ef">float</span> b){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> a<span style="color:#f92672">/</span>b;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Honestly, I don&rsquo;t know how to write raw Makefiles (I&rsquo;m more of a CMake dude), so I had ChatGPT spit one out for me:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-make" data-lang="make"><span style="display:flex;"><span>STATIC_LIB <span style="color:#f92672">=</span> libstatic.a
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Compile static.cpp to a static library (.a)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(STATIC_LIB)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>STATIC_SRC<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>CXXFLAGS<span style="color:#66d9ef">)</span> -c static.cpp -o static.o
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>CXXFLAGS<span style="color:#66d9ef">)</span> -c static_exclude.cpp -o static_exclude.o
</span></span><span style="display:flex;"><span>	ar rcs $@ static.o static_exclude.o
</span></span></code></pre></div><p>So we made our <code>libstatic.a</code>.</p>
<blockquote>
<p>Fun fact: doing this taught me that Makefiles <em>must</em> use tabs in the recipe, not spaces!</p>
</blockquote>
<ol start="2">
<li>Now we make a shared library from <code>shared.cpp</code>. This will use some functions from the static library we just made.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// shared.h
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> <span style="color:#a6e22e">sharedaddscale</span>(<span style="color:#66d9ef">int</span> a, <span style="color:#66d9ef">int</span> b, <span style="color:#66d9ef">int</span> c);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// shared.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;shared.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;static.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> <span style="color:#a6e22e">sharedaddscale</span>(<span style="color:#66d9ef">int</span> a, <span style="color:#66d9ef">int</span> b, <span style="color:#66d9ef">int</span> c){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> addstatic(a,b)<span style="color:#f92672">*</span>c;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And another excerpt from ChatGPT:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-make" data-lang="make"><span style="display:flex;"><span>SHARED_OBJ <span style="color:#f92672">=</span> shared.o
</span></span><span style="display:flex;"><span>SHARED_LIB <span style="color:#f92672">=</span> libshared.so
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Compile shared.cpp to an object file
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(SHARED_OBJ)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>SHARED_SRC<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>CXXFLAGS<span style="color:#66d9ef">)</span> -c $&lt; -o $@
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Link shared.o and static library to create the shared object (.so)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(SHARED_LIB)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>SHARED_OBJ<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>STATIC_LIB<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> -shared -o $@ <span style="color:#66d9ef">$(</span>SHARED_OBJ<span style="color:#66d9ef">)</span> -L. -lstatic
</span></span></code></pre></div><p>This makes <code>libshared.so</code>.</p>
<ol start="3">
<li>Finally, we create our main executable file <code>e.cpp</code>.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;static.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;shared.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;iostream&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> <span style="color:#a6e22e">main</span>(){
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  printf(<span style="color:#e6db74">&#34;%d</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>, sharedaddscale(<span style="color:#ae81ff">2</span>,<span style="color:#ae81ff">3</span>,<span style="color:#ae81ff">4</span>));
</span></span><span style="display:flex;"><span>  printf(<span style="color:#e6db74">&#34;%d</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>, notinshared(<span style="color:#ae81ff">5</span>,<span style="color:#ae81ff">3</span>));
</span></span><span style="display:flex;"><span>  printf(<span style="color:#e6db74">&#34;%d</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>, exclude(<span style="color:#ae81ff">5.0f</span>,<span style="color:#ae81ff">3.0f</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And we link our shared library to it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-make" data-lang="make"><span style="display:flex;"><span>E_SRC <span style="color:#f92672">=</span> e.cpp
</span></span><span style="display:flex;"><span>E_OBJ <span style="color:#f92672">=</span> e.o
</span></span><span style="display:flex;"><span>EXEC <span style="color:#f92672">=</span> final_executable
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Compile e.cpp to an object file
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(E_OBJ)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>E_SRC<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>CXXFLAGS<span style="color:#66d9ef">)</span> -c $&lt; -o $@
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Link e.o with the shared library to create the final executable
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(EXEC)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>E_OBJ<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>SHARED_LIB<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> -o $@ <span style="color:#66d9ef">$(</span>E_OBJ<span style="color:#66d9ef">)</span> -L. -lshared
</span></span></code></pre></div><p>The question for you now is: does this build? If not, which function calls will cause errors?</p>
<p>You can ponder the code and build paths above before continuing down..</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>&hellip;</p>
<p>Alright that&rsquo;s enough blank space. If you said <em>only <code>exclude()</code></em> will cause issues, then you would be correct, so congratulations! You&rsquo;re a qualified GCC nerd.</p>
<h2 id="why-does-exclude-cause-an-undefined-reference">Why does <code>exclude()</code> cause an undefined reference?</h2>
<p>When the linker finds a symbol reference, it searches the later inputs to try to find it. Here, we only linked the shared library, so the next question must be: why is <code>exclude()</code> not inside the shared library? Didn&rsquo;t we link the static library when we created it?</p>
<p>The answer is that the linker resolves and includes only symbol definitions that it <em>requires</em>. The shared library never needed <code>exclude()</code>, so it never got included in the final output.</p>
<h2 id="okay-but-the-shared-library-also-didnt-need-notinshared-so-why-is-it-there">Okay, but the shared library also didn&rsquo;t need <code>notinshared()</code>, so why is it there?</h2>
<p>The above answer isn&rsquo;t technically complete. What the linker does exactly is to find a symbol reference it requries, and then <em>include the entire object file</em>. It doesn&rsquo;t matter whether it is a simple <code>.o</code>, or an archive (static library <code>.a</code>) of object files.</p>
<p>In the above example, we did need <code>addstatic()</code> inside <code>shared.cpp</code>. But <code>addstatic()</code> shares the same compilation unit (and hence the same object file) as <code>notinshared()</code> - both come from <code>static.cpp</code> which became <code>static.o</code> - so both of them end up inside the final shared library!</p>
<blockquote>
<p>Static libraries are basically just containers of object files to the linker.</p>
</blockquote>
<p><a href="https://stackoverflow.com/questions/54126641/symbols-in-static-library-sometimes-got-linked-into-executable-sometimes-not">This</a> is a pretty good post about the above linkage behaviour.</p>
<h2 id="fine-isnt-the-solution-easy-just-link-the-static-library">Fine, isn&rsquo;t the solution easy? Just link the static library?</h2>
<p>Yes, that is indeed the solution here. This way the linker sees the necessary symbol, which is present in <code>libstatic.a</code>. But technically, that&rsquo;s not the only way to fix the problem..</p>
<h2 id="including-everything-from-the-archive">Including everything from the archive</h2>
<p>We&rsquo;ve already seen that the linker has the freedom to pick and choose the objects it wants to include. But if you&rsquo;re building a shared library, you probably just want to dump everything in; that way, the end-user can just use <em>your</em> library, instead of having to link in the original static library as well.</p>
<p>Well, we can do that too, with <code>-Wl,--whole-archive</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-make" data-lang="make"><span style="display:flex;"><span><span style="color:#75715e"># Link shared.o and static library to create the shared object (.so)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(SHARED_LIB)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>SHARED_OBJ<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>STATIC_LIB<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> -shared -o $@ <span style="color:#66d9ef">$(</span>SHARED_OBJ<span style="color:#66d9ef">)</span> -L. -Wl,--whole-archive -lstatic
</span></span></code></pre></div><p>This inserts all object file components from the static library directly into the new shared library, so we can find the symbols again!</p>
<h1 id="finally-we-get-back-to-the-title-question">Finally we get back to the title question..</h1>
<p>Of course, the above isn&rsquo;t the full story. Most libraries nowadays limit the <em>visibility</em> of their functions. For those that want the full story, just read <a href="https://gcc.gnu.org/wiki/Visibility">this</a>.</p>
<p>You might be thinking: okay, I don&rsquo;t really care about &lsquo;protecting&rsquo; any part of my code, so should I ever use this if I&rsquo;m writing a <code>.so</code>? The answer is still yes, because it is likely to generate more optimal and smaller code.</p>
<h2 id="we-could-have-built-the-shared-library-without-linking-the-static-one">We could have built the shared library without linking the static one</h2>
<p>Before we go into the details, let&rsquo;s return to the example above, and assume we linked <code>libstatic.a</code> in the link step for the final executable, in order to fix the issue. You should have noticed that we are now linking <code>libstatic.a</code> at both the DSO creation of <code>libshared.so</code> as well as the executable itself, and this is not necessary; DSOs are perfectly happy with &lsquo;postponing&rsquo; looking for a symbol until a later time, so in this particular scenario we can do the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-make" data-lang="make"><span style="display:flex;"><span><span style="color:#75715e"># Build .so, leaving out all libstatic symbols for now
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(SHARED_LIB)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>SHARED_OBJ<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>STATIC_LIB<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> -shared -o $@ <span style="color:#66d9ef">$(</span>SHARED_OBJ<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ...
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Build final executable, linking in libstatic symbols to solve both the DSO&#39;s
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># used symbol (sharedaddscale) and the ones the symbols from object files
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># that were left out (excluded)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(EXEC)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>E_OBJ<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>SHARED_LIB<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> -o $@ <span style="color:#66d9ef">$(</span>E_OBJ<span style="color:#66d9ef">)</span> -L. -lshared -lstatic
</span></span></code></pre></div><p>This would have solved all our undefined reference problems, and compiles correctly.</p>
<h2 id="now-we-introduce-hidden-visibility">Now we introduce <em>hidden</em> visibility..</h2>
<p>Continuing from above, I&rsquo;m now going to turn on <code>-fvisibility=hidden</code> for both <code>static.cpp</code> and <code>static_exclude.cpp</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-make" data-lang="make"><span style="display:flex;"><span><span style="color:#75715e"># Compile static.cpp to a static library (.a)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(STATIC_LIB)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>STATIC_SRC<span style="color:#66d9ef">)</span>  
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>CXXFLAGS<span style="color:#66d9ef">)</span> -fvisibility<span style="color:#f92672">=</span>hidden -c static.cpp -o static.o
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>CXXFLAGS<span style="color:#66d9ef">)</span> -fvisibility<span style="color:#f92672">=</span>hidden -c static_exclude.cpp -o static_exclude.o
</span></span><span style="display:flex;"><span>	ar rcs $@ static.o static_exclude.o
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Build .so, leaving out all libstatic symbols for now
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(SHARED_LIB)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>SHARED_OBJ<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>STATIC_LIB<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> -shared -o $@ <span style="color:#66d9ef">$(</span>SHARED_OBJ<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Link e.o with the shared library to create the final executable
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># Also link static library to solve undefined reference
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">$(EXEC)</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">$(</span>E_OBJ<span style="color:#66d9ef">)</span> <span style="color:#66d9ef">$(</span>SHARED_LIB<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">$(</span>CXX<span style="color:#66d9ef">)</span> -o $@ <span style="color:#66d9ef">$(</span>E_OBJ<span style="color:#66d9ef">)</span> -L. -lshared -lstatic
</span></span></code></pre></div><p>I did not change the internal code whatsoever, just this Makefile. Building again now results in the following error (yes, this is a screenshot from an iPhone, shoutout to the makers of <a href="https://github.com/ish-app/ish">iSH</a>):</p>
<p><img src="/static/images/hiddensymbolerror.jpg" alt="ish screenshot of hidden symbol error"></p>
<h2 id="recap-of-what-we-did-here-and-the-possible-fixes">Recap of what we did here and the possible fixes</h2>
<p>The order of problems and solutions we just presented to reach this step is as follows:</p>
<ol>
<li>First build the static library as per normal, then create the shared library using it with a simple <code>-lstatic</code>, and finally create the executable linking only the shared library. <em>This causes an undefined reference, since our executable uses a function in an object that was dropped by the linker when creating the shared library.</em></li>
<li>We fixed this by linking our static library in the final executable as well. <em>This fixes the undefined reference as we provide the object file containing the function we used.</em></li>
<li>Since we are linking our static library in 2 steps - the shared library and the executable - we can remove the static library link for the shared library and the executable will still compile correctly, so we do this.</li>
<li>We then turned on <code>-fvisibility=hidden</code> for all our initial static library source files. <em>This causes the <code>hidden symbol in DSO</code> error, since <strong>ALL</strong> the functions we are using are now hidden by default, and hence cannot be used by our executable.</em></li>
</ol>
<p>Now the fix for this is actually quite simple; we simply need to link the static library during the shared library creation again!</p>
<p>Doing this places our hidden static library symbols into the DSO, which can then be used by the shared library&rsquo;s exported symbol. Dumping the symbols using <code>nm libshared.so</code> I see:</p>
<pre tabindex="0"><code>T _Z14sharedaddscaleiii
t _Z15notusedbysharedii
t _Z9addstaticii
</code></pre><p>where the <code>T</code> means that it is included and <code>t</code> means it is hidden (if it&rsquo;s your first time, just look up <code>nm</code> or <code>objdump</code> output). Hence, the shared library now clearly contains the necessary hidden symbol for <code>addstatic</code>, and this is referenced by <code>sharedaddscale</code> in the final executable without having to look <em>outside</em> the current DSO; this is exactly what <em>hidden</em> visibility is meant to do.</p>
<h1 id="some-concluding-remarks">Some concluding remarks</h1>
<p>These recent discoveries have made me think about the importance of understanding not just the shiny <em>algorithms</em> (read: leet code) and computer science things, but also learning about the tools available to us as developers.</p>
<p>It&rsquo;s like a carpenter learning about the optimal angle to hammer a nail but not knowing how to use an electric drill (I&rsquo;m not a carpenter but I build IKEA things so hopefully that was reasonable).</p>
]]></content></item><item><title>Who knew a simple logger class would be this complicated?</title><link>https://icyveins7.github.io/posts/2024/09/who-knew-a-simple-logger-class-would-be-this-complicated/</link><pubDate>Mon, 09 Sep 2024 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2024/09/who-knew-a-simple-logger-class-would-be-this-complicated/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Writing a printf-based C++ logger class was more of a journey than I originally thought..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I recently had to work with a codebase where the build process was so convoluted it couldn’t be run and debugged from within a Visual Studio instance, &lt;em&gt;despite being a Visual Studio solution&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This was primarily because it had a bunch of Java components, which were mainly used for the UI, and that prevented it from being run as a standard C++ application within the debugger (or maybe you could? I couldn’t find a way..)&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Writing a printf-based C++ logger class was more of a journey than I originally thought..</p>
</blockquote>
<p>I recently had to work with a codebase where the build process was so convoluted it couldn’t be run and debugged from within a Visual Studio instance, <em>despite being a Visual Studio solution</em>.</p>
<p>This was primarily because it had a bunch of Java components, which were mainly used for the UI, and that prevented it from being run as a standard C++ application within the debugger (or maybe you could? I couldn’t find a way..)</p>
<p>Regardless, the <em>normal</em> way - the way that everyone else working on it would do it - to debug was through logging. I had to make do, so I hopped on the faulty bandwagon.</p>
<h1 id="printf-enjoyers-unite"><code>printf</code> enjoyers unite</h1>
<p>Alright alright, it’s not C++, but I honestly think C-like printf with its format specifier syntax is the cleanest, most concise way to log different POD-type values.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a6e22e">printf</span>(<span style="color:#960050;background-color:#1e0010">“</span>a: <span style="color:#f92672">%</span><span style="color:#ae81ff">.6f</span><span style="color:#960050;background-color:#1e0010">\</span>nb: <span style="color:#f92672">%</span>d<span style="color:#960050;background-color:#1e0010">\</span>nc: <span style="color:#f92672">%</span><span style="color:#ae81ff">.2</span>g<span style="color:#960050;background-color:#1e0010">\</span>n<span style="color:#960050;background-color:#1e0010">”</span>, a, b, c);
</span></span></code></pre></div><p>The equivalent of the above in C++ with stream-like things:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#960050;background-color:#1e0010">“</span>a: <span style="color:#960050;background-color:#1e0010">“</span> <span style="color:#f92672">&lt;&lt;</span> std<span style="color:#f92672">::</span>fixed <span style="color:#f92672">&lt;&lt;</span> std<span style="color:#f92672">::</span>setprecision(<span style="color:#ae81ff">6</span>) <span style="color:#f92672">&lt;&lt;</span> a <span style="color:#f92672">&lt;&lt;</span> std<span style="color:#f92672">::</span>endl <span style="color:#f92672">&lt;&lt;</span> <span style="color:#960050;background-color:#1e0010">“</span>b: <span style="color:#960050;background-color:#1e0010">“</span> <span style="color:#f92672">&lt;&lt;</span> b <span style="color:#f92672">&lt;&lt;</span> std<span style="color:#f92672">::</span>endl <span style="color:#f92672">&lt;&lt;</span> <span style="color:#960050;background-color:#1e0010">“</span>c: <span style="color:#960050;background-color:#1e0010">“</span> <span style="color:#f92672">&lt;&lt;</span> std<span style="color:#f92672">::</span>scientific <span style="color:#f92672">&lt;&lt;</span> std<span style="color:#f92672">::</span>setprecision(<span style="color:#ae81ff">2</span>) <span style="color:#f92672">&lt;&lt;</span> c <span style="color:#f92672">&lt;&lt;</span> std<span style="color:#f92672">::</span>endl;
</span></span></code></pre></div><p>Holy shit, what a monster. Even if you remove every namespace call to <code>std::</code> and replace the <code>endl</code> calls with the newline characters, you’d still be sitting with a disgustingly long line of code.</p>
<p>It gets slightly better with C++20 and <code>std::fmt</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> std<span style="color:#f92672">::</span>format(<span style="color:#960050;background-color:#1e0010">“</span>a: {<span style="color:#f92672">:%</span><span style="color:#ae81ff">.6f</span>}<span style="color:#960050;background-color:#1e0010">\</span>nb: {<span style="color:#f92672">:%</span>d}<span style="color:#960050;background-color:#1e0010">\</span>nc: {<span style="color:#f92672">:%</span><span style="color:#ae81ff">.2</span>g}<span style="color:#960050;background-color:#1e0010">\</span>n<span style="color:#960050;background-color:#1e0010">”</span>, a, b, c);
</span></span></code></pre></div><p>But still, why write an extra function call to <code>std::format</code> and extra braces when I didn’t have to previously? No one is going to change my mind on this. <em>Yes, I know it provides a lot more functionality, but I don’t really care for all those bells and whistles.</em></p>
<h1 id="printf-but-with-raii"><code>printf</code>, but with RAII</h1>
<p>Alright let’s just assume we want to have <code>printf</code> style logs, but still have access to the C-like defines in <code>__FILE__</code> and <code>__LINE__</code>. That let us print things like</p>
<pre tabindex="0"><code>/some/path/badfile.cpp:237
</code></pre><p>by simply using some macro magic like</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">#define log(…) my_logger_func(__FILE__, __LINE__, __VA_ARGS__)
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// on line 237 of badfile.cpp
</span></span></span><span style="display:flex;"><span>log(<span style="color:#960050;background-color:#1e0010">“</span>some msg<span style="color:#960050;background-color:#1e0010">”</span>);
</span></span></code></pre></div><p>Libraries like <a href="https://github.com/rxi/log.c">this amazing one</a> do exactly that.</p>
<p>But I wanted to encapsulate some other things, like the automatic closing of a log file upon destruction.</p>
<p>Here’s the first problem if you make a class and provide a method to log, instead of a macro; the preprocessor magic no longer works, because the substitution for <code>__FILE__</code> and <code>__LINE__</code> will now occur directly where it was written (inside your class method) instead of where you <em>called the method</em>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// logger.h
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Logger</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">…</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// At line 25 for example
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> log(<span style="color:#960050;background-color:#1e0010">…</span>){
</span></span><span style="display:flex;"><span>  printf(<span style="color:#960050;background-color:#1e0010">“</span><span style="color:#f92672">%</span>s <span style="color:#f92672">%</span>d: some log message.<span style="color:#960050;background-color:#1e0010">\</span>n<span style="color:#960050;background-color:#1e0010">”</span>, __FILE__, __LINE__);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">…</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// main.cpp
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">…</span>
</span></span><span style="display:flex;"><span>logger.log(<span style="color:#960050;background-color:#1e0010">…</span>);
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">…</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// This will always print 
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// logger.h 25: some log message, regardless of where it’s called
</span></span></span></code></pre></div><p>This is obviously not what we want. The only way around this would have been to provide <code>__FILE__</code> and <code>__LINE__</code> on every log method call, which would obviously be a far worse experience.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">// no one is going to do this
</span></span></span><span style="display:flex;"><span>lgr.log(__FILE__, __LINE__, <span style="color:#960050;background-color:#1e0010">“</span>what i actually want to log<span style="color:#960050;background-color:#1e0010">”</span>);
</span></span></code></pre></div><h1 id="c20-and-stdsource_location">C++20 and <code>std::source_location</code></h1>
<p>I said <em>‘would have been’</em> because in C++20 we now have a useful alternative with <code>std::source_location</code>. Now we can do this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Logger</span>{
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">log</span>(std<span style="color:#f92672">::</span>source_location l <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>source_location<span style="color:#f92672">::</span>current()){
</span></span><span style="display:flex;"><span>    printf(<span style="color:#960050;background-color:#1e0010">“</span><span style="color:#f92672">%</span>s:<span style="color:#f92672">%</span>d<span style="color:#960050;background-color:#1e0010">”</span>, l.file_name(), l.line());
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The default argument gets substituted at the point the method is called, and we get back our true filename and line numbers!</p>
<p>But there’s now another problem: dealing with multiple, variable arguments for the substitutions.</p>
<p>In the world of C, we had <code>__VA_ARGS__</code>, or the corresponding variadic function helpers (see <a href="https://en.cppreference.com/w/c/variadic">this</a>). In C++, we have similar functionality with variadic templates (see <a href="https://en.cppreference.com/w/cpp/language/parameter_pack">this</a>), also known as parameter packs.</p>
<p>Now this is great - we can do a variable number of arbitrarily typed substitutions, <code>printf</code> style - but there’s a catch.</p>
<p>Both of these requirements need to be at the <em>end</em> of the function signature. We need the default argument to be at the end, because otherwise it won’t compile.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Logger</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// this doesn’t compile
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span><span style="color:#960050;background-color:#1e0010">…</span> Args<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">void</span> log(std<span style="color:#f92672">::</span>source_location l <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>source_location<span style="color:#f92672">::</span>current(), Args<span style="color:#960050;background-color:#1e0010">…</span> args)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#960050;background-color:#1e0010">…</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>But we also need the parameter pack to be at the end, otherwise it won’t be able to automatically predict the template types for us:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Logger</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// this may compile, but may give unexpected results
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// because the final argument in the function call 
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// will be directed to the std::source_location variable
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// in most cases it will fail since the supplied 
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// argument cannot be converted to a std::source_location
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span><span style="color:#960050;background-color:#1e0010">…</span> Args<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">void</span> log(<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">char</span><span style="color:#f92672">*</span> s, Args<span style="color:#960050;background-color:#1e0010">…</span> args, std<span style="color:#f92672">::</span>source_location l <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>source_location<span style="color:#f92672">::</span>current())
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#960050;background-color:#1e0010">…</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// main.cpp
</span></span></span><span style="display:flex;"><span>lgr.log(<span style="color:#960050;background-color:#1e0010">“</span>my custom message <span style="color:#f92672">%</span>d <span style="color:#f92672">%</span>d<span style="color:#960050;background-color:#1e0010">”</span>, lognumber, mylogvariable);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// the compiler will (attempt to) assign mylogvariable
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// to replace the default std::source_location argument
</span></span></span></code></pre></div><p>There’s a very good discussion of this exact problem - which I referenced while coming up with my own flavour of a solution - <a href="https://www.cppstories.com/2021/non-terminal-variadic-args/">here</a>. For example, one way to ensure that the parameter unpacking template works as intended is to specify the exact types:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>lgr.log<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">int</span>, <span style="color:#66d9ef">int</span><span style="color:#f92672">&gt;</span>(<span style="color:#960050;background-color:#1e0010">“</span>my custom message <span style="color:#f92672">%</span>d <span style="color:#f92672">%</span>d<span style="color:#960050;background-color:#1e0010">”</span>, lognumber, mylogvariable);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// this will print what you would expect
</span></span></span></code></pre></div><p>But this is a lot more verbose than I would like; it should be clear that with 10 variable substitutions you’d need to specify 10 types, so it can quickly get out of hand.</p>
<h1 id="the-most-concise-compromise-i-could-tolerate">The most concise compromise I could tolerate</h1>
<p>My number one goal was to ensure as few characters typed, when compared to the original <code>printf</code> form.</p>
<p>I decided to use the ‘constructor template’ to achieve this. The final code looks something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Writer</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  std<span style="color:#f92672">::</span>source_location m_l;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  Writer(std<span style="color:#f92672">::</span>source_location l) <span style="color:#f92672">:</span> m_l(l)
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#960050;background-color:#1e0010">…</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span><span style="color:#960050;background-color:#1e0010">…</span> Args<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>  log(<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">char</span><span style="color:#f92672">*</span> s, Args<span style="color:#960050;background-color:#1e0010">…</span> args)
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#960050;background-color:#1e0010">…</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Logger</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">…</span>
</span></span><span style="display:flex;"><span>  Writer <span style="color:#66d9ef">operator</span>()(std<span style="color:#f92672">::</span>source_location l<span style="color:#f92672">=</span>std<span style="color:#f92672">::</span>source_location<span style="color:#f92672">::</span>current())
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">Writer</span>(l);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">…</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// main.cpp
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// writer class constructed
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//   ^   writer class method invoked
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//   |   ^
</span></span></span><span style="display:flex;"><span>lgr().log(<span style="color:#960050;background-color:#1e0010">“</span>my message <span style="color:#f92672">%</span>d <span style="color:#f92672">%</span>d<span style="color:#960050;background-color:#1e0010">”</span>, <span style="color:#ae81ff">123</span>, <span style="color:#ae81ff">456</span>);
</span></span></code></pre></div><p>The idea is this:</p>
<ol>
<li>Use <code>operator()</code> to instantiate a writer class, which is constructed with the correct value of <code>std::source_location</code>.</li>
<li>The new writer class then contains the necessary logging methods like <code>log()</code>, with the parameter packs we want. The file and line numbers are substituted directly from the class member variable.</li>
</ol>
<p>This has the benefit of separating the ‘file holding’ class from the ‘log writing’ class, and allows us to fix the fight between the parameter pack and default argument order.</p>
<p>Is this the most efficient? Probably not - I think the compiler will elide the copy during the construction of the writer, but you would still construct on every line you log.</p>
<p>Is it the safest? Also probably not - in the actual code I put some safeguards like having a private constructor, but I’m sure there will be some ways that something can go wrong (and that I’ll update the code with as I find out).</p>
<p>But I got what I wanted: a short logging method call that is barely longer than a <code>printf</code>, and works exactly the same way.</p>
<p>The final code is <a href="https://github.com/icyveins7/spfLogger">here</a> if you’d like to see or use it!</p>
]]></content></item><item><title>Some notes on 2D real-to-complex Fourier transforms</title><link>https://icyveins7.github.io/posts/2024/07/some-notes-on-2d-real-to-complex-fourier-transforms/</link><pubDate>Mon, 15 Jul 2024 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2024/07/some-notes-on-2d-real-to-complex-fourier-transforms/</guid><description>&lt;blockquote&gt;
&lt;p&gt;IPP in particular has some very niche ways of packing R2C DFT output, but otherwise there&amp;rsquo;s a few pointers here to keep in mind for how they are implemented in most libraries.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&amp;rsquo;s pretty well known that the output of a Fourier transform of real inputs has symmetric properties. This is due to the fact that real waves consist of two conjugate pairs of complex exponentials.&lt;/p&gt;
&lt;p&gt;What may be a bit less obvious (at least to me, when examining some programming libraries) is exactly how many useful output elements there are, and under which scenarios. In particular, I looked at IPP (which has some special packed structure), and cuFFT/NumPy/SciPy (which follow the FFTW structure I think).&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>IPP in particular has some very niche ways of packing R2C DFT output, but otherwise there&rsquo;s a few pointers here to keep in mind for how they are implemented in most libraries.</p>
</blockquote>
<p>It&rsquo;s pretty well known that the output of a Fourier transform of real inputs has symmetric properties. This is due to the fact that real waves consist of two conjugate pairs of complex exponentials.</p>
<p>What may be a bit less obvious (at least to me, when examining some programming libraries) is exactly how many useful output elements there are, and under which scenarios. In particular, I looked at IPP (which has some special packed structure), and cuFFT/NumPy/SciPy (which follow the FFTW structure I think).</p>
<h1 id="the-1d-definition">The 1D Definition</h1>
<p>We should start here before going on to 2D things. It&rsquo;s easily wiki-able, but here&rsquo;s our 1D DFT:</p>
$$
X_k = \sum_n x_n e^{-i 2 \pi \frac{kn}{N}}, \text{for } n \in [0,…,N-1]
$$<p>where $x_n$ consists of real elements in a length $N$ array.</p>
<h2 id="conjugate-pairs">Conjugate pairs</h2>
<p>The first obvious redundancy happens when you recognise</p>
$$
\begin{align}
X_{N-k} &= \sum_n x_n e^{-i 2 \pi \frac{(N-k)n}{N}} \\
&= \sum_n x_n e^{-i 2 \pi \frac{Nn}{N}} e^{i 2 \pi \frac{kn}{N}} \\
&= \sum_n x_n e^{i 2 \pi \frac{kn}{N}} \\
&= {X_k}^*
\end{align}
$$<p>
So <em>roughly</em> half the output is redundant. But for programming purposes, let’s be a bit more specific.</p>
<h2 id="odd">Odd $N$</h2>
<p>The 0-th element is self-conjugate, so it must be completely real. The remaining elements can be paired up, so in general there will be $\frac{N-1}{2}$ conjugate pairs of complex values that are useful.</p>
<p>In total this makes for $2\frac{N-1}{2} + 1 = N$ useful real values.</p>
<h2 id="even">Even $N$</h2>
<p>The 0-th element is self-conjugate and is completely real again. However there is another element at $X_{N/2} = {X_{N-N/2}}^*$ that by definition must also be self-conjugate, and hence completely real. The rest of the elements then comprise the $\frac{N-2}{2}$ conjugate pairs of useful complex values.</p>
<p>This again makes for a total of $\frac{N-2}{2} + 2 = N$ useful real values.</p>
<blockquote>
<p>In all cases, the number of useful, real values is equal to $N$. This shouldn’t be too surprising since the dimensionality cannot change!</p>
</blockquote>
<h1 id="library-output-formats">Library output formats</h1>
<h2 id="fftw-like-complex-output-numpyscipycufftipp-ccs">FFTW-like complex output (NumPy/SciPy/cuFFT/IPP CCS)</h2>
<p>Most libraries return a fully complex-valued, truncated output, with length $N/2 +1$. This handles both odd and even valued $N$ correctly (assuming you do integer division for $N/2$), with the last value having a non-zero imaginary component only for odd $N$.</p>
<p>This is equivalent to converting the input to complex first - NumPy/SciPy will do this for you - running a standard FFT, then dropping the second half(ish).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># x has length 5</span>
</span></span><span style="display:flex;"><span>In [<span style="color:#ae81ff">8</span>]: sp<span style="color:#f92672">.</span>fft<span style="color:#f92672">.</span>fft(x)
</span></span><span style="display:flex;"><span>Out[<span style="color:#ae81ff">8</span>]: array([<span style="color:#f92672">-</span><span style="color:#ae81ff">2.3526919</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">0.</span>j        ,  <span style="color:#ae81ff">1.37635917</span><span style="color:#f92672">-</span><span style="color:#ae81ff">0.27259233</span>j,  <span style="color:#ae81ff">0.5953175</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">0.55606247</span>j,  <span style="color:#ae81ff">0.5953175</span> <span style="color:#f92672">+</span><span style="color:#ae81ff">0.55606247</span>j,  <span style="color:#ae81ff">1.37635917</span><span style="color:#f92672">+</span><span style="color:#ae81ff">0.27259233</span>j])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># So this stops at 5//2 + 1 = 3, and last element has a non-zero imaginary component</span>
</span></span><span style="display:flex;"><span>In [<span style="color:#ae81ff">9</span>]: sp<span style="color:#f92672">.</span>fft<span style="color:#f92672">.</span>rfft(x)
</span></span><span style="display:flex;"><span>Out[<span style="color:#ae81ff">9</span>]: array([<span style="color:#f92672">-</span><span style="color:#ae81ff">2.3526919</span> <span style="color:#f92672">+</span><span style="color:#ae81ff">0.</span>j        ,  <span style="color:#ae81ff">1.37635917</span><span style="color:#f92672">-</span><span style="color:#ae81ff">0.27259233</span>j,  <span style="color:#ae81ff">0.5953175</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">0.55606247</span>j])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># .....</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Now x has length 6</span>
</span></span><span style="display:flex;"><span>In [<span style="color:#ae81ff">11</span>]: sp<span style="color:#f92672">.</span>fft<span style="color:#f92672">.</span>fft(x)
</span></span><span style="display:flex;"><span>Out[<span style="color:#ae81ff">11</span>]: array([<span style="color:#f92672">-</span><span style="color:#ae81ff">0.391538</span>  <span style="color:#f92672">-</span><span style="color:#ae81ff">0.</span>j        , <span style="color:#f92672">-</span><span style="color:#ae81ff">2.01281022</span><span style="color:#f92672">-</span><span style="color:#ae81ff">0.2942553</span>j ,  <span style="color:#ae81ff">2.76346742</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2.30952267</span>j,  <span style="color:#ae81ff">1.36395615</span><span style="color:#f92672">-</span><span style="color:#ae81ff">0.</span>j        ,  <span style="color:#ae81ff">2.76346742</span><span style="color:#f92672">-</span><span style="color:#ae81ff">2.30952267</span>j, <span style="color:#f92672">-</span><span style="color:#ae81ff">2.01281022</span><span style="color:#f92672">+</span><span style="color:#ae81ff">0.2942553</span>j ])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># And this stops at 6//2 + 1 = 4, with the last element having zero for its imaginary component</span>
</span></span><span style="display:flex;"><span>In [<span style="color:#ae81ff">12</span>]: sp<span style="color:#f92672">.</span>fft<span style="color:#f92672">.</span>rfft(x)
</span></span><span style="display:flex;"><span>Out[<span style="color:#ae81ff">12</span>]: array([<span style="color:#f92672">-</span><span style="color:#ae81ff">0.391538</span>  <span style="color:#f92672">+</span><span style="color:#ae81ff">0.</span>j        , <span style="color:#f92672">-</span><span style="color:#ae81ff">2.01281022</span><span style="color:#f92672">-</span><span style="color:#ae81ff">0.2942553</span>j ,  <span style="color:#ae81ff">2.76346742</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2.30952267</span>j,  <span style="color:#ae81ff">1.36395615</span><span style="color:#f92672">+</span><span style="color:#ae81ff">0.</span>j        ])
</span></span></code></pre></div><p>So far this shouldn&rsquo;t be hard to accept; you&rsquo;re just taking all the positive frequencies from a standard Fourier transform (with some handling for the edge bin).</p>
<h2 id="packed-output-ipp-pack">Packed output (IPP Pack)</h2>
<p>IPP has packed formats documented for its <a href="https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-12/packed-formats.html">signal</a> processing library part. I think the documentation there describes it sufficiently well, but in case you&rsquo;d like to see a concrete example, here&rsquo;s the extension using the above python array:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># This is the output of the rfft for the length 6 array</span>
</span></span><span style="display:flex;"><span>In [<span style="color:#ae81ff">14</span>]: y
</span></span><span style="display:flex;"><span>Out[<span style="color:#ae81ff">14</span>]: array([<span style="color:#f92672">-</span><span style="color:#ae81ff">0.391538</span>  <span style="color:#f92672">+</span><span style="color:#ae81ff">0.</span>j        , <span style="color:#f92672">-</span><span style="color:#ae81ff">2.01281022</span><span style="color:#f92672">-</span><span style="color:#ae81ff">0.2942553</span>j ,  <span style="color:#ae81ff">2.76346742</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2.30952267</span>j,  <span style="color:#ae81ff">1.36395615</span><span style="color:#f92672">+</span><span style="color:#ae81ff">0.</span>j        ])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># And this is how IPP would effectively pack the output</span>
</span></span><span style="display:flex;"><span>In [<span style="color:#ae81ff">15</span>]: np<span style="color:#f92672">.</span>hstack((y[<span style="color:#ae81ff">0</span>]<span style="color:#f92672">.</span>real, y[<span style="color:#ae81ff">1</span>:<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>]<span style="color:#f92672">.</span>view(np<span style="color:#f92672">.</span>float64), y[<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>]<span style="color:#f92672">.</span>real))
</span></span><span style="display:flex;"><span>Out[<span style="color:#ae81ff">15</span>]: array([<span style="color:#f92672">-</span><span style="color:#ae81ff">0.391538</span>  , <span style="color:#f92672">-</span><span style="color:#ae81ff">2.01281022</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">0.2942553</span> ,  <span style="color:#ae81ff">2.76346742</span>,  <span style="color:#ae81ff">2.30952267</span>,  <span style="color:#ae81ff">1.36395615</span>])
</span></span></code></pre></div><p>Since there are always $N$ real values in the output, we can drop the 0-valued imaginary components (the 0-index, and the N/2-index for the even $N$ case) and simply squeeze everything together. This would make the output a bit shorter.</p>
<p>Notably, I haven’t seen any other library opt to this, since it would require operating on the output in a non-uniform way; you would have to remember that the first (and possibly the last) output has 1 real element, while everything in between has 2 real elements (1 complex element). That means you can’t traverse it with the same pointer.</p>
<p>Understandably, this is probably why it isn’t seen elsewhere. IPP has a variation of choices in what output formats you can select - but not always, since in its image processing section you are <em>forced</em> to use this output format, and then convert it to another format if you wish after.</p>
<h1 id="2d-transforms-and-their-r2c-output-dimensions">2D transforms and their R2C output dimensions</h1>
<p>At first glance, you might have thought that since 1D transforms can be defined by half the output, 2D transforms could be defined by $1/4$ of them right?</p>
<p>Okay if not, then you’re smarter than I was. After I had realised the dimensionality argument for the 1D case, the 2D case became equally clear.</p>
<blockquote>
<p>In an $M \times N$ real matrix, the output of the transform must contain $MN$ real, useful values.</p>
</blockquote>
<p>It shouldn’t be hard to see that this means that you can still only drop about half of the full complex 2D DFT output.</p>
<p>But how do libraries choose what to keep? It turns out everyone seems to have <em>chosen to agree</em> on this part.</p>
<h2 id="slice-the-fastest-changing-dimension">Slice the fastest changing dimension</h2>
<p>The title says it all. In a typical array defined by, for example</p>
<pre tabindex="0"><code>std::complex&lt;float&gt; out[a][b];
</code></pre><p>the <code>b</code> index is the one that gets truncated. This also follows for 3D transforms, though I’m not going to bother with that here.</p>
<p>Again, there’s no mathematical reason behind this; it’s merely a convention that seems to be held by every library: NumPy/SciPy, cuFFT, etc.</p>
<p>To see why you could slice it the other way, let’s consider an example.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># We now have a random 6x6 input array</span>
</span></span><span style="display:flex;"><span>In [<span style="color:#ae81ff">25</span>]: sp<span style="color:#f92672">.</span>fft<span style="color:#f92672">.</span>fft2(x)
</span></span><span style="display:flex;"><span>Out[<span style="color:#ae81ff">25</span>]:
</span></span><span style="display:flex;"><span>array([[ <span style="color:#ae81ff">11.4684</span><span style="color:#f92672">-</span><span style="color:#ae81ff">0.0000e+00</span>j,   <span style="color:#ae81ff">7.1864</span><span style="color:#f92672">+</span><span style="color:#ae81ff">9.1749e+00</span>j,   <span style="color:#ae81ff">4.1346</span><span style="color:#f92672">-</span><span style="color:#ae81ff">3.6415e+00</span>j,   <span style="color:#ae81ff">0.9137</span><span style="color:#f92672">-</span><span style="color:#ae81ff">0.0000e+00</span>j,   <span style="color:#ae81ff">4.1346</span><span style="color:#f92672">+</span><span style="color:#ae81ff">3.6415e+00</span>j,   <span style="color:#ae81ff">7.1864</span><span style="color:#f92672">-</span><span style="color:#ae81ff">9.1749e+00</span>j],
</span></span><span style="display:flex;"><span>       [  <span style="color:#ae81ff">5.6947</span><span style="color:#f92672">+</span><span style="color:#ae81ff">1.9658e-01</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">0.5213</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2.2622e+00</span>j, <span style="color:#f92672">-</span><span style="color:#ae81ff">10.9693</span><span style="color:#f92672">-</span><span style="color:#ae81ff">4.7503e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">3.6554</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2.2839e-01</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">1.3505</span><span style="color:#f92672">+</span><span style="color:#ae81ff">4.1515e+00</span>j,   <span style="color:#ae81ff">3.4155</span><span style="color:#f92672">+</span><span style="color:#ae81ff">3.9835e+00</span>j],
</span></span><span style="display:flex;"><span>       [  <span style="color:#ae81ff">8.1551</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2.6689e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">5.7527</span><span style="color:#f92672">-</span><span style="color:#ae81ff">4.1722e+00</span>j,   <span style="color:#ae81ff">0.6473</span><span style="color:#f92672">-</span><span style="color:#ae81ff">1.1867e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">4.3752</span><span style="color:#f92672">+</span><span style="color:#ae81ff">5.1961e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">3.7517</span><span style="color:#f92672">+</span><span style="color:#ae81ff">1.5745e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">2.1656</span><span style="color:#f92672">+</span><span style="color:#ae81ff">9.6490e+00</span>j],
</span></span><span style="display:flex;"><span>       [ <span style="color:#f92672">-</span><span style="color:#ae81ff">1.089</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">2.9802e-08</span>j,   <span style="color:#ae81ff">1.31</span>  <span style="color:#f92672">-</span><span style="color:#ae81ff">1.0136e+01</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">4.7408</span><span style="color:#f92672">-</span><span style="color:#ae81ff">7.9127e-01</span>j,   <span style="color:#ae81ff">8.455</span> <span style="color:#f92672">+</span><span style="color:#ae81ff">3.7253e-08</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">4.7408</span><span style="color:#f92672">+</span><span style="color:#ae81ff">7.9127e-01</span>j,   <span style="color:#ae81ff">1.31</span>  <span style="color:#f92672">+</span><span style="color:#ae81ff">1.0136e+01</span>j],
</span></span><span style="display:flex;"><span>       [  <span style="color:#ae81ff">8.1551</span><span style="color:#f92672">-</span><span style="color:#ae81ff">2.6689e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">2.1656</span><span style="color:#f92672">-</span><span style="color:#ae81ff">9.6490e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">3.7517</span><span style="color:#f92672">-</span><span style="color:#ae81ff">1.5745e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">4.3752</span><span style="color:#f92672">-</span><span style="color:#ae81ff">5.1961e+00</span>j,   <span style="color:#ae81ff">0.6473</span><span style="color:#f92672">+</span><span style="color:#ae81ff">1.1867e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">5.7527</span><span style="color:#f92672">+</span><span style="color:#ae81ff">4.1722e+00</span>j],
</span></span><span style="display:flex;"><span>       [  <span style="color:#ae81ff">5.6947</span><span style="color:#f92672">-</span><span style="color:#ae81ff">1.9658e-01</span>j,   <span style="color:#ae81ff">3.4155</span><span style="color:#f92672">-</span><span style="color:#ae81ff">3.9835e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">1.3505</span><span style="color:#f92672">-</span><span style="color:#ae81ff">4.1515e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">3.6554</span><span style="color:#f92672">-</span><span style="color:#ae81ff">2.2839e-01</span>j, <span style="color:#f92672">-</span><span style="color:#ae81ff">10.9693</span><span style="color:#f92672">+</span><span style="color:#ae81ff">4.7503e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">0.5213</span><span style="color:#f92672">-</span><span style="color:#ae81ff">2.2622e+00</span>j]], dtype<span style="color:#f92672">=</span>complex64)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># As you can see the output of this is simply indexing the output above like [:,:4]</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># i.e. taking all rows, but cutting at the equivalent 1D required column</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Note that numpy arrays are row-major by default, so this has truncated the fastest changing dimension</span>
</span></span><span style="display:flex;"><span>In [<span style="color:#ae81ff">26</span>]: sp<span style="color:#f92672">.</span>fft<span style="color:#f92672">.</span>rfft2(x)
</span></span><span style="display:flex;"><span>Out[<span style="color:#ae81ff">26</span>]:
</span></span><span style="display:flex;"><span>array([[ <span style="color:#ae81ff">11.4684</span><span style="color:#f92672">+</span><span style="color:#ae81ff">0.0000e+00</span>j,   <span style="color:#ae81ff">7.1864</span><span style="color:#f92672">+</span><span style="color:#ae81ff">9.1749e+00</span>j,   <span style="color:#ae81ff">4.1346</span><span style="color:#f92672">-</span><span style="color:#ae81ff">3.6415e+00</span>j,   <span style="color:#ae81ff">0.9137</span><span style="color:#f92672">+</span><span style="color:#ae81ff">0.0000e+00</span>j],
</span></span><span style="display:flex;"><span>       [  <span style="color:#ae81ff">5.6947</span><span style="color:#f92672">+</span><span style="color:#ae81ff">1.9658e-01</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">0.5213</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2.2622e+00</span>j, <span style="color:#f92672">-</span><span style="color:#ae81ff">10.9693</span><span style="color:#f92672">-</span><span style="color:#ae81ff">4.7503e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">3.6554</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2.2839e-01</span>j],
</span></span><span style="display:flex;"><span>       [  <span style="color:#ae81ff">8.1551</span><span style="color:#f92672">+</span><span style="color:#ae81ff">2.6689e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">5.7527</span><span style="color:#f92672">-</span><span style="color:#ae81ff">4.1722e+00</span>j,   <span style="color:#ae81ff">0.6473</span><span style="color:#f92672">-</span><span style="color:#ae81ff">1.1867e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">4.3752</span><span style="color:#f92672">+</span><span style="color:#ae81ff">5.1961e+00</span>j],
</span></span><span style="display:flex;"><span>       [ <span style="color:#f92672">-</span><span style="color:#ae81ff">1.089</span> <span style="color:#f92672">+</span><span style="color:#ae81ff">2.9802e-08</span>j,   <span style="color:#ae81ff">1.31</span>  <span style="color:#f92672">-</span><span style="color:#ae81ff">1.0136e+01</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">4.7408</span><span style="color:#f92672">-</span><span style="color:#ae81ff">7.9127e-01</span>j,   <span style="color:#ae81ff">8.455</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">3.7253e-08</span>j],
</span></span><span style="display:flex;"><span>       [  <span style="color:#ae81ff">8.1551</span><span style="color:#f92672">-</span><span style="color:#ae81ff">2.6689e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">2.1656</span><span style="color:#f92672">-</span><span style="color:#ae81ff">9.6490e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">3.7517</span><span style="color:#f92672">-</span><span style="color:#ae81ff">1.5745e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">4.3752</span><span style="color:#f92672">-</span><span style="color:#ae81ff">5.1961e+00</span>j],
</span></span><span style="display:flex;"><span>       [  <span style="color:#ae81ff">5.6947</span><span style="color:#f92672">-</span><span style="color:#ae81ff">1.9658e-01</span>j,   <span style="color:#ae81ff">3.4155</span><span style="color:#f92672">-</span><span style="color:#ae81ff">3.9835e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">1.3505</span><span style="color:#f92672">-</span><span style="color:#ae81ff">4.1515e+00</span>j,  <span style="color:#f92672">-</span><span style="color:#ae81ff">3.6554</span><span style="color:#f92672">-</span><span style="color:#ae81ff">2.2839e-01</span>j]], dtype<span style="color:#f92672">=</span>complex64)
</span></span></code></pre></div><p>If you look closely at the original, <em>full</em> output of the 2D FFT, you can separate it into a few block matrix sections that contain the conjugate pairs:</p>
<pre tabindex="0"><code>|A|B|B|B|B|B|
|C|D|D|D|D|D|
|C|D|D|D|D|D|
|C|D|D|D|D|D|
|C|D|D|D|D|D|
|C|D|D|D|D|D|
</code></pre><p>In each block, the conjugate pairs are nicely positioned on what I&rsquo;d like to call <em>opposite ends of a line passing through the block origin</em>. Here are some conjugate pairs you can verify, together with the example output above:</p>
<pre tabindex="0"><code># Block D
|A|B|B|B|B|B|
|C|x| | | | |
|C| | | | | |
|C| | | | | |
|C| | | | | |
|C| | | | |x|

# Block D
|A|B|B|B|B|B|
|C| |x| | | |
|C| | | | | |
|C| | | | | |
|C| | | | | |
|C| | | |x| |

# Block D
|A|B|B|B|B|B|
|C| | | | | |
|C| | | |x| |
|C| | | | | |
|C| |x| | | |
|C| | | | | |

# Block D, self-conjugate i.e. 0 imaginary component
# Note that numerical accuracy sometimes leaves it as a small number instead..
|A|B|B|B|B|B|
|C| | | | | |
|C| | | | | |
|C| | |x| | |
|C| | | | | |
|C| | | | | |

# Block C
|A|B|B|B|B|B|
| |D|D|D|D|D|
|x|D|D|D|D|D|
| |D|D|D|D|D|
|x|D|D|D|D|D|
| |D|D|D|D|D|

# Block C self-conjugate
|A|B|B|B|B|B|
| |D|D|D|D|D|
| |D|D|D|D|D|
|x|D|D|D|D|D|
| |D|D|D|D|D|
| |D|D|D|D|D|
</code></pre><p>Of course, this is just graphically representing the conjugate pair relationship in 2D:</p>
$$
X_{M-k, N-l} = {X_{k,l}}^*
$$<p>The idea is you&rsquo;d need to keep at least one of each conjugate pair to have non-redundant output; and importantly, since the conjugate pairs are in block matrices, you can target to keep half of each block. The way all the libraries do it (probably because they do the transforms down the rows first, then down the columns) is to keep half of block $B$ and all of block $C$, chopping block $D$ vertically in the process.</p>
<pre tabindex="0"><code>|A|B|B|B|-|-|
|C|D|D|D|-|-|
|C|D|D|D|-|-|
|C|D|D|D|-|-|
|C|D|D|D|-|-|
|C|D|D|D|-|-|
</code></pre><p>However you could easily just chop block $D$ horizontally instead, and still keep an equivalent set of non-redundant output:</p>
<pre tabindex="0"><code>|A|B|B|B|B|B|
|C|D|D|D|D|D|
|C|D|D|D|D|D|
|C|D|D|D|D|D|
|-|-|-|-|-|-|
|-|-|-|-|-|-|
</code></pre><h2 id="and-finally-the-ipp-2d-packed-format">And finally, the IPP 2D packed format..</h2>
<p>The <a href="https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-12/real-complex-packed-rcpack2d-format.html">RCPack2D</a> format is found only in the image processing section.</p>
<p>Now this is really something. In theory, this is just extending the 1D packed format idea to 2D; everytime you meet an imaginary component of 0, you skip it (hence the <strong>Packed</strong>), but there&rsquo;s a little more nuance to it.</p>
<p>The first thing to note is that the previous method can still contain redundant values after slicing. In our $6 \times 6$ example above, the final truncated output still has the following redundant pairs:</p>
<pre tabindex="0"><code>|A|B|B|B|-|-|
|x|D|D|D|-|-|
| |D|D|D|-|-|
| |D|D|D|-|-|
| |D|D|D|-|-|
|x|D|D|D|-|-|

|A|B|B|B|-|-|
| |D|D|D|-|-|
|x|D|D|D|-|-|
| |D|D|D|-|-|
|x|D|D|D|-|-|
| |D|D|D|-|-|

|A|B|B|B|-|-|
|C| | |x|-|-|
|C| | | |-|-|
|C| | | |-|-|
|C| | | |-|-|
|C| | |x|-|-|

|A|B|B|B|-|-|
|C| | | |-|-|
|C| | |x|-|-|
|C| | | |-|-|
|C| | |x|-|-|
|C| | | |-|-|
</code></pre><p>I didn&rsquo;t include the self-conjugate centre indices in blocks $B,C,D$ as they should be treated the same as the 1D case.</p>
<p>So there can be a <em>shorter</em> first and last column. Hopefully, it shouldn&rsquo;t be too hard to see that for an odd number of columns, the last column will not have this issue.</p>
<p>Okay, so let&rsquo;s get rid of the extra redundant indices,</p>
<pre tabindex="0"><code>|A|B|B|B|-|-|
|C|D|D|D|-|-|
|C|D|D|D|-|-|
|C|D|D|D|-|-|
|-|D|D|-|-|-|
|-|D|D|-|-|-|
</code></pre><p>and then let&rsquo;s label the fully real \(R\) and fully complex (*) indices:</p>
<pre tabindex="0"><code>|R|*|*|R|-|-|
|*|*|*|*|-|-|
|*|*|*|*|-|-|
|R|*|*|R|-|-|
|-|*|*|-|-|-|
|-|*|*|-|-|-|
</code></pre><p>Here&rsquo;s where IPP&rsquo;s packed format comes in:</p>
<ol>
<li>The first (and in this case also the last) column is flattened to real values <em>into a column</em>. This becomes a length-6 column - 2 from reals and $4=2 \times 2$ from complex values - and is written into the first (and last) column of a $6 \times 6$ matrix.</li>
<li>The columns in between, which are flattened to real values <em>into their respective rows</em>. This will become - in this case - 6 rows and 4 columns of real values.</li>
</ol>
<p>Hence, the final output becomes a $6 \times 6$ real-valued matrix, with no redundant values whatsoever.</p>
<h2 id="sounds-like-a-lot-of-work-for-not-a-lot-of-gain">Sounds like a lot of work for not a lot of gain?</h2>
<p>I think so too. By the logic above, you&rsquo;d save roughly $2M/2 = M$ values worth of memory for a general $M \times N$ matrix. Even worse, if someone had to operate on this other than IPP, you&rsquo;d have to unfold this anyway, or have to remember to deal with these first/last column shenanigans.</p>
]]></content></item><item><title>CRTP, method chaining, and static polymorphism</title><link>https://icyveins7.github.io/posts/2024/05/crtp-method-chaining-and-static-polymorphism/</link><pubDate>Sat, 04 May 2024 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2024/05/crtp-method-chaining-and-static-polymorphism/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Yes, it&amp;rsquo;s yet another blogpost about CRTP and how it&amp;rsquo;d be useful..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="the-1st-issue-method-chaining"&gt;The 1st issue: method chaining&lt;/h1&gt;
&lt;p&gt;Have you seen a billion other blogposts about CRTP? Yes, so have I. But maybe there&amp;rsquo;s a reason for all of them; it wasn&amp;rsquo;t really apparent when reading them previously why it would be useful and/or why I would ever need it.&lt;/p&gt;
&lt;p&gt;But recently, while writing some simple templated code for &lt;a href="https://github.com/icyveins7/ufl"&gt;&lt;code&gt;ufl&lt;/code&gt;&lt;/a&gt;, I had the bright idea of trying to make method chaining possible for the class. That&amp;rsquo;s when my templated class and its derived friend implementation started to fall apart. I fixed this by using CRTP for the first time.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Yes, it&rsquo;s yet another blogpost about CRTP and how it&rsquo;d be useful..</p>
</blockquote>
<h1 id="the-1st-issue-method-chaining">The 1st issue: method chaining</h1>
<p>Have you seen a billion other blogposts about CRTP? Yes, so have I. But maybe there&rsquo;s a reason for all of them; it wasn&rsquo;t really apparent when reading them previously why it would be useful and/or why I would ever need it.</p>
<p>But recently, while writing some simple templated code for <a href="https://github.com/icyveins7/ufl"><code>ufl</code></a>, I had the bright idea of trying to make method chaining possible for the class. That&rsquo;s when my templated class and its derived friend implementation started to fall apart. I fixed this by using CRTP for the first time.</p>
<p>I think <a href="https://dariusgrant.github.io/2021/02/11/Method-Chaining-Base-Class-Methods-With-Derived-Classes.html">this post</a> is probably the best explanation for how CRTP solves this problem, so I&rsquo;m not going to repeat it here, but rather leave a link (for myself). Bonus points for not having ads all over the place on that page.</p>
<h1 id="the-2nd-issue-static-polymorphism">The 2nd issue: static polymorphism</h1>
<p>I remember stumbling on a <a href="https://www.youtube.com/watch?v=NH1Tta7purM">video</a> that removed virtual functions with templates a few years back. At the time, I didn&rsquo;t really grasp the concept very well (<em>is this what they call experience?</em>), but it&rsquo;s starting to dawn on me I think.</p>
<p>While writing some code for <a href="https://github.com/icyveins7/uhdeb"><code>uhdeb</code></a>, I tried to wrap 2 similar but related streamer object classes in a container. I wanted a parent class that would hold a different type of streamer object - a <code>tx_streamer</code> or a <code>rx_streamer</code> - and to perform this initialization in the constructor (using another common object type that was passed in).</p>
<p>My first thought was to define a parent templated class that contained a <code>T m_stream</code>. But now I had 2 choices:</p>
<ol>
<li>I write a custom constructor for each of the derived classes. This would call the appropriate initialization for each type of <code>m_stream</code>. But I wanted the constructor to also do similar things for both derived classes - allocate some memory, start a thread - and so I would have had to copy all those calls, violating DRY.</li>
<li>Do some virtual calls.</li>
</ol>
<p>The code (roughly speaking) looked like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-c++" data-lang="c++"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> T<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Parent</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    T m_stream;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">DerivedRX</span> <span style="color:#f92672">:</span> Parent<span style="color:#f92672">&lt;</span>RXStreamer<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    DerivedRX(...) <span style="color:#f92672">:</span> Parent(...)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// create the RXStreamer..
</span></span></span><span style="display:flex;"><span>        ...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// the rest depend on the streamer, so I couldn&#39;t throw it into Parent&#39;s constructor
</span></span></span><span style="display:flex;"><span>        allocate();
</span></span><span style="display:flex;"><span>        start_thread();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">DerivedTX</span> <span style="color:#f92672">:</span> Parent<span style="color:#f92672">&lt;</span>TXStreamer<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    DerivedTX(...) <span style="color:#f92672">:</span> Parent(...)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// create the TXStreamer..
</span></span></span><span style="display:flex;"><span>        ...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// the rest depend on the streamer, so I couldn&#39;t throw it into Parent&#39;s constructor
</span></span></span><span style="display:flex;"><span>        allocate();
</span></span><span style="display:flex;"><span>        start_thread();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>Could I have refactored the code to not have this problem? Probably. But at the time I was adamant on finding a way of getting this to work.</p>
<p>The way I did it was to push all the &lsquo;standard&rsquo; constructor logic to the parent class. The parent class would call the appropriate derived class&rsquo;s <code>create_stream</code> method through CRTP:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-c++" data-lang="c++"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> T, <span style="color:#66d9ef">typename</span> U<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Parent</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    Parent()
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>U<span style="color:#f92672">*&gt;</span>(<span style="color:#66d9ef">this</span>)<span style="color:#f92672">-&gt;</span>create_stream();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// the rest of the ctor..
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    T m_stream;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">DerivedRX</span> <span style="color:#f92672">:</span> Parent<span style="color:#f92672">&lt;</span>RXStreamer, DerivedRX<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    DerivedRX(...) <span style="color:#f92672">:</span> Parent(...)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// nothing else needs to be done..
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">create_stream</span>()
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// custom RX code..
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">DerivedTX</span> <span style="color:#f92672">:</span> Parent<span style="color:#f92672">&lt;</span>TXStreamer, DerivedTX<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    DerivedTX(...) <span style="color:#f92672">:</span> Parent(...)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// nothing else needs to be done
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">create_stream</span>()
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// custom TX code..
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>This avoids any use of <code>virtual</code> methods - <em>no vtables here hoho</em> - and the correct derived method is called for each derived class!</p>
<p>Oh, I also had to add a <code>friend class</code> declaration within the derived class definitions in order for it to work.</p>
<p>Now, if you look at this and are thinking: couldn&rsquo;t he have just moved the custom streamer types back into the derived class definitions? Then there would be no need for any of these shenanigans, and there would just be simple parent and derived classes. And you would be right.</p>
<p>But like I said, I was exploring, and it was interesting.</p>
<p>Going to leave some more references here for myself:</p>
<ol>
<li><a href="https://www.fluentcpp.com/2017/05/16/what-the-crtp-brings-to-code/">https://www.fluentcpp.com/2017/05/16/what-the-crtp-brings-to-code/</a></li>
<li><a href="https://eli.thegreenplace.net/2013/12/05/the-cost-of-dynamic-virtual-calls-vs-static-crtp-dispatch-in-c">https://eli.thegreenplace.net/2013/12/05/the-cost-of-dynamic-virtual-calls-vs-static-crtp-dispatch-in-c</a></li>
</ol>
<p>I&rsquo;m sure some day soon I&rsquo;ll find a stronger use-case for this. I never really liked doing <code>virtual</code> methods after all.</p>
]]></content></item><item><title>Some heuristic proofs for cyclostationary methods</title><link>https://icyveins7.github.io/posts/2024/04/some-heuristic-proofs-for-cyclostationary-methods/</link><pubDate>Tue, 16 Apr 2024 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2024/04/some-heuristic-proofs-for-cyclostationary-methods/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Carrier offset and baud rate estimation can be done blindly using cyclostationary (cyclic moment) methods, but why?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;In &lt;a href="https://github.com/icyveins7/reimage"&gt;ReImage&lt;/a&gt;, there are options to blindly estimate a signal&amp;rsquo;s baud rate or residual carrier offset. These require some working knowledge, and only work on some types of modulations like PSK. But why do they work, and how do we explain the peaks we see in the resulting spectra?&lt;/p&gt;
&lt;p&gt;First, some terminology. Here I&amp;rsquo;ll often mention the exponents in a &lt;em&gt;CM&lt;/em&gt; XY form: this refers to applying an exponent onto a signal with a total of $X$, where $Y$ of that is the conjugate. These refer to the cyclic moments.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Carrier offset and baud rate estimation can be done blindly using cyclostationary (cyclic moment) methods, but why?</p>
</blockquote>
<h1 id="introduction">Introduction</h1>
<p>In <a href="https://github.com/icyveins7/reimage">ReImage</a>, there are options to blindly estimate a signal&rsquo;s baud rate or residual carrier offset. These require some working knowledge, and only work on some types of modulations like PSK. But why do they work, and how do we explain the peaks we see in the resulting spectra?</p>
<p>First, some terminology. Here I&rsquo;ll often mention the exponents in a <em>CM</em> XY form: this refers to applying an exponent onto a signal with a total of $X$, where $Y$ of that is the conjugate. These refer to the cyclic moments.</p>
<p>For example, <em>CM</em> 20 refers to $x^2$ whereas <em>CM</em> 21 refers to $x x^*$.</p>
<p>To see a proper treatment, refer to <a href="https://cyclostationary.blog/2019/04/26/simple-synchronization-using-csp/">the man himself</a>. Otherwise, onwards we go..</p>
<h1 id="carrier-offset-estimation">Carrier offset estimation</h1>
<p>Let&rsquo;s consider a generic QPSK signal given by:</p>
$$
s(t) = \left[ \sum_k m_k (\delta(t - kT) \star h(t)) \right] e^{i 2 \pi f_c t}
$$<p>where the symbols are given by</p>
$$
m_k \in \{1, i, -1, -i\}
$$<p>The baud period here is specified by $T$ and the carrier offset to be estimated is given by $f_c$.</p>
<p>The correct thing to do here is to find some values of exponents where the result contains <em>additive tones</em>. These will show up in a specturm of FFT plot as peaks that can be easily measured.</p>
<p>We claim now the required operation is <em>CM</em> 40 i.e.</p>
$$
s(t) \rightarrow s^4 (t)
$$<p>Let&rsquo;s see what this does to the signal. First, observe that the exponent applied directly to the message symbols always results in 1:</p>
$$
{m_k}^4 = 1 \, \text{for all } m_k
$$<p>What happens when we first try to square the terms in the square brackets?</p>
<p>There are 2 terms at play here: intra-term products and inter-term products.</p>
<p>Let&rsquo;s first consider the inter-term products:</p>
$$
m_k m_{k'} (\delta(t - kT) \star h(t)) (\delta(t - k'T) \star h(t))
$$<p>It shouldn&rsquo;t be too difficult to see that this should tend to 0, since for a random variable $m_k$, the expectation value of $m_k m_{k'}$ should approach 0. Moreover, most reasonable time-limited pulse shapes $h(t)$ only have non-negligible support within 1 symbol i.e. within $T$ of each other. Hence, the contribution of non-zero products is also limited.</p>
<p>Hence we turn our attention to the intra-term products:</p>
$$
{m_k}^2 (\delta(t-kT) \star h(t))^2
$$<p>We square again to get our exponent of 4, obtaining</p>
$$
{m_k}^4 (\delta(t-kT) \star h(t))^4 = (\delta(t-kT) \star h(t))^4
$$<p>where we have substituted ${m_k}^4 = 1$.</p>
<p>What can we say about this final expression? Let&rsquo;s bring the summation back into the picture:</p>
$$
\sum_k (\delta(t-kT) \star h(t))^4
$$<p>We start the analysis by considering what would happen if we used a $\delta$ function as a pulse:</p>
$$
\sum_k (\delta(t-kT) \star h(t))^4 \rightarrow \sum_k (\delta(t-kT))^4 = \sum_k \delta(t-kT)
$$<p>Well the Fourier transform of this Dirac comb is simply another Dirac comb (<a href="https://dspillustrations.com/pages/posts/misc/the-dirac-comb-and-its-fourier-transform.html">see this</a>):</p>
$$
\mathcal{F}\left[\sum_k \delta(t-kT)\right] = \sum_k \delta\left(f - \frac{k}{T}\right)
$$<p>Let&rsquo;s put this together with the other parts of $s(t)$ to get</p>
$$
\begin{align}
\mathcal{F}\left[s(t)^4\right] &\approx \sum_k \delta\left(f - \frac{k}{T}\right) \star \delta(f - 4f_c)\\
&= \sum_k \delta(f - 4f_c - \frac{k}{T} )
\end{align}
$$<p>where we have left out any inter-term products and also the Fourier transform of ${m_k}^4$.</p>
<p>What does this suggest to us? The spectrum consists of components centred around $f = 4f_c$, with steps of $\pm1/T$, otherwise known as the baud rate.</p>
<p>But here we assumed the infinitesimal pulse shape $\delta$; what if we revert to a more practical pulse shape like an RRC? Heuristically, we need only consider the properties of Fourier transforms: the more support it has in time, the less support its transform has in frequency. Hence, we expect to have the Dirac comb in frequency space be multiplied (since we convolved with $h(t)$) with a pulse of some <em>bandwidth</em>.</p>
$$
\begin{align}
\sum_k (\delta(t-kT) \star h(t))^4 &= \sum_k h^4(t-kT) \\
&= \sum_k \delta(t - kT) \star h^4(t)
\end{align}
$$$$
\begin{align}
\mathcal{F}\left[ \sum_k \delta(t-kT) \star h^4(t) \right] &= \sum_k  \delta(f - \frac{k}{T}) \times \mathcal{F} \left[ h^4 (t) \right] \\
&= \sum_k  \delta(f - \frac{k}{T}) \times H(f)
\end{align}
$$<p>That is, the amplitude of the Dirac comb in frequency space has an <em>envelope</em> defined by the Fourier transform of $h^4 (t) = H(f)$. Together with the other $\delta$-function, this shifts the entire enveloped Dirac comb to centre around $4f_c$.</p>
<p>Hence, we expect to see a large peak in the spectrum at $4f_c$, with smaller peaks uniformly at $1/T$ spacing.</p>
]]></content></item><item><title>Getting rid of clangd's errors on Windows</title><link>https://icyveins7.github.io/posts/2024/03/getting-rid-of-clangds-errors-on-windows/</link><pubDate>Tue, 26 Mar 2024 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2024/03/getting-rid-of-clangds-errors-on-windows/</guid><description>&lt;blockquote&gt;
&lt;p&gt;My first steps into migrating to neovim, some clangd problems and my solutions..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="kickstartnvim-brought-me-here"&gt;Kickstart.nvim brought me here&lt;/h1&gt;
&lt;p&gt;I started using vim motions in VSCode a month or two ago, and decided to actually try to see if neovim would work for me. Honestly, I probably wouldn&amp;rsquo;t have gone down this path if &lt;a href="https://github.com/nvim-lua/kickstart.nvim"&gt;kickstart.nvim&lt;/a&gt; didn&amp;rsquo;t exist. But it does, and I tried it, and it looks like I&amp;rsquo;m here to stay.&lt;/p&gt;
&lt;h1 id="clangd-as-my-first-lsp"&gt;clangd as my first LSP&lt;/h1&gt;
&lt;p&gt;In kickstart.nvim&amp;rsquo;s template, &lt;code&gt;clangd&lt;/code&gt; is listed as an example LSP, so I decided to uncomment that line and try it out. I opened my &lt;a href="https://github.com/icyveins7/ffs"&gt;ffs&lt;/a&gt; repository at the time, and was immediately greeted with a flood of warnings on my code. This brings us to the first problem.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>My first steps into migrating to neovim, some clangd problems and my solutions..</p>
</blockquote>
<h1 id="kickstartnvim-brought-me-here">Kickstart.nvim brought me here</h1>
<p>I started using vim motions in VSCode a month or two ago, and decided to actually try to see if neovim would work for me. Honestly, I probably wouldn&rsquo;t have gone down this path if <a href="https://github.com/nvim-lua/kickstart.nvim">kickstart.nvim</a> didn&rsquo;t exist. But it does, and I tried it, and it looks like I&rsquo;m here to stay.</p>
<h1 id="clangd-as-my-first-lsp">clangd as my first LSP</h1>
<p>In kickstart.nvim&rsquo;s template, <code>clangd</code> is listed as an example LSP, so I decided to uncomment that line and try it out. I opened my <a href="https://github.com/icyveins7/ffs">ffs</a> repository at the time, and was immediately greeted with a flood of warnings on my code. This brings us to the first problem.</p>
<h1 id="header-resolution-with-clangd">Header Resolution with clangd</h1>
<p>My code was immediately flooded with warnings, to the point that <code>clangd</code> itself said it would stop reporting errors. A lot of them looked like this:</p>
<p><img src="/static/images/clangd-warnings.png" alt="clangd-warnings"></p>
<p>It seemed like most of them were related to header resolution; I had seen a similar type of error/warning in VSCode before, which always seemed to magically disappear after running <code>cmake</code> the first time.</p>
<p>Solution? Generate a <code>compile_commands.json</code> <a href="https://clangd.llvm.org/design/compile-commands">(see this)</a>. In particular, I needed to make sure this was <em>near the root of the directory</em>. In my original setup, I had separate <code>CMakeLists.txt</code> files in my <code>examples/</code> and <code>tests/</code> subdirectories, and would manually enter each subdirectory when I needed to do stuff there. However, this meant that when opening my <code>include/</code> subdirectory&rsquo;s files, I would be greeted with similar <code>clangd</code> errors, as it wouldn&rsquo;t see the <code>compile_commands.json</code> generated from running <code>cmake</code> for the prior folders.</p>
<p>So I needed to write an outermost <code>CMakeLists.txt</code>, to ensure that the <code>compile_commands.json</code> appeared in the <code>build/</code> subdirectory at the top. Enabling this explicitly is trivial by just placing this line in that top-level <code>CMakeLists.txt</code>:</p>
<pre tabindex="0"><code>set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
</code></pre><h1 id="compile-commands-dont-work-with-msvc">Compile commands don&rsquo;t work with MSVC</h1>
<p>The second problem is related to the first. As it turns out, <a href="https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html">setting the above does absolutely nothing when using MSVC</a>, which I was using since I was in Windows. I didn&rsquo;t want to use MinGW-gcc here, so I thought I&rsquo;d try the other generator in the link: <a href="https://github.com/ninja-build/ninja">Ninja</a>.</p>
<p>After installing and calling <code>cmake .. -G Ninja</code>, the <code>compile_commands.json</code> was generated and everything worked as they should.</p>
<h1 id="other-ninja-benefits">Other Ninja Benefits</h1>
<ol>
<li>I always didn&rsquo;t like how MSVC <em>had</em> to be different by pushing the build type configuration to after the <code>cmake</code> call. Using <code>ninja</code> lets me have a more symmetric experience while retaining the use of MSVC, as now I can always specify <code>-DCMAKE_BUILD_TYPE</code> at the <code>cmake</code> step regardless of whether I&rsquo;m on unix or Windows.</li>
<li>I get to <code>make</code> things by <code>ninja</code> and <code>make clean</code> by <code>ninja clean</code>. This is pretty satisfying.</li>
<li>Having <code>compile_commands.json</code> means I can see the literal command for every executable, rather than having to dig inside the <code>.vcxproj</code> to double-check every option that was enabled; previously I would just wait til compile time to see the command line arguments the project file would use.</li>
</ol>
]]></content></item><item><title>Clang and Eigen's alternatives to complex multiplication SIMD</title><link>https://icyveins7.github.io/posts/2024/03/clang-and-eigens-alternatives-to-complex-multiplication-simd/</link><pubDate>Mon, 11 Mar 2024 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2024/03/clang-and-eigens-alternatives-to-complex-multiplication-simd/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Clang isn&amp;rsquo;t much better than MSVC for complex number multiplication, while Eigen is equivalent to GCC but uses slightly different instructions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="complex-number-multiplication-is-probably-not-common-enough"&gt;Complex Number Multiplication Is Probably Not Common Enough&lt;/h1&gt;
&lt;p&gt;I think I might have bashed MSVC too much on the last post; trying to vectorise our simple 4-element complex number multiplication using clang with AVX instructions delivers &lt;a href="https://godbolt.org/z/35dGqTfKv"&gt;similarly poor results&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#include &amp;lt;complex&amp;gt;
void cmul(
const std::complex&amp;lt;float&amp;gt;* __restrict__ x,
const std::complex&amp;lt;float&amp;gt;* __restrict__ y,
std::complex&amp;lt;float&amp;gt;* __restrict__ z
){
for (int i = 0; i &amp;lt; 4; ++i)
z[i] = x[i] * y[i];
}
void mul(
const float* __restrict__ x,
const float* __restrict__ y,
float* __restrict__ z
){
for (int i = 0; i &amp;lt; 8; ++i)
z[i] = x[i] * y[i];
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;cmul(std::complex&amp;lt;float&amp;gt; const*, std::complex&amp;lt;float&amp;gt; const*, std::complex&amp;lt;float&amp;gt;*): # @cmul(std::complex&amp;lt;float&amp;gt; const*, std::complex&amp;lt;float&amp;gt; const*, std::complex&amp;lt;float&amp;gt;*)
vmovsd xmm0, qword ptr [rdi] # xmm0 = mem[0],zero
vmovsd xmm1, qword ptr [rsi] # xmm1 = mem[0],zero
vbroadcastss xmm2, xmm0
vmovshdup xmm0, xmm0 # xmm0 = xmm0[1,1,3,3]
vshufps xmm3, xmm1, xmm1, 225 # xmm3 = xmm1[1,0,2,3]
vmulps xmm0, xmm3, xmm0
vfmaddsub231ps xmm0, xmm1, xmm2 # xmm0 = (xmm1 * xmm2) +/- xmm0
vmovlps qword ptr [rdx], xmm0
vmovsd xmm0, qword ptr [rdi + 8] # xmm0 = mem[0],zero
vmovsd xmm1, qword ptr [rsi + 8] # xmm1 = mem[0],zero
vbroadcastss xmm2, xmm0
vmovshdup xmm0, xmm0 # xmm0 = xmm0[1,1,3,3]
vshufps xmm3, xmm1, xmm1, 225 # xmm3 = xmm1[1,0,2,3]
vmulps xmm0, xmm3, xmm0
vfmaddsub231ps xmm0, xmm1, xmm2 # xmm0 = (xmm1 * xmm2) +/- xmm0
vmovlps qword ptr [rdx + 8], xmm0
vmovsd xmm0, qword ptr [rdi + 16] # xmm0 = mem[0],zero
vmovsd xmm1, qword ptr [rsi + 16] # xmm1 = mem[0],zero
vbroadcastss xmm2, xmm0
vmovshdup xmm0, xmm0 # xmm0 = xmm0[1,1,3,3]
vshufps xmm3, xmm1, xmm1, 225 # xmm3 = xmm1[1,0,2,3]
vmulps xmm0, xmm3, xmm0
vfmaddsub231ps xmm0, xmm1, xmm2 # xmm0 = (xmm1 * xmm2) +/- xmm0
vmovlps qword ptr [rdx + 16], xmm0
vmovsd xmm0, qword ptr [rdi + 24] # xmm0 = mem[0],zero
vmovsd xmm1, qword ptr [rsi + 24] # xmm1 = mem[0],zero
vbroadcastss xmm2, xmm0
vmovshdup xmm0, xmm0 # xmm0 = xmm0[1,1,3,3]
vshufps xmm3, xmm1, xmm1, 225 # xmm3 = xmm1[1,0,2,3]
vmulps xmm0, xmm3, xmm0
vfmaddsub231ps xmm0, xmm1, xmm2 # xmm0 = (xmm1 * xmm2) +/- xmm0
vmovlps qword ptr [rdx + 24], xmm0
ret
mul(float const*, float const*, float*): # @mul(float const*, float const*, float*)
vmovups ymm0, ymmword ptr [rsi]
vmulps ymm0, ymm0, ymmword ptr [rdi]
vmovups ymmword ptr [rdx], ymm0
vzeroupper
ret
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compiled with clang 18.1.0, with &lt;code&gt;-O3 -ffast-math -march=x86-64-v3&lt;/code&gt;, this still refuses to vectorise the &lt;code&gt;cmul&lt;/code&gt; function correctly, only using the &lt;code&gt;xmm&lt;/code&gt; registers. I included the normal real-valued float multiplication to check that clang is indeed able to vectorise that. Note that you still need the &lt;code&gt;__restrict__&lt;/code&gt; keywords for the vectorisation to work. Using &lt;code&gt;-ffast-math&lt;/code&gt; doesn&amp;rsquo;t seem to do anything for us in the real float vectorisation, but it does make the complex-valued vectorisation less verbose.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Clang isn&rsquo;t much better than MSVC for complex number multiplication, while Eigen is equivalent to GCC but uses slightly different instructions.</p>
</blockquote>
<h1 id="complex-number-multiplication-is-probably-not-common-enough">Complex Number Multiplication Is Probably Not Common Enough</h1>
<p>I think I might have bashed MSVC too much on the last post; trying to vectorise our simple 4-element complex number multiplication using clang with AVX instructions delivers <a href="https://godbolt.org/z/35dGqTfKv">similarly poor results</a>:</p>
<pre tabindex="0"><code>#include &lt;complex&gt;

void cmul(
    const std::complex&lt;float&gt;* __restrict__ x,
    const std::complex&lt;float&gt;* __restrict__ y,
    std::complex&lt;float&gt;* __restrict__ z
){
    for (int i = 0; i &lt; 4; ++i)
        z[i] = x[i] * y[i];
}

void mul(
    const float* __restrict__ x,
    const float* __restrict__ y,
    float* __restrict__ z
){
    for (int i = 0; i &lt; 8; ++i)
        z[i] = x[i] * y[i];
}
</code></pre><pre tabindex="0"><code>cmul(std::complex&lt;float&gt; const*, std::complex&lt;float&gt; const*, std::complex&lt;float&gt;*):          # @cmul(std::complex&lt;float&gt; const*, std::complex&lt;float&gt; const*, std::complex&lt;float&gt;*)
        vmovsd  xmm0, qword ptr [rdi]           # xmm0 = mem[0],zero
        vmovsd  xmm1, qword ptr [rsi]           # xmm1 = mem[0],zero
        vbroadcastss    xmm2, xmm0
        vmovshdup       xmm0, xmm0              # xmm0 = xmm0[1,1,3,3]
        vshufps xmm3, xmm1, xmm1, 225           # xmm3 = xmm1[1,0,2,3]
        vmulps  xmm0, xmm3, xmm0
        vfmaddsub231ps  xmm0, xmm1, xmm2        # xmm0 = (xmm1 * xmm2) +/- xmm0
        vmovlps qword ptr [rdx], xmm0
        vmovsd  xmm0, qword ptr [rdi + 8]       # xmm0 = mem[0],zero
        vmovsd  xmm1, qword ptr [rsi + 8]       # xmm1 = mem[0],zero
        vbroadcastss    xmm2, xmm0
        vmovshdup       xmm0, xmm0              # xmm0 = xmm0[1,1,3,3]
        vshufps xmm3, xmm1, xmm1, 225           # xmm3 = xmm1[1,0,2,3]
        vmulps  xmm0, xmm3, xmm0
        vfmaddsub231ps  xmm0, xmm1, xmm2        # xmm0 = (xmm1 * xmm2) +/- xmm0
        vmovlps qword ptr [rdx + 8], xmm0
        vmovsd  xmm0, qword ptr [rdi + 16]      # xmm0 = mem[0],zero
        vmovsd  xmm1, qword ptr [rsi + 16]      # xmm1 = mem[0],zero
        vbroadcastss    xmm2, xmm0
        vmovshdup       xmm0, xmm0              # xmm0 = xmm0[1,1,3,3]
        vshufps xmm3, xmm1, xmm1, 225           # xmm3 = xmm1[1,0,2,3]
        vmulps  xmm0, xmm3, xmm0
        vfmaddsub231ps  xmm0, xmm1, xmm2        # xmm0 = (xmm1 * xmm2) +/- xmm0
        vmovlps qword ptr [rdx + 16], xmm0
        vmovsd  xmm0, qword ptr [rdi + 24]      # xmm0 = mem[0],zero
        vmovsd  xmm1, qword ptr [rsi + 24]      # xmm1 = mem[0],zero
        vbroadcastss    xmm2, xmm0
        vmovshdup       xmm0, xmm0              # xmm0 = xmm0[1,1,3,3]
        vshufps xmm3, xmm1, xmm1, 225           # xmm3 = xmm1[1,0,2,3]
        vmulps  xmm0, xmm3, xmm0
        vfmaddsub231ps  xmm0, xmm1, xmm2        # xmm0 = (xmm1 * xmm2) +/- xmm0
        vmovlps qword ptr [rdx + 24], xmm0
        ret
mul(float const*, float const*, float*):                         # @mul(float const*, float const*, float*)
        vmovups ymm0, ymmword ptr [rsi]
        vmulps  ymm0, ymm0, ymmword ptr [rdi]
        vmovups ymmword ptr [rdx], ymm0
        vzeroupper
        ret
</code></pre><p>Compiled with clang 18.1.0, with <code>-O3 -ffast-math -march=x86-64-v3</code>, this still refuses to vectorise the <code>cmul</code> function correctly, only using the <code>xmm</code> registers. I included the normal real-valued float multiplication to check that clang is indeed able to vectorise that. Note that you still need the <code>__restrict__</code> keywords for the vectorisation to work. Using <code>-ffast-math</code> doesn&rsquo;t seem to do anything for us in the real float vectorisation, but it does make the complex-valued vectorisation less verbose.</p>
<p>There&rsquo;s probably not enough complex number math code out there in the wild, and so only the most used compilers - like GCC - actually have decent vectorisation when it comes to this.</p>
<h1 id="how-does-eigen-do-it">How does Eigen do it?</h1>
<p>Since we&rsquo;re on the topic, I decided to try and see what Eigen&rsquo;s implementation looks like. After all - and I just discovered yet another amazing feature here - godbolt.org actually allows you to include popular libraries, right there in the online interface!</p>
<p>So we have the <a href="https://godbolt.org/z/rcPn8Wf9a">following, compiled with GCC</a>:</p>
<pre tabindex="0"><code>#include &lt;Eigen/Dense&gt;
#include &lt;complex&gt;


void eigCmul4(
    const Eigen::Array4cf&amp; x,
    const Eigen::Array4cf&amp; y,
    Eigen::Array4cf &amp;z
){
    z = x * y;
}

void cmul4(
    const std::complex&lt;float&gt; * __restrict__ x,
    const std::complex&lt;float&gt; * __restrict__ y,
    std::complex&lt;float&gt; * __restrict__ z
){
    for (int i = 0; i &lt; 4; ++i)
        z[i] = x[i] * y[i];
}
</code></pre><p>We use <code>Array</code> here instead of <code>Vector</code> or <code>Matrix</code> since we want the element-wise products.</p>
<p>The assembly looks like this:</p>
<pre tabindex="0"><code>eigCmul4(Eigen::Array&lt;std::complex&lt;float&gt;, 4, 1, 0, 4, 1&gt; const&amp;, Eigen::Array&lt;std::complex&lt;float&gt;, 4, 1, 0, 4, 1&gt; const&amp;, Eigen::Array&lt;std::complex&lt;float&gt;, 4, 1, 0, 4, 1&gt;&amp;):
        vmovaps ymm1, YMMWORD PTR [rdi]
        vmovaps ymm0, YMMWORD PTR [rsi]
        vmovsldup       ymm3, ymm1
        vmovshdup       ymm1, ymm1
        vpermilps       ymm2, ymm0, 177
        vmulps  ymm1, ymm2, ymm1
        vmulps  ymm0, ymm0, ymm3
        vaddsubps       ymm0, ymm0, ymm1
        vmovaps YMMWORD PTR [rdx], ymm0
        vzeroupper
        ret

cmul4(std::complex&lt;float&gt; const*, std::complex&lt;float&gt; const*, std::complex&lt;float&gt;*):
        vmovups ymm0, YMMWORD PTR [rdi]
        vmovups ymm3, YMMWORD PTR [rsi]
        vpermilps       ymm1, ymm0, 160
        vpermilps       ymm2, ymm3, 177
        vpermilps       ymm0, ymm0, 245
        vmulps  ymm0, ymm2, ymm0
        vfmaddsub231ps  ymm0, ymm1, ymm3
        vmovups YMMWORD PTR [rdx], ymm0
        vzeroupper
        ret
</code></pre><p>Aside from the aligned vs non-aligned <code>mov</code> instructions, the main difference is that Eigen uses <code>vmovsldup</code> and <code>vmovshdup</code> instead of <code>vpermilps</code> for two of the instructions. However, it turns out this choice is insignificant, as the same number of cycles are used for permute and shuffle:</p>
<pre tabindex="0"><code>Timeline view:
                    012
Index     0123456789   

[0,0]     DeER .    . .   vmovsldup     ymm3, ymm1
[0,1]     DeER .    . .   vmovshdup     ymm1, ymm1
[0,2]     D=eER.    . .   vpermilps     ymm2, ymm0, 177
[0,3]     D==eeeeER . .   vmulps        ymm1, ymm2, ymm1
[0,4]     D=eeeeE-R . .   vmulps        ymm0, ymm0, ymm3
[0,5]     D======eeeeER   vaddsubps     ymm0, ymm0, ymm1

Timeline view:
                    01
Index     0123456789  

[0,0]     DeER .    ..   vpermilps      ymm1, ymm0, 160
[0,1]     DeER .    ..   vpermilps      ymm2, ymm3, 177
[0,2]     D=eER.    ..   vpermilps      ymm0, ymm0, 245
[0,3]     D==eeeER  ..   vmulps ymm0, ymm2, ymm0
[0,4]     D=====eeeeER   vfmaddsub231ps ymm0, ymm1, ymm3
</code></pre><h1 id="fma-instructions-and-using--marchx86-64-v3">FMA instructions and using <code>-march=x86-64-v3</code></h1>
<p>If you noticed, the GCC code this time is slightly different from the last post. That&rsquo;s because I discovered that it&rsquo;s a lot easier to specify the entire instruction <em>set</em> using <code>-march=x86-64-v3</code>, which includes all of the following, taken from the <a href="https://en.wikipedia.org/wiki/X86-64">wikipedia page</a>: AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, OSXSAVE.</p>
<p>Honestly, don&rsquo;t know what half of them are right now, but hey, FMA and AVX are both in there and are relevant to what I do.</p>
<p>Surprisingly though, it seems like there&rsquo;s very little real difference in terms of number of cycles (only 1 less, since we have to wait for the previous <code>mul</code> instructions to finish), so I guess the benefit for this case is primarily in higher precision using FMA.</p>
]]></content></item><item><title>MSVC's terrible auto-vectoriser for AVX</title><link>https://icyveins7.github.io/posts/2024/02/msvcs-terrible-auto-vectoriser-for-avx/</link><pubDate>Sat, 24 Feb 2024 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2024/02/msvcs-terrible-auto-vectoriser-for-avx/</guid><description>&lt;blockquote&gt;
&lt;p&gt;MSVC has extremely lackluster auto-vectorisation, so I handrolled intrinsic calls by backtranslating GCC&amp;rsquo;s output.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="the-motivation"&gt;The Motivation&lt;/h1&gt;
&lt;p&gt;I recently decided I wanted to spend some time understanding intrinsics and SIMD at a deeper level.&lt;/p&gt;
&lt;p&gt;In developing code for my project &lt;a href="https://github.com/icyveins7/ffs"&gt;&lt;code&gt;ffs&lt;/code&gt;&lt;/a&gt;, I wanted to make sure that the code was running with at least AVX instructions (because that&amp;rsquo;s my target architecture, and honestly very few computers don&amp;rsquo;t have AVX these days..).&lt;/p&gt;
&lt;p&gt;This led me down a path of discovery; first I discovered the amazing-ness that is &lt;a href="https://godbolt.org"&gt;godbolt.org&lt;/a&gt;, then I joined their discord, where I then asked for some help with understanding basic .asm compiler output.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>MSVC has extremely lackluster auto-vectorisation, so I handrolled intrinsic calls by backtranslating GCC&rsquo;s output.</p>
</blockquote>
<h1 id="the-motivation">The Motivation</h1>
<p>I recently decided I wanted to spend some time understanding intrinsics and SIMD at a deeper level.</p>
<p>In developing code for my project <a href="https://github.com/icyveins7/ffs"><code>ffs</code></a>, I wanted to make sure that the code was running with at least AVX instructions (because that&rsquo;s my target architecture, and honestly very few computers don&rsquo;t have AVX these days..).</p>
<p>This led me down a path of discovery; first I discovered the amazing-ness that is <a href="https://godbolt.org">godbolt.org</a>, then I joined their discord, where I then asked for some help with understanding basic .asm compiler output.</p>
<p>Ultimately though, I needed to know how to write SIMD-tuned functions for complex numbers i.e. <code>std::complex&lt;T&gt;</code>. These were the types that were going into <code>ffs</code>.</p>
<h1 id="old-but-gold-stackoverflows">Old but Gold Stackoverflows</h1>
<p>Some google searching led me to one of the best references for complex-number SIMD multiplication in an answer by <a href="https://stackoverflow.com/questions/39509746/how-to-square-two-complex-doubles-with-256-bit-avx-vectors/39521257#39521257">Peter Cordes on StackOverflow</a>.</p>
<p>Although it&rsquo;s almost 8 years old at this point, his recommendation is still valid: use <code>-ffast-math</code> and label arrays with <code>__restrict__</code>, and GCC will do a pretty damned good job vectorising with <code>-mavx</code> on.</p>
<p>So with that I placed essentially the same code and compiler arguments - <code>-O3 -ffast-math -mavx</code> - into godbolt again and re-tried it with newer compilers.</p>
<p>I tried first with a simple loop over 4 <code>std::complex&lt;float&gt;</code> values, enough to fill a 256-bit AVX YMM register:</p>
<pre tabindex="0"><code>for (int i = 0; i &lt; 4; ++i)
{
    z[i] = x[i] * y[i];
}
</code></pre><p>And indeed, x86-64 GCC 13.2 outputs the following:</p>
<pre tabindex="0"><code>vmovups ymm2, YMMWORD PTR [rdi]
vmovups ymm0, YMMWORD PTR [rsi]
vpermilps       ymm1, ymm2, 160
vpermilps       ymm2, ymm2, 245
vmulps  ymm1, ymm1, ymm0
vpermilps       ymm0, ymm0, 177
vmulps  ymm0, ymm0, ymm2
vaddsubps       ymm1, ymm1, ymm0
vmovups YMMWORD PTR [rdx], ymm1
vzeroupper
ret
</code></pre><h1 id="msvc-doesnt-auto-vectorise">MSVC.. doesn&rsquo;t auto-vectorise</h1>
<p>Using MSVC, the equivalent compiler options would be to use <code>/O2 /fp:fast /arch:AVX</code>. You also have to change <code>__restrict__</code> to <code>__restrict</code>. This, however, emits the following:</p>
<pre tabindex="0"><code>    sub     rsp, 24
    vmovss  xmm5, DWORD PTR [rcx]
    vmulss  xmm1, xmm5, DWORD PTR [rdx]
    vmulss  xmm2, xmm5, DWORD PTR [rdx+4]
    vmovss  xmm5, DWORD PTR [rcx+8]
    vmovaps XMMWORD PTR [rsp], xmm6
    vmovss  xmm6, DWORD PTR [rcx+4]
    vmulss  xmm0, xmm6, DWORD PTR [rdx+4]
    vsubss  xmm3, xmm1, xmm0
    vmulss  xmm1, xmm6, DWORD PTR [rdx]
    vmovss  xmm6, DWORD PTR [rcx+12]
    vaddss  xmm0, xmm2, xmm1
    vmulss  xmm1, xmm5, DWORD PTR [rdx+8]
    vmulss  xmm2, xmm5, DWORD PTR [rdx+12]
    vmovss  xmm5, DWORD PTR [rcx+16]
    vmovss  DWORD PTR [r8+4], xmm0
    vmulss  xmm0, xmm6, DWORD PTR [rdx+12]
    vmovss  DWORD PTR [r8], xmm3
    vsubss  xmm3, xmm1, xmm0
    vmulss  xmm1, xmm6, DWORD PTR [rdx+8]
    vmovss  xmm6, DWORD PTR [rcx+20]
    vaddss  xmm0, xmm2, xmm1
    vmulss  xmm1, xmm5, DWORD PTR [rdx+16]
    vmulss  xmm2, xmm5, DWORD PTR [rdx+20]
    vmovss  xmm5, DWORD PTR [rcx+24]
    vmovss  DWORD PTR [r8+12], xmm0
    vmulss  xmm0, xmm6, DWORD PTR [rdx+20]
    vmovss  DWORD PTR [r8+8], xmm3
    vsubss  xmm3, xmm1, xmm0
    vmulss  xmm1, xmm6, DWORD PTR [rdx+16]
    vmovss  xmm6, DWORD PTR [rcx+28]
    vaddss  xmm0, xmm2, xmm1
    vmulss  xmm1, xmm5, DWORD PTR [rdx+24]
    vmulss  xmm2, xmm5, DWORD PTR [rdx+28]
    vmovss  DWORD PTR [r8+20], xmm0
    vmulss  xmm0, xmm6, DWORD PTR [rdx+28]
    vmovss  DWORD PTR [r8+16], xmm3
    vsubss  xmm3, xmm1, xmm0
    vmulss  xmm1, xmm6, DWORD PTR [rdx+24]
    vmovaps xmm6, XMMWORD PTR [rsp]
    vaddss  xmm0, xmm2, xmm1
    vmovss  DWORD PTR [r8+28], xmm0
    vmovss  DWORD PTR [r8+24], xmm3
    add     rsp, 24
    ret     0
</code></pre><p>Obviously, not a single YMM register used, and no packed instructions either. You can enable the following MSVC compiler flag, <code>/Qvec-report: 2</code>, to ask it why it didn&rsquo;t vectorise the loop:</p>
<pre tabindex="0"><code>&lt;source&gt;(15) : info C5002: loop not vectorized due to reason &#39;1200&#39;
</code></pre><p>Reason 1200 is apparently <a href="https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/vectorizer-and-parallelizer-messages?view=msvc-170">&lsquo;Loop contains loop-carried data dependencies that prevent vectorization. Different iterations of the loop interfere with each other such that vectorizing the loop would produce wrong answers, and the auto-vectorizer can&rsquo;t prove to itself that there aren&rsquo;t such data dependencies.&rsquo;</a>, which obviously is complete garbage.</p>
<p>I posted all these to the godbolt discord to ask the grandmasters of assembly over there if there was any way around MSVC&rsquo;s issues:</p>
<p><img src="/static/images/msvc-bad-autovectoriser/godbolt-discord.png" alt="godbolt discord says msvc is bad"></p>
<p>If you&rsquo;d like to play around with the MSVC implementation, <a href="https://godbolt.org/z/Txf66f9on">here&rsquo;s the link on godbolt</a>.</p>
<h1 id="since-gcc-works-just-copy-them">Since GCC works, just copy them?</h1>
<p>I needed the above to work with MSVC and Windows, so the easiest thing I could think of was to just reverse-translate GCC&rsquo;s output into the appropriate intrinsic calls; I just went to Intel&rsquo;s <a href="https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html">catalogue</a> and searched them one by one. The model answers are literally given, so why not right?</p>
<p>Word for word translation of the GCC instructions:</p>
<pre tabindex="0"><code>__m256 ymm2 = _mm256_loadu_ps(reinterpret_cast&lt;const float*&gt;(x));
__m256 ymm0 = _mm256_loadu_ps(reinterpret_cast&lt;const float*&gt;(y));

__m256 ymm1 = _mm256_permute_ps(ymm2, 160);
ymm2 = _mm256_permute_ps(ymm2, 245);

ymm1 = _mm256_mul_ps(ymm1, ymm0);

ymm0 = _mm256_permute_ps(ymm0, 177);

ymm0 = _mm256_mul_ps(ymm0, ymm2);

ymm1 = _mm256_addsub_ps(ymm1, ymm0);

_mm256_storeu_ps(reinterpret_cast&lt;float*&gt;(z), ymm1);
</code></pre><p>And their assembly output:</p>
<pre tabindex="0"><code>vmovups ymm1, YMMWORD PTR [rdi]
vmovups ymm0, YMMWORD PTR [rsi]
vpermilps       ymm3, ymm1, 160
vpermilps       ymm2, ymm0, 177
vpermilps       ymm1, ymm1, 245
vmulps  ymm0, ymm0, ymm3
vmulps  ymm1, ymm1, ymm2
vaddsubps       ymm0, ymm0, ymm1
vmovups YMMWORD PTR [rdx], ymm0
vzeroupper
ret
</code></pre><p>If you look closely, the assembly is not 100% identical, but it <strong>is functionally identical</strong> (just track the registers and you&rsquo;ll see it).</p>
<p>Performance-wise, there is also no difference, as confirmed by the man himself:</p>
<p><img src="/static/images/msvc-bad-autovectoriser/godbolt-discord-llvm-mca.png" alt="matt godbolt showing me llvm-mca"></p>
<p>Links to my godbolt are <a href="https://godbolt.org/z/3qcMqvjxT">here</a>. Note that if you change it back to MSVC, the original <code>movups</code> instructions will disappear (this seems to be a thing with MSVC assuming/optimizing the arguments away directly into registers).</p>
<h1 id="some-concluding-remarks">Some Concluding Remarks</h1>
<p>I didn&rsquo;t go into the workings of how the complex number multiplies work, because that&rsquo;s pretty much inside the stackoverflow link.</p>
<p>Also, some might ask why I didn&rsquo;t use a library like Eigen. Yes, I could have, but that would require including a giant library just for one function which is <code>ffs</code>. Also, it was a pretty good learning opportunity.</p>
]]></content></item><item><title>Getting IPP to work on non-Intel chips</title><link>https://icyveins7.github.io/posts/2024/02/getting-ipp-to-work-on-non-intel-chips/</link><pubDate>Mon, 19 Feb 2024 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2024/02/getting-ipp-to-work-on-non-intel-chips/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Intel Performance Primitives is not guaranteed to work on non-Intel chips, but there are some ways around it..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Over the last year or so I&amp;rsquo;ve written a C++ wrapper of header-only templates around a library known as Intel Performance Primitives (IPP). I often use this for its signal processing library, which is - at least by my measurements - one of the fastest, if not the fastest one around. It also has the benefit of having almost everything I need in one place: FFT/DFTs, math array processing, low-pass filtering etc. You can see my templates at the &lt;a href="https://github.com/icyveins7/ipp_ext"&gt;&lt;code&gt;ipp_ext&lt;/code&gt;&lt;/a&gt; repository.&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Intel Performance Primitives is not guaranteed to work on non-Intel chips, but there are some ways around it..</p>
</blockquote>
<p>Over the last year or so I&rsquo;ve written a C++ wrapper of header-only templates around a library known as Intel Performance Primitives (IPP). I often use this for its signal processing library, which is - at least by my measurements - one of the fastest, if not the fastest one around. It also has the benefit of having almost everything I need in one place: FFT/DFTs, math array processing, low-pass filtering etc. You can see my templates at the <a href="https://github.com/icyveins7/ipp_ext"><code>ipp_ext</code></a> repository.</p>
<p>Intel themselves make no promises about IPP&rsquo;s performance on non-Intel chipsets; in fact, they recently pulled support for MacOS, since Apple has gone with Apple Silicon now.</p>
<p>This is trouble for people like me who develop <em><strong>for</strong></em> Intel chipsets, but <em><strong>on</strong></em> non-Intel chipsets (like my home computer and my Macbook).</p>
<h1 id="dynamic-linking-is-the-problem">Dynamic Linking is the Problem</h1>
<p>On Apple Silicon, any linkage with IPP will refuse to build when dynamically linked. Only <strong>static linking</strong> works; that is, you link with the <code>.a</code> libraries directly. This still throws a whole shitload of warnings, but <strong>at least it builds</strong>. I did this when writing my Catch2 tests within <code>ipp_ext</code>.</p>
<p>On AMD however, the problem is more nefarious. I recently spent the better part of a week trying to debug an unknown issue with some code that uses IPP; it would randomly crash when referencing a DFT function, and explicitly checking every reference and pointer would prove that everything was okay. I opened it in Visual Studio debugger to really make sure everything was okay i.e. not that my copy/assignment methods were failing etc.</p>
<p>It seemed likely to me that the problem was the function itself was problematic. IPP dispatches the correct function that corresponds to your chip&rsquo;s instruction set at runtime, so maybe on non-Intel chips this fails for the dynamically linked libraries?</p>
<p>So I just relinked with the static libraries (my AMD is on Windows) which are <code>ippcoremt.lib</code>, <code>ippsmt.lib</code> and <code>ippvmmt.lib</code>. The crashes stopped.</p>
<p>Why? I have no idea, and IPP is closed-source so I will never know.</p>
<h1 id="implications">Implications</h1>
<p>I hope Intel doesn&rsquo;t find this and then decide &lsquo;oh shit he found a loophole&rsquo;. You have to realise that we use this because we want to deploy on Intel eventually.</p>
<p>For the rest of us using IPP, remember that <em><strong>static linking is your friend</strong></em> if all other errors have been checked and you&rsquo;re developing on a non-Intel chip. <strong>Do it even if it compiles properly</strong>, because the runtime crash is almost impossible to debug!</p>
<h1 id="for-the-nay-sayers">For the nay-sayers</h1>
<p>I suspect someone who reads this is going to think: nah bro you definitely wrote some memory leak in, or something along those lines.</p>
<p>Please, if you manage to not crash <em><strong>consistently</strong></em> when linking with IPP dynamically, do let me know how. The setup is here at my non-master <a href="https://github.com/icyveins7/pydsproutines/tree/pybindCZTdebug/pybinds/ippCZT/testCZT"><code>pybindCZTdebug</code> branch of <code>pydsproutines</code></a>. I link with my <code>ipp_ext</code> repository there so maybe the error is inside <code>ipp_ext</code>? But I&rsquo;ve never found the error (and why would the error only surface when dynamically linking..).</p>
<p>Build the solution and link with dynamic libraries - <code>ippcore.lib</code>, <code>ipps.lib</code> - and issue a fix for me if you can find it. I tested this on my AMD Ryzen 5600X (which crashed almost every time, but not every time mind you) and another 2 Intel-based chips (which never crashed with the dynamic linking).</p>
]]></content></item><item><title>Is this thing on?</title><link>https://icyveins7.github.io/posts/2024/02/is-this-thing-on/</link><pubDate>Wed, 14 Feb 2024 20:00:00 +0800</pubDate><guid>https://icyveins7.github.io/posts/2024/02/is-this-thing-on/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Testing out this template..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Are we live?&lt;/p&gt;
&lt;p&gt;Seems like we are. Playing around with next-js for like the 2nd time in my life here..&lt;/p&gt;</description><content type="html"><![CDATA[<blockquote>
<p>Testing out this template..</p>
</blockquote>
<p>Are we live?</p>
<p>Seems like we are. Playing around with next-js for like the 2nd time in my life here..</p>
]]></content></item></channel></rss>