<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Serpent7776</title>
    <description>The latest articles on DEV Community by Serpent7776 (@serpent7776).</description>
    <link>https://dev.to/serpent7776</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F362421%2F1e5e8dca-8ff4-43b1-8b9f-cbf1dfe9ff9c.png</url>
      <title>DEV Community: Serpent7776</title>
      <link>https://dev.to/serpent7776</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/serpent7776"/>
    <language>en</language>
    <item>
      <title>Hive-index now shows operations stats</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Tue, 24 Mar 2026 18:13:42 +0000</pubDate>
      <link>https://dev.to/serpent7776/hive-index-now-shows-operations-stats-3kap</link>
      <guid>https://dev.to/serpent7776/hive-index-now-shows-operations-stats-3kap</guid>
      <description>&lt;p&gt;Previously I published initial version of hive-index, showing custom JSON operation stats. &lt;/p&gt;

&lt;p&gt;This time I want to present an update: hive-index now also shows &lt;a href="https://index.hive-adventures.com/operations" rel="noopener noreferrer"&gt;Hive operations stats page&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The custom JSON operation stats page has been moved to its own page at &lt;a href="https://index.hive-adventures.com/custom_json_operations" rel="noopener noreferrer"&gt;https://index.hive-adventures.com/custom_json_operations&lt;/a&gt; and index now only shows links to these two pages.&lt;/p&gt;

&lt;p&gt;The top 2 operations take up 90% of all operations: &lt;code&gt;custom_json_operation&lt;/code&gt; and &lt;code&gt;vote_operation&lt;/code&gt;. The third one is &lt;code&gt;comment_operation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The project still lives at &lt;a href="https://index.hive-adventures.com/" rel="noopener noreferrer"&gt;https://index.hive-adventures.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enjoy, and if you have questions or comments, just ask.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>webdev</category>
      <category>development</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Introducing Hive Index: A real-time custom json operations explorer for Hive</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Thu, 05 Mar 2026 21:35:49 +0000</pubDate>
      <link>https://dev.to/serpent7776/introducing-hive-index-a-real-time-custom-json-operations-explorer-for-hive-35p7</link>
      <guid>https://dev.to/serpent7776/introducing-hive-index-a-real-time-custom-json-operations-explorer-for-hive-35p7</guid>
      <description>&lt;p&gt;I'd like to share &lt;strong&gt;Hive Index&lt;/strong&gt;, a web UI for exploring Hive blockchain activity with clear, live stats, built in Rust 🦀.&lt;/p&gt;

&lt;p&gt;It runs as a single binary, continuously pulling Hive blocks and updating its internal database with metadata about the blocks and operations.&lt;/p&gt;

&lt;p&gt;Currently, it aggregates &lt;code&gt;custom_json&lt;/code&gt; operations and shows ranked stats for them.&lt;/p&gt;

&lt;p&gt;If I read the data correctly, the top 10 is dominated by Splinterlands, but there's also &lt;code&gt;follow&lt;/code&gt; operation at #7.&lt;/p&gt;

