<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Linux on Clément Joly – Open-Source, Rust &amp; SQLite</title><link>https://joly.pw/tags/linux/</link><description>Recent content in Linux on Clément Joly – Open-Source, Rust &amp; SQLite</description><image><title>Clément Joly – Open-Source, Rust &amp; SQLite</title><url>https://joly.pw/images/open-graph-pages.jpg</url><link>https://joly.pw/images/open-graph-pages.jpg</link></image><generator>Hugo</generator><language>en</language><copyright>Clément Joly</copyright><lastBuildDate>Thu, 06 Aug 2026 00:41:55 +0100</lastBuildDate><atom:link href="https://joly.pw/tags/linux/index.xml" rel="self" type="application/rss+xml"/><item><title>Should I Compress My Initramfs?</title><link>https://joly.pw/blog/should-i-compress-my-initramfs/</link><pubDate>Wed, 31 Aug 2022 06:47:08 +0100</pubDate><guid>https://joly.pw/blog/should-i-compress-my-initramfs/</guid><description>When decompressing is faster than reading from the disk.</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-tldr">
    <p class="alert-heading">
      ⚡
      
        TL;DR
      
    </p>
    <p>As a small start-up time optimization, you can pick the best suited compression algorithm for the initial ramdisk.</p>
  </div>



<h2 id="the-initial-ramdisk">The Initial Ramdisk</h2>
<p>When a Linux system boots, it needs to mount the root filesystem <code>/</code>.
This may be relatively complicated, as it may be on a software RAID, on LVM, encrypted…
To keep things manageable, an <a href="https://en.wikipedia.org/wiki/Initial_ramdisk">initial ramdisk</a> can be used to get a small environment that has all the required modules and configuration to load the root filesystem.
On <a href="https://archlinux.org/">Arch Linux</a>, this initial ramdisk is generated using <a href="https://wiki.archlinux.org/title/Mkinitcpio">mkinitcpio</a>.
It takes multiple parameters to tune various aspects of the system and of the generated ramdisk.</p>
<h3 id="compression">Compression</h3>
<p>One such parameter is <code>COMPRESSION</code>.
It compresses the ramdisk to make the resulting image smaller.
The <a href="https://man.archlinux.org/man/mkinitcpio.conf.5.en#VARIABLES">manpage</a> reads:</p>




  <figure>
    <blockquote cite="https://man.archlinux.org/man/mkinitcpio.conf.5.en#VARIABLES">
      <h4 id="compression-1">COMPRESSION</h4>
<p>Defines a program to filter the generated image through. The kernel understands the compression formats yielded by the zstd, gzip, bzip2, lz4, lzop, lzma, and xz compressors. If unspecified, this setting defaults to zstd compression. In order to create an uncompressed image, define this variable as cat.</p>

    </blockquote>
    
      <figcaption class="blockquote-caption">
        
          <cite style="text-align: right"><a href="https://man.archlinux.org/man/mkinitcpio.conf.5.en#VARIABLES">https://man.archlinux.org/man/mkinitcpio.conf.5.en#VARIABLES</a></cite>
          <br/>
        
        
      </figcaption>
    
  </figure>



<p>Another reason to compress the image is that it may reduce the start-up time.
To understand why, imagine that the image is 100 MiB in size and only 20 MiB after compression.
Let&rsquo;s say that the disk reads 10 MiB<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> per second and that the CPU can decompress the full image in 1 second.
If we keep the image uncompressed, the disk will need <em>10 seconds</em> to read the uncompressed image, while it needs only 2 seconds to read the compressed image.
Adding the decompression time, the compressed version require only <em>3 seconds</em>.</p>
<h3 id="trade-offs">Trade-offs</h3>
<p>The above example is quite simple, but it illustrates the trade-off between a bigger image that the disk will take longer to read and a smaller image that may take longer to decompress. It is thus more of a spectrum, where more CPU-intensive compression (and decompression) methods could result in a smaller image and less read from the disk but more CPU time:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 760 73"
      >
      <g transform='translate(8,16)'>
<path d='M 16,32 L 712,32' fill='none' stroke='currentColor'></path>
<polygon points='24.000000,32.000000 12.000000,26.400000 12.000000,37.599998' fill='currentColor' transform='rotate(180.000000, 16.000000, 32.000000)'></polygon>
<polygon points='720.000000,32.000000 708.000000,26.400000 708.000000,37.599998' fill='currentColor' transform='rotate(0.000000, 712.000000, 32.000000)'></polygon>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='16' y='52' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='24' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='32' y='52' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='40' y='52' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='48' y='52' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='56' y='52' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>U</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='88' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='96' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='52' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='368' y='52' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='376' y='52' fill='currentColor' style='font-size:1em'>z</text>
<text text-anchor='middle' x='384' y='52' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='640' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='648' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='656' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='656' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='664' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='664' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='672' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='680' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='680' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='688' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='688' y='52' fill='currentColor' style='font-size:1em'>z</text>
<text text-anchor='middle' x='696' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='696' y='20' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='696' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='704' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='704' y='20' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='704' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='712' y='4' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='712' y='20' fill='currentColor' style='font-size:1em'>U</text>
<text text-anchor='middle' x='712' y='52' fill='currentColor' style='font-size:1em'>d</text>
</g>

    </svg>
  
</div>
<p>Then, the question is: is it worth compressing an image more (or at all), to get a faster start-up time?</p>
<h2 id="protocol">Protocol</h2>
<p>To answer this question <strong>on a particular machine</strong><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, let’s compare the time required to read and decompress various initial ramdisks.</p>
<p>I’m using the <code>linux</code> package in version <em>5.18.15-arch1-2</em> from the Arch Linux repository. Then, I generate (<code>sudo mkinitcpio -p linux</code>) various images with the following parameters in <code>/etc/mkinitcpio.conf</code>:</p>
<ul>
<li><code>COMPRESSION=&quot;cat&quot;</code></li>
<li><code>COMPRESSION=&quot;lz4&quot;</code></li>
<li><code>COMPRESSION=&quot;zstd&quot;</code></li>
</ul>
<p>Each image is copied in a directory and renamed according to the compression used: <code>cp /boot/initramfs-linux.img initramfs-linux.img.zstd</code>.
The result is as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>$ file *.img*
</span></span><span style="display:flex;"><span>initramfs-linux.img:      ASCII cpio archive <span style="color:#56b6c2">(</span>SVR4 with no CRC<span style="color:#56b6c2">)</span>
</span></span><span style="display:flex;"><span>initramfs-linux.img.lz4:  LZ4 compressed data <span style="color:#56b6c2">(</span>v0.1-v0.9<span style="color:#56b6c2">)</span>
</span></span><span style="display:flex;"><span>initramfs-linux.img.zstd: Zstandard compressed data <span style="color:#56b6c2">(</span>v0.8+<span style="color:#56b6c2">)</span>, Dictionary ID: None
</span></span></code></pre></div><p>The images are compressing quite well too:</p>
<table id="files-size">
  <thead>
      <tr>
          <th>File</th>
          <th>Size (MiB)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>initramfs-linux.img</td>
          <td>61M</td>
      </tr>
      <tr>
          <td>initramfs-linux.img.lz4</td>
          <td>32M</td>
      </tr>
      <tr>
          <td>initramfs-linux.img.zstd</td>
          <td>22M</td>
      </tr>
  </tbody>