&lt;p&gt;The data was synced starting at block 7,000,000 (it doesn't seem there was anything interesting before that). The current database weight is about 9GB.&lt;/p&gt;

&lt;p&gt;If you're interested in trying it out, head to &lt;a href="https://index.hive-adventures.com/" rel="noopener noreferrer"&gt;https://index.hive-adventures.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me know if it's useful to you and if you have any questions or suggestions!&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>development</category>
      <category>rust</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Comparing images with AVX</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Sun, 02 Nov 2025 17:11:05 +0000</pubDate>
      <link>https://dev.to/serpent7776/comparing-images-with-avx-3fo2</link>
      <guid>https://dev.to/serpent7776/comparing-images-with-avx-3fo2</guid>
      <description>&lt;p&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest"&gt;2025 Hacktoberfest Writing Challenge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the first time I heard about &lt;a href="https://hacktoberfest.com/" rel="noopener noreferrer"&gt;hacktoberfest&lt;/a&gt; - a month-long initiative in October that encourages developers around the world to contribute to open source projects. This looked interesting, so I thought I'd give it a go.&lt;/p&gt;

&lt;p&gt;Some time before that I started learning more about SIMD (AVX512 specifically) and was looking for a project where I can apply this in practice. One of the projects I knew was odiff, which I learned about at the &lt;a href="https://fun-ocaml.com/" rel="noopener noreferrer"&gt;FunOCaml&lt;/a&gt; conference I went to last year.&lt;/p&gt;

&lt;p&gt;This blog post summarises my contribution to odiff, by rewriting odiff core algorithm into AVX512 assembly.&lt;/p&gt;

&lt;h2&gt;
  
  
  odiff
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/dmtrKovalenko/odiff" rel="noopener noreferrer"&gt;odiff&lt;/a&gt; is a very fast pixel-by-pixel image comparison tool. It is comparing images in the YIQ colour space rather than the RGB space to better represent visual differences. It's a really cool project. Go check it out.&lt;/p&gt;

&lt;p&gt;It was originally written in &lt;a href="https://ocaml.org/" rel="noopener noreferrer"&gt;OCaml&lt;/a&gt; and recently it was rewritten in &lt;a href="https://ziglang.org/" rel="noopener noreferrer"&gt;zig&lt;/a&gt; for better SIMD support.&lt;/p&gt;

&lt;h2&gt;
  
  
  SIMD
&lt;/h2&gt;

&lt;p&gt;Normally, CPU can only operate on a single memory location at a time (e.g. a byte, word, etc). This means that if we want to apply the same operation to large number of different memory locations, we need to do it in a loop, one element at a time. This is quite tedious and easy to get wrong. It also doesn't use all the power the CPU has.&lt;/p&gt;

&lt;p&gt;There's a lot of workflows that could be optimised to do this is the hardware. Image processing, like in odiff, is one example. Other examples include audio and video processing. All of them essentially apply the same processing to a very large number of data samples.&lt;/p&gt;

&lt;p&gt;Originally introduced as MMX extension for x86, but over time evolved to a large number of other extensions, with growing capabilities.&lt;br&gt;
Each CPU has its own SIMD extensions: x86 has MMX, SSE and AVX, ARM has NEON, and RISC-V has RVV.&lt;/p&gt;

&lt;h2&gt;
  
  
  AVX512
&lt;/h2&gt;

&lt;p&gt;AVX is a variant of x86 SIMD that operates on 256 bits of data at a time.&lt;br&gt;
AVX512 is a evolution of AVX that operates on 512 bit of data. It's really awesome.&lt;/p&gt;

&lt;p&gt;512 bits is 64 bytes, which for RGBA image data means we can operate on 16 pixels at a time in one instruction.&lt;/p&gt;

&lt;p&gt;If you ever used array programming languages, AVX512 kind of feels similar (though of course it's definitely lower level). You always operate on whole arrays, not single elements. This requires a shift in the way you think about doing things, but it's very rewarding and can give you some nice performance boost.&lt;/p&gt;

&lt;h2&gt;
  
  
  vxdiff
&lt;/h2&gt;

&lt;p&gt;This lead to the creation of &lt;a href="https://github.com/serpent7776/vxdiff" rel="noopener noreferrer"&gt;vxdiff&lt;/a&gt;, a pure assembly AVX512 based reimplementation of the core odiff algorithm. It only implements the default set of odiff options (and odiff has some interesting options to tweak its behaviour), but it's still a capable tool. It's also easier to start simple rather than trying to implement all the possible options right away.&lt;/p&gt;

&lt;p&gt;It was a really fun thing to do, and I learnt a lot about AVX&lt;/p&gt;

&lt;h2&gt;
  
  
  The algorithm
&lt;/h2&gt;

&lt;p&gt;I will now explain the odiff core algorithm after translation to AVX. This might be a bit chaotic, because I was writing this in a bit of a hurry, so bear with me.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/serpent7776/vxdiff/blob/c0bde9009b6398ff657834eb42f6ea721a180fb1/vxdiff.asm" rel="noopener noreferrer"&gt;full source is here&lt;/a&gt;, you can follow along.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First the input images are loaded into memory using RGBA8 format, which means that there's 3 colour channels (red, green, blue) and an alpha channel, each having 8 bits.&lt;/li&gt;
&lt;li&gt;Once we've got the image data, the actual processing loop starts at &lt;a href="https://github.com/serpent7776/vxdiff/blob/c0bde9009b6398ff657834eb42f6ea721a180fb1/vxdiff.asm#L129" rel="noopener noreferrer"&gt;.y_loop&lt;/a&gt; label, which iterates over image rows. Each row is handled by &lt;a href="https://github.com/serpent7776/vxdiff/blob/c0bde9009b6398ff657834eb42f6ea721a180fb1/vxdiff.asm#L137" rel="noopener noreferrer"&gt;.x_loop&lt;/a&gt; label, which is a loop that loads 512bit of data at a time. &lt;code&gt;rdi&lt;/code&gt; and &lt;code&gt;rsi&lt;/code&gt; point to image data of the base and other images. After executing &lt;code&gt;xmm1&lt;/code&gt; and &lt;code&gt;xmm2&lt;/code&gt; will have 64 bytes of loaded data, which we can now operate on.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vmovdqu8 xmm1, [rdi]
vmovdqu8 xmm2, [rsi]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now, we want to replace pixels having alpha equal to &lt;code&gt;0&lt;/code&gt; with white. First we clear &lt;code&gt;k6&lt;/code&gt; and &lt;code&gt;k7&lt;/code&gt; mask registers with &lt;code&gt;kxor&lt;/code&gt;. Then we check alpha channel to see which values are equal to 0 and store the result into &lt;code&gt;k6&lt;/code&gt; mask using &lt;code&gt;vpcmpequb&lt;/code&gt;. At the indices where alpha was &lt;code&gt;0&lt;/code&gt;, mask will equal &lt;code&gt;1&lt;/code&gt; and for all other indices it will be &lt;code&gt;0&lt;/code&gt;. Then we propagate that information to other channels (red, green and blue) using &lt;code&gt;kshiftlb&lt;/code&gt; and &lt;code&gt;kor&lt;/code&gt;. Then we store &lt;code&gt;255&lt;/code&gt; at places where resulting mask is &lt;code&gt;1&lt;/code&gt;. We do this twice - once for the base image and second time for the image we compare against.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kxor k6, k6, k6              
kxor k7, k7, k7              
vpcmpequb k6 {k4}, xmm1, xmm0
kshiftlb k6, k6, 1           
kor k7, k7, k6               
kshiftlb k6, k6, 1           
kor k7, k7, k6               
kshiftlb k6, k6, 1           
kor k7, k7, k6               
vmovdqu8 xmm1 {k7}, xmm31    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, we want to do some multiplications, so we'll convert bytes to floats. First we convert from bytes to dwords using &lt;code&gt;vpmovzxbd&lt;/code&gt; and then from dwords to floats using &lt;code&gt;vcvtudq2ps&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vpmovzxbd zmm1, xmm1  
vcvtudq2ps zmm1, zmm1 
vpmovzxbd zmm2, xmm2  
vcvtudq2ps zmm2, zmm2 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Normalise alpha from initial &lt;code&gt;0-255&lt;/code&gt; range to &lt;code&gt;0.0 - 1.0&lt;/code&gt; range. &lt;code&gt;k4&lt;/code&gt; mask apply the &lt;code&gt;vmulps&lt;/code&gt; operation (which does multiplication) only to alpha channel.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vmulps zmm1 {k4}, zmm1, zmm3
vmulps zmm2 {k4}, zmm2, zmm3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Do alpha-blending, so blend red, green and blue channels with white pixel using alpha channel. This essentially compute &lt;code&gt;(RGB - 255.0) * A + 255.0&lt;/code&gt;. I'm not actually sure why it's computed this way, but this is what odiff used to do in the previous version. &lt;code&gt;k5&lt;/code&gt; apply the operations only to red, green and blue channels, leaving alpha channel unmodified.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vsubps zmm1 {k5}, zmm1, zmm30  
vshufps zmm10, zmm1, zmm1, 0xff
vmulps zmm1 {k5}, zmm1, zmm10  
vaddps zmm1 {k5}, zmm1, zmm30  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now we start RGB to YIQ conversion. We use precomputed matrix in &lt;code&gt;zmm7&lt;/code&gt;, &lt;code&gt;zmm8&lt;/code&gt; and &lt;code&gt;zmm9&lt;/code&gt; to multiply to with RGB values to get YIQ values with &lt;code&gt;vmulps&lt;/code&gt;. That will give us 3 separate vectors with &lt;code&gt;Y&lt;/code&gt;, &lt;code&gt;I&lt;/code&gt; and &lt;code&gt;Q&lt;/code&gt; channels. Then we shuffle them around and add them up to get &lt;code&gt;YIQ&lt;/code&gt; vector.
We will use this to compute the difference between base image and the image we are comparing against.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vmulps zmm10, zmm1, zmm7 ; y
vmulps zmm11, zmm1, zmm8 ; i
vmulps zmm12, zmm1, zmm9 ; q

; yiq(R)                                    
vxorps zmm13, zmm13, zmm13                  
vshufps zmm13 {k1}, zmm10, zmm10, 0b00000000
vshufps zmm13 {k2}, zmm11, zmm11, 0b00000000
vshufps zmm13 {k3}, zmm12, zmm12, 0b00000000
; yiq(G)                                    
vxorps zmm14, zmm14, zmm14                  
vshufps zmm14 {k1}, zmm10, zmm10, 0b00000001
vshufps zmm14 {k2}, zmm11, zmm11, 0b00000100
vshufps zmm14 {k3}, zmm12, zmm12, 0b00010000
; yiq(B)                                    
vxorps zmm15, zmm15, zmm15                  
vshufps zmm15 {k1}, zmm10, zmm10, 0b00000010
vshufps zmm15 {k2}, zmm11, zmm11, 0b00001000
vshufps zmm15 {k3}, zmm12, zmm12, 0b00100000
; yiq                     
vaddps zmm16, zmm13, zmm14
vaddps zmm16, zmm16, zmm15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The result is that we have &lt;code&gt;YIQ&lt;/code&gt; of base image in &lt;code&gt;zmm16&lt;/code&gt; and &lt;code&gt;YIQ&lt;/code&gt; of the other image in &lt;code&gt;zmm26&lt;/code&gt;. We take the difference of the two using &lt;code&gt;vsubps&lt;/code&gt;, square the result and multiply by delta coefficient.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;; YIQ diff                
vsubps zmm16, zmm16, zmm26

; YIQ*YIQ                 
vmulps zmm16, zmm16, zmm16
; YIQ*YIQ * delta coef    
vmulps zmm16, zmm16, zmm29
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now we have squares of differences of channels in &lt;code&gt;zmm16&lt;/code&gt; vector, so we need to sum those channels into a single one. This way even there's difference in both channel &lt;code&gt;Y&lt;/code&gt; and &lt;code&gt;I&lt;/code&gt;, it will be counted as a single difference.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vxorps zmm17, zmm17, zmm17                  
vxorps zmm18, zmm18, zmm18                  
vxorps zmm19, zmm19, zmm19                  
vshufps zmm17 {k1}, zmm16, zmm16, 0b10101010
vshufps zmm18 {k1}, zmm16, zmm16, 0b01010101
vshufps zmm19 {k1}, zmm16, zmm16, 0b00000000

; delta                                     
vaddps zmm16, zmm19, zmm18                  
vaddps zmm16, zmm16, zmm17                  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Finally, we count the number of different pixels. We compare the resulting values against maximum allowed delta in &lt;code&gt;xmm28&lt;/code&gt;, which gives boolean mask in &lt;code&gt;k6&lt;/code&gt;, which we then treat as an array with &lt;code&gt;1&lt;/code&gt; or &lt;code&gt;0&lt;/code&gt; values and sum it up. The sum is the number of pixels that differ when comparing base image against the other image.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vcompressps zmm16 {k1}, zmm16
vcmpgtps k6, xmm16, xmm28    
kmov eax, k6                 
popcnt eax, eax              
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Back to odiff
&lt;/h2&gt;

&lt;p&gt;vxdiff is definitely an interesting project, but its usefulness is limited when compared to odiff, given all its options. Fortunately, &lt;a href="https://github.com/dmtrKovalenko/odiff/pull/131" rel="noopener noreferrer"&gt;vxdiff is now part of odiff&lt;/a&gt;. To activate it, you need to specify &lt;code&gt;--enable-asm&lt;/code&gt; when invoking odiff.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's all folks
&lt;/h2&gt;

&lt;p&gt;Shout out to &lt;a href="https://github.com/dmtrKovalenko" rel="noopener noreferrer"&gt;Dmitriy Kovalenko&lt;/a&gt; and the organisers of &lt;a href="https://fun-ocaml.com/" rel="noopener noreferrer"&gt;FunOCaml&lt;/a&gt;, because as strange as it may seem, this project wouldn't have happened if FunOCaml hadn't been organised.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>assembly</category>
    </item>
    <item>
      <title>Signal safety and why should you care</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Wed, 23 Jul 2025 20:01:24 +0000</pubDate>
      <link>https://dev.to/serpent7776/signal-safety-and-why-should-you-care-4pe7</link>
      <guid>https://dev.to/serpent7776/signal-safety-and-why-should-you-care-4pe7</guid>
      <description>&lt;p&gt;UPDATE 2025-07-25: Clarified async-signal safety in context of boost::asio::signal_set&lt;br&gt;
UPDATE 2025-08-12: Mention &lt;code&gt;sigaction&lt;/code&gt; as an alternative to &lt;code&gt;signal&lt;/code&gt;. Thanks to Paul J. Lucas.&lt;/p&gt;

&lt;p&gt;Calling a non-async-signal-safe function from a signal handler is essentially an undefined behaviour and can lead to all sorts of weird behaviours: deadlocks, race conditions and even memory corruption. You may even experience crashes originating from libc, such as the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;malloc(): unsorted double linked list corrupted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fatal glibc error: malloc.c:2599 (sysmalloc): assertion failed: (old_top == initial_top (av) &amp;amp;&amp;amp; old_size == 0) || ((unsigned long) (old_size) &amp;gt;= MINSIZE &amp;amp;&amp;amp; prev_inuse (old_top) &amp;amp;&amp;amp; ((unsigned long) old_end &amp;amp; (pagesize - 1)) == 0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What are signals?
&lt;/h2&gt;

&lt;p&gt;Signals are a way to do asynchronous messages on a POSIX system (such as BSD, Linux, macOS) to notify a process that a specific event happened. The key feature here is the asynchronous part. A signal can be delivered at almost any moment interrupting the application's normal flow. This might happen when the application is inside critical session or syscall. Your application can be in the middle of allocating or freeing memory, or performing any other standard C function call. Pretty scary.&lt;/p&gt;

&lt;p&gt;Most signals are ignored by default, some automatically terminate the entire program. For most of them a custom signal handler can be attached to execute potentially any code when the signal is delivered. Potentially, because of all the restrictions described in the previous paragraph. Therefore the installed signal handler needs to be so-called async-signal-safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is async‑signal safety?
&lt;/h2&gt;

&lt;p&gt;A function is considered async-signal-safe if it can be safely called from within a signal handler without causing undefined behaviour. This is pretty circular definition. Basically there are two possibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the function needs to be reentrant and only execute atomic operations&lt;/li&gt;
&lt;li&gt;the function needs to block signals temporarily&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A function is reentrant if it can be invoked again even if the previous invocation is still running. Basically it must be able to recurse into itself at any point. Such function cannot rely on global state or execute non-atomic operations.&lt;br&gt;
An atomic operation is the one that completes as a single step that cannot be interrupted. It either happens completely or not at all - an intermediate state cannot be observed. Most operations are not atomic. Even seemingly simple operations might not be, e.g. incrementing a variable is a complex operation (unless it's of an atomic type).&lt;br&gt;
Both properties drastically reduce the number of possible operations that can be safely executed from a handler.&lt;br&gt;
The second option is to temporarily block signals while executing a function. This should not be overused as it may limit the program responsiveness.&lt;br&gt;
The general advice is that a signal handlers should perform only the minimum required work.&lt;/p&gt;
&lt;h2&gt;
  
  
  Functions that are not async-signal-safe
&lt;/h2&gt;

&lt;p&gt;Below are some examples of functions that should not be called from a signal handler. Note that this is not an exhaustive list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory management: &lt;code&gt;malloc&lt;/code&gt;, &lt;code&gt;free&lt;/code&gt;, &lt;code&gt;realloc&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Standard C I/O: &lt;code&gt;printf&lt;/code&gt;, &lt;code&gt;fprintf&lt;/code&gt;, &lt;code&gt;fopen&lt;/code&gt;, &lt;code&gt;fclose&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Thread synchronisation: &lt;code&gt;pthread_mutex_lock&lt;/code&gt;, &lt;code&gt;pthread_cond_wait&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Dynamic symbol resolution: &lt;code&gt;dlopen&lt;/code&gt;, &lt;code&gt;dlsym&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;High‑level POSIX calls: &lt;code&gt;system&lt;/code&gt;, &lt;code&gt;getaddrinfo&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is not always obvious if a function is async-signal-safe or not. For example you might think that &lt;code&gt;memset&lt;/code&gt; should be safe, but it turns out that &lt;a href="https://boston.conman.org/2016/12/17.1" rel="noopener noreferrer"&gt;memset is not async-signal-safe&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Functions that are guaranteed to be async-signal-safe
&lt;/h2&gt;

&lt;p&gt;POSIX specifies a list of functions that are required to be async-signal-safe, here are a few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I/O: &lt;code&gt;read(2)&lt;/code&gt;, &lt;code&gt;write(2)&lt;/code&gt;, &lt;code&gt;creat(2)&lt;/code&gt;, &lt;code&gt;close(2)&lt;/code&gt;, &lt;code&gt;unlink(2)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Process control: &lt;code&gt;kill(2)&lt;/code&gt;, &lt;code&gt;wait(2)&lt;/code&gt;, &lt;code&gt;abort(3)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Reading and writing to atomic flags (&lt;code&gt;volatile sig_atomic_t&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To see the full list consult the &lt;a href="https://pubs.opengroup.org/onlinepubs/9799919799/functions/V2_chap02.html#:~:text=The%20following%20table%20defines%20a%20set%20of%20functions%20and%20function-like%20macros%20that%20shall%20be%20async-signal-safe" rel="noopener noreferrer"&gt;POSIX specification&lt;/a&gt; or your operating system's documentation. For Linux its &lt;a href="https://man7.org/linux/man-pages/man7/signal-safety.7.html" rel="noopener noreferrer"&gt;signal-safety(7)&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Common practice
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Use &lt;code&gt;volatile sig_atomic_t&lt;/code&gt; flag
&lt;/h3&gt;

&lt;p&gt;If any more complex work is required in response to a signal, the common practice is to set a &lt;code&gt;volatile sig_atomic_t&lt;/code&gt; flag in the handler and return immediately. The bulk of the required work is then done elsewhere in the normal program loop after the flag has been checked. However, not every signal can be handled in this way (e.g. &lt;code&gt;SIGSEGV&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;sig_atomic_t&lt;/span&gt; &lt;span class="n"&gt;got_sigint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;got_sigint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Safe atomic store&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(...)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// normal program loop&lt;/span&gt;
        &lt;span class="n"&gt;do_work&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got_sigint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// handle signal&lt;/span&gt;
            &lt;span class="n"&gt;handle_sigint&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;got_sigint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: this code uses &lt;code&gt;signal&lt;/code&gt;, which is the traditional way of handling signals. There are newer APIs to do the same, which should be used instead, e.g. &lt;a href="https://www.man7.org/linux/man-pages/man2/sigaction.2.html" rel="noopener noreferrer"&gt;sigaction&lt;/a&gt;. The detailed comparison is out of scope here; it's a topic for a future post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Block signals during critical sections
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;sigprocmask&lt;/code&gt; or &lt;code&gt;pthread_sigmask&lt;/code&gt; to block relevant signals around non‑async-signal‑safe code and unblock immediately after.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real world signals
&lt;/h2&gt;

&lt;p&gt;Most of the code isn't likely to be invoked from a signal handler. However in some contexts this may happen. And it's not always clear what parts of code might be invoked from a signal handler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Postgresql query timeout handlers
&lt;/h3&gt;

&lt;p&gt;In Postgresql you can register a query timeout handler. It will be invoked automatically when a query exceeds the specified time limit. What might be surprising is that Postgresql uses &lt;code&gt;SIGALRM&lt;/code&gt; signal to interrupt the query. This means that the timeout handler runs from a signal handler. You might want to produce a log that says that the query exceeded the specified time in the handler, but this could lead to all the weird behaviours mentioned above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Ctrl+C for safe exit
&lt;/h3&gt;

&lt;p&gt;A common use of signals is to implement safe program termination by handling Ctrl-C key combination. This is done by handling the &lt;code&gt;SIGINT&lt;/code&gt; signal. And again, you must be very careful what you do inside the handler. Typically you would simply set a flag, return from the handler and do the actual handling elsewhere in the program. Doing anything more complex directly in the handler is very risky.&lt;/p&gt;

&lt;h3&gt;
  
  
  boost::asio::signal_set
&lt;/h3&gt;

&lt;p&gt;If you are using C++, &lt;code&gt;boost::asio::signal_set&lt;/code&gt; automatically handles all of this for you. This is because it installs its own, small signal handler that does the minimal work and invokes your callback in the context of the thread running the asio IO service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Be aware of the contexts in which the code might be called from a signal handler. It's very easy to introduce undefined behaviour in these places if you're not careful.&lt;/p&gt;

&lt;p&gt;If you encounter weird memory corruption or other weird behaviours, signal handlers could be the first place to look for potential issues.&lt;/p&gt;

</description>
      <category>c</category>
      <category>posix</category>
      <category>programming</category>
      <category>dev</category>
    </item>
    <item>
      <title>Snowflakes and other UUIDs</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Wed, 09 Jul 2025 17:43:29 +0000</pubDate>
      <link>https://dev.to/serpent7776/snowflakes-and-other-uuids-54n8</link>
      <guid>https://dev.to/serpent7776/snowflakes-and-other-uuids-54n8</guid>
      <description>&lt;p&gt;In software projects there's often a need to assign a unique identifier to a data record or entity so that it can be distinctly recognised. Aside from sequential numbering, there are two common methods of generating such IDs: UUIDs and snowflakes. This blog post describes both of them pointing out their strength and weaknesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  UUID
&lt;/h2&gt;

&lt;p&gt;UUIDs are 128-bit values that are meant to be unique for practical purposes.&lt;/p&gt;

&lt;p&gt;An example UUID is &lt;code&gt;a5de120b-fe73-4fbf-afbf-061551b299bb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All UUIDs have the following structure: &lt;code&gt;xxxxxxxx-xxxx-Vxxx-Rxxx-xxxxxxxxxxxx&lt;/code&gt;&lt;br&gt;
The values in the &lt;code&gt;V&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt; locations uniquely identify the UUID version and variant respectively. The &lt;code&gt;x&lt;/code&gt; values are set depending on the version of UUID.&lt;/p&gt;

&lt;p&gt;So we can see that the above example ID is a version 4 UUID.&lt;br&gt;
&lt;code&gt;89b910b8-59c1-11f0-94b8-325096b39f47&lt;/code&gt; is a version 1 UUID.&lt;br&gt;
&lt;code&gt;0197db8e-0f63-74a6-9ee5-d402424d1cd4&lt;/code&gt; is a version 7 UUID.&lt;/p&gt;

&lt;p&gt;There's a few different UUID versions and I will describe them out-of-order, because it will be easier to explain:&lt;/p&gt;

&lt;h3&gt;
  
  
  Version 1 (timestamp + MAC address)
&lt;/h3&gt;

&lt;p&gt;Concatenates a 60-bit timestamp with the 48-bit MAC address of the node that is generating the UUID. This sounds pretty neat and useful, until you find out that the timestamp is the number of 100-nanosecond intervals since midnight 15 October 1582 UTC - the date of Gregorian reform to the Christian calendar. This is quite a weird encoding. What's worse, timestamp bits are stored in the UUID from the lowest to the highest bits, which means these IDs cannot be trivially sorted by the date. The advantage of this version is that it is guaranteed to be a unique ID, unless generated on the same computer and at the exact same time. The issue is that it exposes the generating machine's MAC address.&lt;/p&gt;

&lt;h3&gt;
  
  
  Version 6 (timestamp + MAC address)
&lt;/h3&gt;

&lt;p&gt;This is the same as version 1 except all timestamp bits are ordered from most significant to least significant. This fixes one issue with version 1 - IDs can now be trivially sorted by the date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Version 4 (random)
&lt;/h3&gt;

&lt;p&gt;All the bits are generated randomly. This means there's potentially a chance of duplication, but this is almost impossible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Version 5 (namespace name-based)
&lt;/h3&gt;

&lt;p&gt;This generates a unique ID from a SHA-1 hash of a concatenation of a namespace and a name. There is no temporal or random component, so the same input produces the same output every time.  If you need to generate reproducible UUIDs from given names, you likely want this version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Version 3 (namespace name-based)
&lt;/h3&gt;

&lt;p&gt;The same as version 5, but uses MD5 instead of SHA-1. Unless you know you need it, you should use version 5 instead of this one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Version 7 (timestamp and random)
&lt;/h3&gt;

&lt;p&gt;Begins with a 48 bit big-endian Unix Epoch timestamp with approximately millisecond granularity. The following bits are random seeded counter&lt;/p&gt;

&lt;h3&gt;
  
  
  Version 2 (DCE Security version, with embedded POSIX UIDs)
&lt;/h3&gt;

&lt;p&gt;Not very widely used.&lt;br&gt;
Similar to version 1 - uses the current time, along with the MAC address, but replaces the low part of the time field with a local identifier e.g. the user ID or group ID of the local account that created the UUID. Compared to version 1, it has the advantage that you can know not only when and where the ID was created, but also who created the identifier. Replacing part of the timestamp with local ID has some important drawbacks though. It reduces the number of unique ID that are possible to generate and makes the timestamp lossy - they only have granularity of about 7 minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Version 8 (custom)
&lt;/h3&gt;

&lt;p&gt;There are one two requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The variant bits have to be 10.&lt;/li&gt;
&lt;li&gt;The version nibble has to be the value of 8.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The remaining bits are up to the vendor to customise.&lt;/p&gt;

&lt;p&gt;The advantages of UUID are that they are standardised and there's many ready to use libraries. One disadvantage is that they are 128-bit values, which might be pretty big for some use cases. There might be other issues depending on the exact context.&lt;br&gt;
For example if a deterministic ID is needed then only versions 3 and 5 are appropriate. If a timestamp-sortable ID is needed then only versions 6 or 7 are adequate. But what if both properties are needed? Versions 3 and 5 are not sortable by time, and versions 6 and 7 are not fully deterministic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Snowflake
&lt;/h2&gt;

&lt;p&gt;Snowflake were originally created at Twitter as a performant way to create a large amount of unique IDs in distributed context.&lt;/p&gt;

&lt;p&gt;They are in spirit similar to UUID version 6. They contain timestamp, generating node's ID and a sequence number. Similarly to UUID version 6, snowflakes are trivially sortable by time. The big difference is that snowflakes are 64-bit compared to 128-bit UUID. For this reason they don't have any particular representation - they are presented simply as an integer.&lt;/p&gt;

&lt;p&gt;Twitter's snowflake has the following fields widths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sign bit - unused&lt;/li&gt;
&lt;li&gt;41 bits for the timestamp&lt;/li&gt;
&lt;li&gt;10 bits for node ID&lt;/li&gt;
&lt;li&gt;12 bits for sequence number&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because it's just an integer without any structure, it's also very easy to customise. Simple bit manipulation operations are enough to create your own snowflake type in handful of lines. It's easy to change bit widths of the fields and also add or remove fields.&lt;/p&gt;

&lt;p&gt;The main issue with snowflakes is that they are less well-known and there's less ready to be used libraries. It's also not standardised, which combined with the easy customisability cause that the existing libraries might not be compatible with each other.&lt;/p&gt;

&lt;p&gt;With all this freedom you can create snowflake type that is both sortable by time and deterministic. It's just a matter of proper field interpretation.&lt;/p&gt;

&lt;h2&gt;
  
  
  pg-snowflake
&lt;/h2&gt;

&lt;p&gt;I created a Postgresql extension that can create customisable snowflake IDs. You can create many distinct ID types, each having different bit allocations for its fields.&lt;/p&gt;

&lt;p&gt;Here's the &lt;a href="https://github.com/serpent7776/pg_snowflake" rel="noopener noreferrer"&gt;Github page for pg_snowflake extension&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other snowflakes
&lt;/h2&gt;

&lt;p&gt;There are a few other libraries creating snowflakes and UUIDs, you can find some here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/uuid-ossp.html" rel="noopener noreferrer"&gt;Postgres builtin uuid-ossp module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pgEdge/snowflake" rel="noopener noreferrer"&gt;https://github.com/pgEdge/snowflake&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sony/sonyflake" rel="noopener noreferrer"&gt;https://github.com/sony/sonyflake&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mausimag/pgflake" rel="noopener noreferrer"&gt;https://github.com/mausimag/pgflake&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Read more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.rfc-editor.org/rfc/rfc9562.html" rel="noopener noreferrer"&gt;RFC 9562&lt;/a&gt; - specifies UUIDs&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/datatype-uuid.html" rel="noopener noreferrer"&gt;Postgres UUID type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Snowflake_ID" rel="noopener noreferrer"&gt;Snowflake ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Universally_unique_identifier" rel="noopener noreferrer"&gt;UUID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.uuidtools.com/uuid-versions-explained" rel="noopener noreferrer"&gt;https://www.uuidtools.com/uuid-versions-explained&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/twitter-archive/snowflake/tree/snowflake-2010" rel="noopener noreferrer"&gt;Twitter original snowflake implementation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>development</category>
      <category>database</category>
    </item>
    <item>
      <title>LD_PRELOAD explained</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Sun, 08 Jun 2025 21:18:04 +0000</pubDate>
      <link>https://dev.to/serpent7776/ldpreload-explained-4aif</link>
      <guid>https://dev.to/serpent7776/ldpreload-explained-4aif</guid>
      <description>&lt;p&gt;EDIT 2025-06-09 Added libSegFault example as suggested by Jeremy from #include.&lt;/p&gt;

&lt;p&gt;LD_PRELOAD is an environment variable on Unix-like operating systems that allows users to specify a shared library to be loaded before others when a program is executed. This can be used to override functions in existing libraries or to inject custom code into applications. This functionality is provided by system dynamic linker and works only for dynamically linked executables. Statically linked binaries skip this entirely.&lt;br&gt;
This post will be focused on Linux and BSDs.&lt;/p&gt;
&lt;h2&gt;
  
  
  The dynamic linker
&lt;/h2&gt;

&lt;p&gt;First, let's see what the dynamic linker is.&lt;/p&gt;

&lt;p&gt;When a dynamically linked program is launched, before even calling the &lt;code&gt;main&lt;/code&gt; function of the program a few things are done. One of the first things that the operating system does is that it reads the path of the dynamic linker from the executable image and then execute that as a program. This has to succeed or the whole program execution fails. When dynamic loader is executed, it loads the program image and all the dynamically loaded shared libraries that the executed program needs, and then starts the execution.&lt;br&gt;
So basically, the dynamic linker is another program that handles execution of all other dynamically executed programs on the system.&lt;/p&gt;

&lt;p&gt;On Linux the dynamic linker will be something like &lt;code&gt;ld-linux.so&lt;/code&gt;, e.g. on my system it's &lt;code&gt;/lib64/ld-linux-x86-64.so.2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On FreeBSD it will be something like &lt;code&gt;/libexec/ld-elf.so.1&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  LD_PRELOAD
&lt;/h2&gt;

&lt;p&gt;So, how does &lt;code&gt;LD_PRELOAD&lt;/code&gt; work?&lt;br&gt;
It basically adds a list of shared libraries to be loaded by the dynamic linker after the program image is loaded, but before its shared object dependencies are loaded. This way, when a particular function symbol name is looked for, it will be found in preloaded shared object first. This is how function overriding is achieved.&lt;/p&gt;
&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Zlibc
&lt;/h3&gt;

&lt;p&gt;Zlibc, also known as uncompress.so - facilitates transparent decompression when used through the LD_PRELOAD. It is therefore possible to read compressed (gzipped) files data as if they were uncompressed, essentially adding support for reading compressed files to any application.&lt;/p&gt;
&lt;h3&gt;
  
  
  LibSegFault
&lt;/h3&gt;

&lt;p&gt;Another interesting example is LibSegFault, which is preloadable library that handles segfaults and produce nice stack trace on crashes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Overriding malloc
&lt;/h3&gt;

&lt;p&gt;The following is a simple example of a library that overrides &lt;code&gt;malloc&lt;/code&gt; function.&lt;br&gt;
It uses &lt;code&gt;dlsym(RTLD_NEXT, "malloc")&lt;/code&gt; to look up the real malloc that is being overridden and counts the number of all allocations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define _GNU_SOURCE
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;dlfcn.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;malloc_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;real_malloc&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;real_malloc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;real_malloc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dlsym&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RTLD_NEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"malloc"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;real_malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;malloc_count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you need to be careful when adding custom code to &lt;code&gt;malloc&lt;/code&gt;, not to call itself recursively, which might be easier than you think.&lt;/p&gt;

&lt;p&gt;This example is not very exciting, so let's take a look at something more interesting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overriding read and write
&lt;/h3&gt;

&lt;p&gt;Another example would be a library that overrides &lt;code&gt;read&lt;/code&gt; and &lt;code&gt;write&lt;/code&gt; calls to add a sleep before doing the actual work:&lt;br&gt;
This example comes from &lt;a href="https://github.com/serpent7776/crawlio" rel="noopener noreferrer"&gt;crawlio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time the actual implementation of &lt;code&gt;read&lt;/code&gt; and &lt;code&gt;write&lt;/code&gt; is loaded in the constructor function. This is a function that is executed just after the library is loaded. This way the overloaded functions are loaded only once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define _GNU_SOURCE
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;dlfcn.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;time.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="nf"&gt;ssize_t&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;orig_read_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="nf"&gt;ssize_t&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;orig_write_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;orig_read_t&lt;/span&gt; &lt;span class="n"&gt;original_read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;orig_write_t&lt;/span&gt; &lt;span class="n"&gt;original_write&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sleep_ms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;timespec&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tv_sec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tv_nsec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;nanosleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;__attribute__&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;original_read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orig_read_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;dlsym&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RTLD_NEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;original_read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error loading original read function: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dlerror&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;original_write&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orig_write_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;dlsym&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RTLD_NEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"write"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;original_write&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error loading original write function: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dlerror&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;ssize_t&lt;/span&gt; &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sleep_ms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;original_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;ssize_t&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sleep_ms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;original_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, compile the code into a shared library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcc &lt;span class="nt"&gt;-shared&lt;/span&gt; &lt;span class="nt"&gt;-fPIC&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; my_readwrite.so my_readwrite.c &lt;span class="nt"&gt;-ldl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run a program with the library preloaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LD_PRELOAD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./my_readwrite.so find /tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should notice that &lt;code&gt;find&lt;/code&gt; prints results slower than normally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;testing - functions can be overridden to introduce arbitrary slow downs or random failures.&lt;/li&gt;
&lt;li&gt;hot-fixes - shared library can be selectively overridden to change particular behaviour without rebuilding.&lt;/li&gt;
&lt;li&gt;observability - this technique can be used to non-intrusively gather all sort of metrics and statistics.&lt;/li&gt;
&lt;li&gt;alternative implementations - shared library can provide alternative implementations of overridden functions, e.g. &lt;code&gt;malloc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;injecting custom code - preloaded shared libraries can inject arbitrary code on load time via constructor functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other ways to preload a library (Linux only)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;LD_PRELOAD&lt;/code&gt; environment variable.
What was described in this post above.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--preload&lt;/code&gt; command-line option when invoking the dynamic linker directly.
The dynamic linker is a normal executable file so it can executed manually like any other program. E.g. instead of executing &lt;code&gt;ls&lt;/code&gt; one can do the same by executing dynamic linker and passing the executable as an argument: &lt;code&gt;/lib64/ld-linux-x86-64.so.2 /bin/ls&lt;/code&gt; (full path to &lt;code&gt;ls&lt;/code&gt; in required in this case). And this is what actually is being done by the system every time a dynamically linked executable is run. The dynamic linker accepts a few flags, one of them being &lt;code&gt;--preload&lt;/code&gt; and it works the same as &lt;code&gt;LD_PRELOAD&lt;/code&gt; environment variable.&lt;/li&gt;
&lt;li&gt;The /etc/ld.so.preload file.
This configuration file contains a list of white-space separated full paths to shared libraries that should be automatically preloaded by the dynamic linker. The big difference here is that this method preloads libraries for all processes whereas for the previous methods you could preload libraries only for selected processes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security implications
&lt;/h2&gt;

&lt;p&gt;It's important to understand security implications of &lt;code&gt;LD_PRELOAD&lt;/code&gt;. It can be used to override any system call, like &lt;code&gt;open&lt;/code&gt; or &lt;code&gt;execve&lt;/code&gt; and therefore can be used to hijack execution flow or hide malicious behaviour.&lt;/p&gt;

&lt;p&gt;Linux provides some mitigations against that.&lt;br&gt;
if the dynamic linker determines that a binary should be run in secure-execution mode (e.g. when executing a set-user-ID or set-group-ID program) preloading is very limited. E.g. pathnames containing slashes are ignored and shared objects are preloaded only from the standard search directories and only if they have set-user-ID mode bit enabled.&lt;/p&gt;

&lt;p&gt;On FreeBSD &lt;code&gt;LD_PRELOAD&lt;/code&gt; is ignored altogether for set-user-ID and set-group-ID programs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;Some of the uses described above might be achieved with the use of eBPF, but not al of them. eBPF is much more restricted, but also doesn't introduce the possible security issues of injecting arbitrary code.&lt;/p&gt;

&lt;h2&gt;
  
  
  See also
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/serpent7776/crawlio" rel="noopener noreferrer"&gt;crawlio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Dynamic_linker#Systems_using_ELF" rel="noopener noreferrer"&gt;Dynamic linker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man8/ld.so.8.html#:~:text=LD_PRELOAD%20A%20list%20of%20additional%2C,functions%20in%20other%20shared%20objects" rel="noopener noreferrer"&gt;Linux dynamic linker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man.freebsd.org/cgi/man.cgi?query=ld.so#:~:text=LD_PRELOAD%20A%20list%20of%20%20shared%20%20libraries" rel="noopener noreferrer"&gt;FreeBSD dynamic linker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>c</category>
      <category>programming</category>
      <category>linux</category>
    </item>
    <item>
      <title>Publish your first Hive Adventure</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Mon, 03 Mar 2025 20:58:35 +0000</pubDate>
      <link>https://dev.to/serpent7776/publish-your-first-hive-adventure-2l9l</link>
      <guid>https://dev.to/serpent7776/publish-your-first-hive-adventure-2l9l</guid>
      <description>&lt;p&gt;Hello fellow adventurers,&lt;/p&gt;

&lt;p&gt;Today I'd like to announce an important update to the Hive Adventures platform, the place where anyone can create their own choose-your-own-adventure text-only game and publish it for other players.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hive Keychain
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hive-keychain.com/" rel="noopener noreferrer"&gt;Hive Keychain&lt;/a&gt; is a browser extension and mobile app that lets you access Hive powered websites, dApps, and services with your Hive account.&lt;/p&gt;

&lt;p&gt;The website is now integrated with &lt;strong&gt;Hive Keychain&lt;/strong&gt;. This allows you to publish games directly from your browser. All you need to do is to install and set up the extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality of life improvements
&lt;/h2&gt;

&lt;p&gt;As well as this big change, you'll also find some important &lt;strong&gt;Quality of Life Improvements&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The site now remembers your game content across page loads. This makes it easier to iterate on your game design. &lt;/li&gt;
&lt;li&gt;It is now easier to switch between writing mode and game testing mode.&lt;/li&gt;
&lt;li&gt;An additional button has been added to the game test mode to reset the game to its initial state. This makes it easier to test all the paths in your game to make sure everything works as expected.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to get started
&lt;/h2&gt;

&lt;p&gt;Just go to &lt;a href="https://try.hive-adventures.com/" rel="noopener noreferrer"&gt;https://try.hive-adventures.com/&lt;/a&gt; and start creating. You can test your game freely locally in the browser. To publish, you'll need a Hive account and the Hive Keychain web browser extension.&lt;/p&gt;

&lt;p&gt;Have fun playing and creating.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>webdev</category>
      <category>gamedev</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Play your first Hive adventure now</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Tue, 07 Jan 2025 20:34:47 +0000</pubDate>
      <link>https://dev.to/serpent7776/play-your-first-hive-adventure-now-2o53</link>
      <guid>https://dev.to/serpent7776/play-your-first-hive-adventure-now-2o53</guid>
      <description>&lt;p&gt;Hello fellow adventurers.&lt;/p&gt;

&lt;p&gt;I'm building a &lt;a href="https://dev.to/serpent7776/its-adventure-time-but-not-any-adventure-a-hive-adventure-130d"&gt;hive-adventures&lt;/a&gt;, a platform that allows users to create, play, and share CYOA-style text-based games.&lt;/p&gt;

&lt;p&gt;Last time I described &lt;a href="https://dev.to/serpent7776/synchronising-hive-adventures-with-hive-blockchain-167c"&gt;the synchronisation method with the blockchain&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Today I'd like to announce an important next step: a place where you can play the games: &lt;a href="https://hive-adventures.com/adventures" rel="noopener noreferrer"&gt;https://hive-adventures.com/adventures&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a simple website with a list of all stored adventures ready to be played.&lt;br&gt;
Simply click on the game you want to play to start the adventure. The whole gaming experience takes place right in your browser, no further interaction with the server is needed.&lt;br&gt;
The website has a simple and clean theme that helps you to immerse yourself in the adventure.&lt;/p&gt;

&lt;p&gt;There are already some example games you can play.&lt;/p&gt;

&lt;p&gt;The next big step will be to create an easy-to-use interface to publish games on the Hive blockchain.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://hive-adventures.com/adventures" rel="noopener noreferrer"&gt;https://hive-adventures.com/adventures&lt;/a&gt; and start your adventure!&lt;/p&gt;

&lt;p&gt;Have fun playing.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>gamedev</category>
      <category>development</category>
      <category>news</category>
    </item>
    <item>
      <title>Synchronising hive-adventures with Hive blockchain</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Fri, 15 Nov 2024 21:14:12 +0000</pubDate>
      <link>https://dev.to/serpent7776/synchronising-hive-adventures-with-hive-blockchain-167c</link>
      <guid>https://dev.to/serpent7776/synchronising-hive-adventures-with-hive-blockchain-167c</guid>
      <description>&lt;p&gt;Hello fellow adventurers.&lt;/p&gt;

&lt;p&gt;Today I'd like to get a bit more technical and describe how the hive-adventures will synchronise its data with &lt;a href="https://hive.io/" rel="noopener noreferrer"&gt;the Hive blockchain&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The application will read the &lt;code&gt;custom_json_operation&lt;/code&gt;s of the following format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"custom_json_operation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hive-adventure"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required_auths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"anonymous"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required_posting_auths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All these operations are stored locally and then presented to the players as HTML.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; must be set to &lt;code&gt;hive-adventure&lt;/code&gt;. The &lt;code&gt;json&lt;/code&gt; field must contain the game text in the markdown format I described in &lt;a href="https://hive.blog/announcement/@serpent7776/it-s-time-for-adventure-a-hive-adventure" rel="noopener noreferrer"&gt;the announcement blog post&lt;/a&gt;. Either &lt;code&gt;required_auths&lt;/code&gt; or &lt;code&gt;required_posting_auths&lt;/code&gt; must be set to the game's author. Currently only one author is supported.&lt;/p&gt;

&lt;p&gt;On startup, the application looks at the largest block number stored in the local database. It then performs synchronisation in three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First, the application enters &lt;strong&gt;massive sync mode&lt;/strong&gt;.&lt;br&gt;
Once started, the first step is to check the current head of the remote we're syncing with. If the remote's head is larger than ours, we synchronise with that remote, 1000 blocks at a time, until our head matches the remote's head.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the massive sync is complete, the application will go into &lt;strong&gt;sync mode&lt;/strong&gt;. It's similar to massive sync, but it synchronises one block at a time. After each block, it checks if the block time is less than 1 minute from the current time. If it is, the sync mode is finished.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After all this the application goes into &lt;strong&gt;live mode&lt;/strong&gt;.&lt;br&gt;
In live mode, the application synchronises with the remote one block at a time, just like in sync mode. This time, however, it sleeps between blocks to wait just long enough for the next block to be produced.&lt;br&gt;
If a delay of 1,000 blocks or more is detected in live mode, the application will go back to massive sync mode. This shouldn't normally happen, but it can for example if the network goes down. This step is an important optimisation to quickly catch up with the current head in such situations.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The only place where an error will cause the application to quit is in the case of massive synchronisation. It's then up to systemd to restart the app after a while, in hope that the problem was temporary.&lt;/p&gt;

&lt;p&gt;In all these modes, the application first tries to get the data from the hafah instance, and if that fails, from the witness APIs. Hafah is chosen as the primary data source because it allows to query only specific types of operations. In my case I'm only interested in &lt;code&gt;custom_json_operation&lt;/code&gt;s, so it serves as a slight network IO optimisation to fetch only those operations.&lt;/p&gt;

&lt;p&gt;The synchronisation application is already running on my server as described above, but its data is not yet exposed to the public.&lt;/p&gt;

&lt;p&gt;Please let me know if you have any thoughts on synchronising with Hive.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>gamedev</category>
      <category>webdev</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Try your first Hive Adventure now</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Tue, 15 Oct 2024 20:19:21 +0000</pubDate>
      <link>https://dev.to/serpent7776/try-your-first-hive-adventure-now-576o</link>
      <guid>https://dev.to/serpent7776/try-your-first-hive-adventure-now-576o</guid>
      <description>&lt;p&gt;Hello fellow adventurers!&lt;/p&gt;

&lt;p&gt;I'm working on a choose-your-own-adventure (CYOA) text game creation platform built on Hive. If you haven't read it yet, here's the &lt;a href="https://dev.to/serpent7776/its-adventure-time-but-not-any-adventure-a-hive-adventure-130d"&gt;project announcement post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Today I'd like to announce the first publicly available website at &lt;a href="//try.hive-adventures.com"&gt;try.hive-adventures.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The goal
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//try.hive-adventures.com"&gt;try.hive-adventures.com&lt;/a&gt; is a local testing environment where you can play and refine your adventure games right in your browser. Whether you're an experienced game designer or just starting to explore the world of interactive storytelling, this feature offers a safe space for you to experiment, iterate, and perfect your narratives. Think of it as a sandbox environment, where you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Experiment freely&lt;/strong&gt;: take your time and explore various storylines, tweak dialogues, and change endings without the pressure of whether it's ready for an audience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview and play your game&lt;/strong&gt; as if you were the player, allowing you to catch plot holes, improve pacing, and enhance the overall flow of your adventure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Catch any issues or errors&lt;/strong&gt; to ensure that all paths, choices, and endings work exactly as intended.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All this without having to publish the game on the blockchain.&lt;/p&gt;

&lt;p&gt;Whether you're building a sprawling fantasy epic with countless choices or a small, personal narrative, the ability to privately test your game is invaluable. By working iteratively, you can continuously refine your branching paths, dialogue options, and plot twists—without worrying about exposing unfinished work to your audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The current state
&lt;/h2&gt;

&lt;p&gt;The current version is considered to be an alpha version and runs on a cheap VPS. It should be up and running though. Please report any errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  The next steps
&lt;/h2&gt;

&lt;p&gt;I'm currently working on the integration with Hive.&lt;br&gt;
The game data will be read from the blockchain data, translated into an html page and this is what will be served to the user.&lt;br&gt;
I have a rough version working on my local machine, but I need to work on some more integration details.&lt;/p&gt;

&lt;p&gt;That's all for now. Head over to &lt;a href="//try.hive-adventures.com"&gt;try.hive-adventures.com&lt;/a&gt; and create your first adventure today!&lt;/p&gt;

</description>
      <category>web3</category>
      <category>gamedev</category>
      <category>programming</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>It's adventure time, but not any adventure, a Hive Adventure!</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Tue, 03 Sep 2024 15:54:09 +0000</pubDate>
      <link>https://dev.to/serpent7776/its-adventure-time-but-not-any-adventure-a-hive-adventure-130d</link>
      <guid>https://dev.to/serpent7776/its-adventure-time-but-not-any-adventure-a-hive-adventure-130d</guid>
      <description>&lt;p&gt;I'd like to announce the project I'm currently working on: a new choose-your-own-adventure (CYOA) text game creation platform built on &lt;a href="https://hive.io/" rel="noopener noreferrer"&gt;Hive&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With this platform you can easily create and share your own interactive stories, where every decision the player makes affects the outcome of the game.&lt;br&gt;
It is aimed at non-programmers, with an easy way to write stories that guide players through mazes of choices and thrilling outcomes.&lt;br&gt;
It will allow you to create a web of possibilities that will keep the player engaged and eager to explore each path.&lt;/p&gt;
&lt;h2&gt;
  
  
  The game
&lt;/h2&gt;

&lt;p&gt;The whole game is expressed as a &lt;a href="https://www.markdownguide.org/basic-syntax/" rel="noopener noreferrer"&gt;markdown document&lt;/a&gt;. Here's how it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Game title

## Start

Some text describing the situation.

- [choice A label](#choice-a-section)
- [choice B label](#choice-b-section)

## Choice A section

Some more text describing the effect user choice had on the world.

## Choice B section

Some other text describing some other effect user choice had on the world.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Heading contains the name of the game.&lt;br&gt;
The sub-heading indicates the section of the game. Only one section is visible at a time.&lt;br&gt;
&lt;code&gt;Start&lt;/code&gt; is the first section of the game.&lt;/p&gt;

&lt;p&gt;The choices are presented as a list of links to other sections. The choice label is what is presented to the user. The choice link is a slugified section label (this is a limitation of the markdown format - it cannot contain spaces). This basically means that all spaces are replaced by hyphens and the text is lowercased. The hash is optional and can be omitted. If the link is the same as the label, the link can be skipped and left blank. It's then automatically set to the slugified label.&lt;/p&gt;

&lt;p&gt;That's it. You could probably start writing your first game straight away.&lt;/p&gt;

&lt;p&gt;You can have as many choices as you like, though realistically you should limit it to 2-4 per section.&lt;br&gt;
Currently the supported HTML tags are very limited. In particular, images, videos and audio are not available. External links are not supported either.&lt;br&gt;
You can use any other markdown element you like, it doesn't have any special meaning.&lt;br&gt;
There's no limit to the length of the game, but the standard Hive Blockchain rules apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example game
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The Enchanted Forest

## Start

You find yourself at the entrance of a mysterious forest. The path splits in two directions.

- [Take the left path]()
- [Take the right path]()

## Take the left path

You choose the left path. It leads you to a clearing with a sparkling pond.

- [Drink from the pond](#drink)
- [Continue past the pond](#continue)

## Take the right path

You choose the right path. You come across an old, wooden cabin.

- [Knock on the cabin door](#knock)
- [Sneak around the cabin](#sneak)

## Drink

You drink the water and suddenly feel energized. You've gained magical powers!

**The End**

## Continue

As you walk past the pond, you discover a hidden treasure chest!

**The End**

## Knock

An old wizard opens the door and invites you in for tea and magic lessons.

**The End**

## Sneak

While sneaking, you trip on a root and fall into a portal, transporting you to another world!

**The End**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope this has sparked some interest. It's not available to the public yet. I have an initial version running locally on my machine, but it will take some time to get it ready for a wider audience.&lt;/p&gt;

</description>
      <category>cyoa</category>
      <category>gamedev</category>
      <category>web3</category>
      <category>web</category>
    </item>
    <item>
      <title>Comparing C++ range libraries for filter+reverse case with non-trivial lambda</title>
      <dc:creator>Serpent7776</dc:creator>
      <pubDate>Mon, 03 Jun 2024 19:42:42 +0000</pubDate>
      <link>https://dev.to/serpent7776/comparing-c-range-libraries-for-filterreverse-case-with-non-trivial-lambda-4lj7</link>
      <guid>https://dev.to/serpent7776/comparing-c-range-libraries-for-filterreverse-case-with-non-trivial-lambda-4lj7</guid>
      <description>&lt;p&gt;EDIT 2024-06-04: Fixed issue with flux implementation, thanks to &lt;a href="https://github.com/tcbrindle" rel="noopener noreferrer"&gt;tcbrindle&lt;/a&gt; for catching this.&lt;br&gt;
EDIT 2024-06-24: Removed extra allocation from ranges libraries implementations. Thanks again to &lt;a href="https://github.com/tcbrindle" rel="noopener noreferrer"&gt;tcbrindle&lt;/a&gt; for suggesting this. This only affected the 'every other item' test.&lt;/p&gt;

&lt;p&gt;C++ ranges are awesome as they often make the code simpler and easier to reason about. But how they compare against each other? To check that I did a micro-benchmark of c++ ranges for one particular use case I encountered in practice.&lt;/p&gt;

&lt;p&gt;The scenario I tested was as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I have an input data set that needs to be filtered based on non-trivial lambda.&lt;/li&gt;
&lt;li&gt;Only a subset of this data is needed.&lt;/li&gt;
&lt;li&gt;Then an index is added to the elements.&lt;/li&gt;
&lt;li&gt;The last step is to reverse the sequence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repo with source code is here &lt;a href="https://github.com/serpent7776/cpp-ranges-bench" rel="noopener noreferrer"&gt;https://github.com/serpent7776/cpp-ranges-bench&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's basically filter+take+enumerate+reverse, but there are two tricky things here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The non-triviality of the lambda being used for filtering. In the example code, such a lambda is often a trivial function. What happens when the cost of invoking such lambda is not not negligible? &lt;/li&gt;
&lt;li&gt;Filter+reverse is a tricky combination. Filtering yields a result set of unknown size, which means that subsequent reverse cannot be done easily in the general case.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ranges library needs to take both of these situations into account, otherwise it might introduce noticeable overhead.&lt;/p&gt;

&lt;p&gt;Let's check it out.&lt;/p&gt;

&lt;p&gt;The code uses &lt;a href="https://github.com/catchorg/Catch2/" rel="noopener noreferrer"&gt;catch2&lt;/a&gt; to test for correctness and performance.&lt;br&gt;
Catch2 is a unit testing framework for C++, but it also provides basic micro-benchmarking features.&lt;br&gt;
Unfortunately, it doesn't provide any way to visualise the results, so I created a script and submitted &lt;a href="https://github.com/catchorg/Catch2/pull/2869" rel="noopener noreferrer"&gt;PR adding plotting capabilities to Catch&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tested implementations
&lt;/h2&gt;

&lt;p&gt;I have reviewed the following implementations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clike - a simple C-like implementation. This still uses c++ &lt;code&gt;vector&lt;/code&gt; and its methods, but it's just a for loop with if statements. It collects the result in a vector and when it's done, it reverses it in-place.&lt;/li&gt;
&lt;li&gt;algorithms - Manual implementation using STL algorithms. Instead of operating on the elements themselves, it allocates an extra vector with indices and operates on that. It only copies the elements in the last step.&lt;/li&gt;
&lt;li&gt;boost_adaptors - Uses boost::adaptors. It provides very minimal feature set, but it's surprisingly capable. Due to the limitations of adaptors library, it allocates an extra vector of filtered elements and operates on that.&lt;/li&gt;
&lt;li&gt;rangesv3 - Uses &lt;a href="https://github.com/ericniebler/range-v3" rel="noopener noreferrer"&gt;ranges-v3&lt;/a&gt; - range library for C++14/17/20. Very similar to the implementation using std::ranges.&lt;/li&gt;
&lt;li&gt;stdranges - Uses C++23 ranges. This is the shortest and cleanest implementation. The compiler I was using didn't have &lt;code&gt;to&lt;/code&gt; implemented, so I used equivalent function from &lt;code&gt;rangesv3&lt;/code&gt; library. This may have affected the results.&lt;/li&gt;
&lt;li&gt;fluxranges - Uses &lt;a href="https://github.com/tcbrindle/flux" rel="noopener noreferrer"&gt;flux&lt;/a&gt;, a C++20 library for sequence-oriented programming. A bit more noisy compared to other range implementations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used the following versions of the libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ranges-v3 0.12&lt;/li&gt;
&lt;li&gt;fluxranges faf2e0f8b9f616001890a87b613f47313443f8e5&lt;/li&gt;
&lt;li&gt;g++ stdlib, g++ (GCC) 13.2.1 20230801
All compiled in C++23 mode with the &lt;code&gt;-O2&lt;/code&gt; flag.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Test cases
&lt;/h2&gt;

&lt;p&gt;I have generated some random data using the &lt;a href="https://pypi.org/project/Faker/" rel="noopener noreferrer"&gt;faker&lt;/a&gt; python library. The data is a set of usernames with an id and a set of connected names. I will search for usernames that match some criteria which involves looking through the connected names. This should be enough to consider the filter criteria as non-trivial.&lt;br&gt;
To do the comparisons I have generated 10 000 rows of data.&lt;/p&gt;

&lt;p&gt;The test cases I will check are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all ismiths - select all items that have "ismith" as connection. There are 15 such entries in total.&lt;/li&gt;
&lt;li&gt;5 ismiths - select at most 5 items that have "ismith" as connection.&lt;/li&gt;
&lt;li&gt;empty result set - searches for an item that doesn't exist, yielding no results.&lt;/li&gt;
&lt;li&gt;early single item - searches for all items that have "henry79" as connection. There's only one such item, early in the sequence.&lt;/li&gt;
&lt;li&gt;late single item - search for all items that have "emilymclaughlin" as connection. There's only one such item, late in the sequence.&lt;/li&gt;
&lt;li&gt;every other item - accept every other item, while also performing some operation on the input data.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjv4wvct2bgcfdcl5lbop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjv4wvct2bgcfdcl5lbop.png" alt="all_ismiths" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhde7a0ut407mx7wjnxw2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhde7a0ut407mx7wjnxw2.png" alt="5_ismiths" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fle4dqrqjcpdqjacx01di.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fle4dqrqjcpdqjacx01di.png" alt="empty_result_set" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbsz0m123ef81lga6e1qp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbsz0m123ef81lga6e1qp.png" alt="late_single_item" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4mvvp6s2b3hyiwt4lhxt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4mvvp6s2b3hyiwt4lhxt.png" alt="every_other_item" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flux and std::ranges are very similar with very good results. The only exceptions are 'early single item' and 'every other item' cases.&lt;/li&gt;
&lt;li&gt;Ranges-v3 was generally on par with other solutions. There are two cases where it's slower: 'all ismiths' and 'early single item'.&lt;/li&gt;
&lt;li&gt;boost adaptors are overall quite good. In 'all ismiths', 'empty result set' and 'late single item' are on par with fastest implementation and just slightly slower in other cases.&lt;/li&gt;
&lt;li&gt;C-like solution was always on par or better than other solutions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Some more details
&lt;/h2&gt;

&lt;p&gt;Let's try to see some more details. I will look at the 'early single item' test. This case is interesting, because there are big differences between the implementations.&lt;/p&gt;

&lt;p&gt;First, let's run the test case for a few more times. I'll make the test cases run 1000 times by adding this code to each implementation section.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;auto _ = GENERATE(range(1, 1000));&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This uses Catch2 &lt;a href="https://github.com/catchorg/Catch2/blob/devel/docs/generators.md" rel="noopener noreferrer"&gt;data generator&lt;/a&gt; to execute the section multiple times, each time with a different value. In this case I'm not interested in the value itself, I'm only doing this to run the section again.&lt;/p&gt;

&lt;p&gt;Now let's run &lt;code&gt;perf stat&lt;/code&gt; on it. To collect the data I'll will only run this single test case and only one implementation at a time, ignoring the benchmarks. I do this by invoking the compiled binary with the test-case name, the &lt;code&gt;-c&lt;/code&gt; option with a section name that specifies which implementation to use and the &lt;code&gt;--skip-benchmarks&lt;/code&gt; option so that no benchmarks are run. You can read more about &lt;a href="https://github.com/catchorg/Catch2/blob/devel/docs/command-line.md" rel="noopener noreferrer"&gt;Catch2 command line usage&lt;/a&gt;.&lt;br&gt;
&lt;code&gt;perf stat&lt;/code&gt; is invoked with &lt;code&gt;-o stats --append&lt;/code&gt;, which appends the perf data to &lt;code&gt;stats&lt;/code&gt; file. Read more about &lt;a href="https://www.man7.org/linux/man-pages/man1/perf-stat.1.html" rel="noopener noreferrer"&gt;perf stat usage&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; stats  &lt;span class="c"&gt;# truncate stats file&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;t &lt;span class="k"&gt;in &lt;/span&gt;clike algorithms boost_adaptors rangesv3 stdranges fluxranges&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;perf &lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; stats &lt;span class="nt"&gt;--append&lt;/span&gt; ./read &lt;span class="s2"&gt;"early single item"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;$t&lt;/span&gt; &lt;span class="nt"&gt;--skip-benchmarks&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will then convert the &lt;code&gt;stats&lt;/code&gt; file with a &lt;a href="https://github.com/serpent7776/bits/blob/master/perf-stat-to-sc/perf-stat-to-sc.pl" rel="noopener noreferrer"&gt;perf-stat-to-sc&lt;/a&gt; script I wrote. It will create a &lt;a href="https://github.com/andmarti1424/sc-im" rel="noopener noreferrer"&gt;sc-im spreadsheet&lt;/a&gt;, which will make it easier to compare the results.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;command&lt;/th&gt;
&lt;th&gt;context_switches&lt;/th&gt;
&lt;th&gt;cpu_migrations&lt;/th&gt;
&lt;th&gt;page_faults&lt;/th&gt;
&lt;th&gt;cycles&lt;/th&gt;
&lt;th&gt;instructions&lt;/th&gt;
&lt;th&gt;insn_per_cycle&lt;/th&gt;
&lt;th&gt;branches&lt;/th&gt;
&lt;th&gt;branch_misses&lt;/th&gt;
&lt;th&gt;backend_bound&lt;/th&gt;
&lt;th&gt;bad_speculation&lt;/th&gt;
&lt;th&gt;frontend_bound&lt;/th&gt;
&lt;th&gt;retiring&lt;/th&gt;
&lt;th&gt;real&lt;/th&gt;
&lt;th&gt;user&lt;/th&gt;
&lt;th&gt;sys&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;clike&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1,417&lt;/td&gt;
&lt;td&gt;9,763,699,969&lt;/td&gt;
&lt;td&gt;27,310,955,366&lt;/td&gt;
&lt;td&gt;27,310,955,366&lt;/td&gt;
&lt;td&gt;6,611,235,612&lt;/td&gt;
&lt;td&gt;84,103,168&lt;/td&gt;
&lt;td&gt;7.2&lt;/td&gt;
&lt;td&gt;22.2&lt;/td&gt;
&lt;td&gt;21.2&lt;/td&gt;
&lt;td&gt;49.4&lt;/td&gt;
&lt;td&gt;3.573872222&lt;/td&gt;
&lt;td&gt;3.437020000&lt;/td&gt;
&lt;td&gt;0.119355000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rangesv3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1,399&lt;/td&gt;
&lt;td&gt;11,634,561,888&lt;/td&gt;
&lt;td&gt;29,211,691,985&lt;/td&gt;
&lt;td&gt;29,211,691,985&lt;/td&gt;
&lt;td&gt;7,103,602,542&lt;/td&gt;
&lt;td&gt;123,617,995&lt;/td&gt;
&lt;td&gt;8.1&lt;/td&gt;
&lt;td&gt;22.9&lt;/td&gt;
&lt;td&gt;22.8&lt;/td&gt;
&lt;td&gt;46.2&lt;/td&gt;
&lt;td&gt;4.132017782&lt;/td&gt;
&lt;td&gt;4.001041000&lt;/td&gt;
&lt;td&gt;0.116310000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;boost_adaptors&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1,394&lt;/td&gt;
&lt;td&gt;10,715,252,768&lt;/td&gt;
&lt;td&gt;28,262,420,566&lt;/td&gt;
&lt;td&gt;28,262,420,566&lt;/td&gt;
&lt;td&gt;6,857,638,043&lt;/td&gt;
&lt;td&gt;104,769,454&lt;/td&gt;
&lt;td&gt;7.0&lt;/td&gt;
&lt;td&gt;24.6&lt;/td&gt;
&lt;td&gt;21.7&lt;/td&gt;
&lt;td&gt;46.7&lt;/td&gt;
&lt;td&gt;3.909084680&lt;/td&gt;
&lt;td&gt;3.807669000&lt;/td&gt;
&lt;td&gt;0.083028000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fluxranges&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1,394&lt;/td&gt;
&lt;td&gt;10,775,098,223&lt;/td&gt;
&lt;td&gt;28,262,194,528&lt;/td&gt;
&lt;td&gt;28,262,194,528&lt;/td&gt;
&lt;td&gt;6,857,615,031&lt;/td&gt;
&lt;td&gt;104,014,045&lt;/td&gt;
&lt;td&gt;7.6&lt;/td&gt;
&lt;td&gt;25.1&lt;/td&gt;
&lt;td&gt;21.4&lt;/td&gt;
&lt;td&gt;46.0&lt;/td&gt;
&lt;td&gt;3.965913088&lt;/td&gt;
&lt;td&gt;3.814496000&lt;/td&gt;
&lt;td&gt;0.132746000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stdranges&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1,413&lt;/td&gt;
&lt;td&gt;10,731,793,760&lt;/td&gt;
&lt;td&gt;28,251,095,766&lt;/td&gt;
&lt;td&gt;28,251,095,766&lt;/td&gt;
&lt;td&gt;6,857,354,712&lt;/td&gt;
&lt;td&gt;104,070,967&lt;/td&gt;
&lt;td&gt;7.6&lt;/td&gt;
&lt;td&gt;23.0&lt;/td&gt;
&lt;td&gt;21.7&lt;/td&gt;
&lt;td&gt;47.7&lt;/td&gt;
&lt;td&gt;3.955925504&lt;/td&gt;
&lt;td&gt;3.844894000&lt;/td&gt;
&lt;td&gt;0.092972000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;algorithms&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;7,623&lt;/td&gt;
&lt;td&gt;10,706,979,451&lt;/td&gt;
&lt;td&gt;28,341,604,071&lt;/td&gt;
&lt;td&gt;28,341,604,071&lt;/td&gt;
&lt;td&gt;6,867,444,605&lt;/td&gt;
&lt;td&gt;104,582,547&lt;/td&gt;
&lt;td&gt;6.9&lt;/td&gt;
&lt;td&gt;25.5&lt;/td&gt;
&lt;td&gt;21.5&lt;/td&gt;
&lt;td&gt;46.1&lt;/td&gt;
&lt;td&gt;3.911286798&lt;/td&gt;
&lt;td&gt;3.801366000&lt;/td&gt;
&lt;td&gt;0.089797000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first thing to notice is the difference in the number of page faults. Most implementations had about 1400 faults. The exceptions are custom algorithms with ~7600 faults. The differences in cycles and instructions are quite small. It's a bit bigger for rangesv3 and smaller for C-like though. The number of branches is also similar, slightly higher for rangesv3 and slightly lower for C-like. There are bigger differences in branch misses. The C-like implementation has the least number of misses. Rangesv3 have the most misses.&lt;/p&gt;

&lt;p&gt;But this collects stats from the entire program run. The issue is that most of the time is spent on reading and parsing the input file, which is the same in all cases. I want to collect stats only for the single implementation function. For this I can use &lt;code&gt;valgrind&lt;/code&gt;'s &lt;code&gt;cachegrind&lt;/code&gt; with additional code instrumentation.&lt;br&gt;
I will add &lt;code&gt;CACHEGRIND_START_INSTRUMENTATION&lt;/code&gt; and &lt;code&gt;CACHEGRIND_STOP_INSTRUMENTATION&lt;/code&gt; just before and after the implementation function I'm interested in and then run the binary with &lt;code&gt;--instr-at-start=no&lt;/code&gt; which will disable instrumentation at startup and will wait for the first &lt;code&gt;CACHEGRIND_START_INSTRUMENTATION&lt;/code&gt; to enable it.&lt;br&gt;
Read more about &lt;a href="https://www.man7.org/linux/man-pages/man1/valgrind.1.html" rel="noopener noreferrer"&gt;cachegrind usage&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;    &lt;span class="n"&gt;SECTION&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"clike"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GENERATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;CACHEGRIND_START_INSTRUMENTATION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;CACHEGRIND_STOP_INSTRUMENTATION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;REQUIRE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;valgrind &lt;span class="nt"&gt;--tool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cachegrind &lt;span class="nt"&gt;--instr-at-start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no ./read &lt;span class="s1"&gt;'early single item'&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; fluxranges
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the data I got:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;implementation&lt;/th&gt;
&lt;th&gt;instructions executed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;clike&lt;/td&gt;
&lt;td&gt;128 871&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;algorithms&lt;/td&gt;
&lt;td&gt;274 757 967&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;boost_adaptors&lt;/td&gt;
&lt;td&gt;274 757 967&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rangesv3&lt;/td&gt;
&lt;td&gt;549 464 985&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stdranges&lt;/td&gt;
&lt;td&gt;274 757 967&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fluxranges&lt;/td&gt;
&lt;td&gt;274 757 967&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It's a bit strange that all the results except for clike and rangesv3 are the same.&lt;br&gt;
But this confirms that C-like executes far fever instructions than other implementations and rangesv3, the most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Ranges are really cool. They often make the code easier to read and reason about. But they do not always fit all the use cases. Sometimes, in some very specific cases, a for loop might give more control that is needed to get some better results. In general though, it's usually better to start with a ranges, as they will be good enough in most cases.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>performance</category>
      <category>benchmark</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