</table>
<p>We could compare more algorithms and compression level, but compression levels would need to be passed through <code>COMPRESSION_OPTIONS</code>, which the <a href="https://man.archlinux.org/man/mkinitcpio.conf.5.en#VARIABLES">manpage</a> discourages, as it can result in an unbootable image.</p>
<h2 id="results">Results</h2>
<p>Let’s run some decompression commands and compare their run-time with <a href="https://github.com/sharkdp/hyperfine">hyperfine</a>. On a quiet computer:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>$ hyperfine <span style="color:#98c379">\
</span></span></span><span style="display:flex;"><span>    --prepare <span style="color:#98c379">&#39;sync; echo 3 | sudo tee /proc/sys/vm/drop_caches&#39;</span> <span style="color:#98c379">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#98c379">&#39;lz4 -d &lt;./initramfs-linux.img.lz4&#39;</span> <span style="color:#98c379">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#98c379">&#39;zstd -d &lt;initramfs-linux.img.zstd&#39;</span> <span style="color:#98c379">\
</span></span></span><span style="display:flex;"><span>    <span style="color:#98c379">&#39;cat &lt;initramfs-linux.img&#39;</span>
</span></span></code></pre></div><p>Note that the command has a <code>--prepare 'sync; echo 3 | sudo tee /proc/sys/vm/drop_caches'</code> argument.
This empties the OS file system caches to be closer to start-up conditions: when the computer starts, everything has to be read from the disk as the RAM is basically empty.
Without this <code>--prepare</code> argument, we get much shorter times, e.g. 45ms for lz4.</p>
<p>Here are the results:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Command</th>
          <th style="text-align: right">Mean [ms]</th>
          <th style="text-align: right">Min [ms]</th>
          <th style="text-align: right">Max [ms]</th>
          <th style="text-align: right">Relative</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><code>lz4 -d &lt;./initramfs-linux.img.lz4</code></td>
          <td style="text-align: right">137.9 ± 13.5</td>
          <td style="text-align: right">122.8</td>
          <td style="text-align: right">157.4</td>
          <td style="text-align: right">1.00</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>zstd -d &lt;initramfs-linux.img.zstd</code></td>
          <td style="text-align: right">164.9 ± 13.4</td>
          <td style="text-align: right">153.6</td>
          <td style="text-align: right">187.9</td>
          <td style="text-align: right">1.20 ± 0.15</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>cat &lt;initramfs-linux.img</code></td>
          <td style="text-align: right">175.9 ± 19.0</td>
          <td style="text-align: right">157.9</td>
          <td style="text-align: right">218.8</td>
          <td style="text-align: right">1.28 ± 0.19</td>
      </tr>
  </tbody>
</table>
<p>Lz4 is slightly faster, followed by zstd and no compression at all with <code>cat</code>.
If we go back to the <a href="#files-size">sizes</a> table, the trade-off between a smaller image but a slower decompression is clear.
Despite a ~30% smaller file size, zstd is still a bit slower to decompress than lz4, while no compression at all is even worse.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The above results are based on runs on a particular machine.
As mentioned different machines will yield different results, depending on the relative performance of the disk and the CPU.
It’s also a pretty small improvement in the grand scheme of things: only a few tens of milliseconds on a process that takes a couple seconds.
But I found it to be a nice example of how compression can make things faster, compared to no compression at all, because CPU nowadays are so fast.</p>
<hr>
<h2 id="appendix-recording-of-the-hyperfine-run">Appendix: Recording of the <code>hyperfine</code> Run</h2>
<p>This was done on a different run from the table above, as running the benchmark through Asciinema is sometimes a bit less stable):
<div id="demo3"></div>
<script>
AsciinemaPlayer.create("/blog/should-i-compress-my-initramfs/hyperfine.json", document.getElementById('demo3'), {
"autoPlay":  false ,"loop":  false ,"poster": "npt:0:16",
});
</script>
<noscript><blockquote><p>To run this asciicast without javascript, use <code>asciinema play https://joly.pw/blog/should-i-compress-my-initramfs/hyperfine.json</code> with <a href="https://asciinema.org/">Asciinema</a></p></blockquote></noscript>
</p>




  
  
  
  

  <div class="alert alert-edit">
    <p class="alert-heading">
      ✏
      
        Edit
      
    </p>
    <ul>
<li>2022-08-02: Slightly reworded the intro to account for <a href="https://fosstodon.org/@pixelherodev/108927525223368261">https://fosstodon.org/@pixelherodev/108927525223368261</a></li>
<li>2022-11-13: As pointed out in <a href="https://www.reddit.com/r/archlinux/comments/ytk6t3/comment/iw74omr/">this comment on Reddit</a>, sometimes one wants to trade CPU time for disc space:




  <figure>
    <blockquote >
      <p>Another point: by default, Windows made a 100Mo EFI partition on my system. To conserve the dual boot capabilities without having to modify my partition scheme in ways I am not comfortable with, I have to compress my initramfs or it will literally not fit in the partition.</p>

    </blockquote>
    
  </figure>



</li>
<li>2022-11-14: Clarify that the 10 MiB reading speed is made up for the example.</li>
</ul>
  </div>



<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>That’s a completely made up number for the sake of the example.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>The conclusions will in all likelihood change depending on the machine, namely the relative performance of the CPU and the disk.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>