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

<channel>
	<title>Education &#8211; Wade Tregaskis</title>
	<atom:link href="https://wadetregaskis.com/categories/education/feed/" rel="self" type="application/rss+xml" />
	<link>https://wadetregaskis.com</link>
	<description></description>
	<lastBuildDate>Wed, 30 Jul 2025 20:25:35 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://wadetregaskis.com/wp-content/uploads/2016/03/Stitch-512x512-1-256x256.png</url>
	<title>Education &#8211; Wade Tregaskis</title>
	<link>https://wadetregaskis.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">226351702</site>	<item>
		<title>32-bit float audio recording is not a panacea</title>
		<link>https://wadetregaskis.com/32-bit-float-audio-recording-is-not-a-panacea/</link>
					<comments>https://wadetregaskis.com/32-bit-float-audio-recording-is-not-a-panacea/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Thu, 31 Jul 2025 03:18:00 +0000</pubDate>
				<category><![CDATA[Education]]></category>
		<category><![CDATA[Reviews]]></category>
		<category><![CDATA[32-bit float audio]]></category>
		<category><![CDATA[audio]]></category>
		<category><![CDATA[lies]]></category>
		<category><![CDATA[Portacapture X8]]></category>
		<category><![CDATA[Sennheiser K6]]></category>
		<category><![CDATA[Sennheiser ME65]]></category>
		<category><![CDATA[Sennheiser ME66]]></category>
		<category><![CDATA[Tascam]]></category>
		<category><![CDATA[Tested]]></category>
		<category><![CDATA[Zoom H4n]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=8524</guid>

					<description><![CDATA[I recently replaced a horrible, dodgy Zoom H4n with a Tascam Portacapture X8, for recording (primarily) theatre and music performances. One of the appeals was 32-bit floating-point recording which was literally promised to eliminate concerns about input levelling, clipping, and noise: The reality is, with 32-bit float recording you can turn on your recorder, hit&#8230; <a class="read-more-link" href="https://wadetregaskis.com/32-bit-float-audio-recording-is-not-a-panacea/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>I recently replaced a horrible, dodgy <a href="https://zoomcorp.com/en/us/handheld-recorders/handheld-recorders/h4n/" data-wpel-link="external" target="_blank" rel="external noopener">Zoom H4n</a> with a <a href="https://tascam.com/us/product/portacapture_x8" data-wpel-link="external" target="_blank" rel="external noopener">Tascam Portacapture X8</a>, for recording (primarily) theatre and music performances. One of the appeals was 32-bit floating-point recording which was literally promised to eliminate concerns about input levelling, clipping, and noise:</p>



<figure class="wp-block-pullquote is-style-default"><blockquote><p>The reality is, with 32-bit float recording you can turn on your recorder, hit record, and be 100% confident that <strong>you&#8217;ll be capturing high-fidelity, low-noise audio, without ever adjusting your input level</strong>.</p><cite><a href="https://tascam.jp/int/feature/32-bit_float" data-wpel-link="external" target="_blank" rel="external noopener">Why 32-bit Float Recording</a>, Tascam</cite></blockquote></figure>



<figure class="wp-block-pullquote"><blockquote><p>…the huge dynamic range that 32-bit float offers means your audio is always captured well above the noise floor, and also <strong>makes it basically impossible to distort due to high input levels</strong>.</p><cite><a href="https://tascam.jp/int/feature/32-bit_float" data-wpel-link="external" target="_blank" rel="external noopener">Why 32-bit Float Recording</a>, Tascam</cite></blockquote></figure>



<p>Five minutes of some trivial testing shows that this is just not true.</p>



<h2 class="wp-block-heading">Noise is still affected by input gain</h2>



<p>Here&#8217;s a composed recording of four 3-second clips recording the room tone in my office. They are (in order): Auto gain, 57dB, 35dB, 0dB:</p>


<div
    id="h5ap-player-1"
    data-id="h5ap-player-1"
    data-attributes="{&quot;uniqueId&quot;:&quot;h5apc2f6cbfb&quot;,&quot;source&quot;:&quot;https:\/\/wadetregaskis.com\/wp-content\/uploads\/2025\/07\/Noise.wav&quot;,&quot;primaryColor&quot;:&quot;#4A5464&quot;,&quot;controlColor&quot;:&quot;#4A5464&quot;,&quot;bgColor&quot;:&quot;#F5F5F5&quot;,&quot;loader&quot;:true,&quot;preload&quot;:&quot;auto&quot;,&quot;radius&quot;:&quot;50px&quot;,&quot;clientId&quot;:&quot;&quot;,&quot;align&quot;:&quot;&quot;,&quot;alignment&quot;:&quot;left&quot;,&quot;poster&quot;:&quot;&quot;,&quot;title&quot;:&quot;audio title&quot;,&quot;artist&quot;:&quot;&quot;,&quot;color&quot;:&quot;#87ceeb&quot;,&quot;hoverColor&quot;:&quot;#00B3FF&quot;,&quot;skin&quot;:&quot;Default&quot;,&quot;repeat&quot;:false,&quot;autoplay&quot;:false,&quot;muted&quot;:false,&quot;saveState&quot;:false,&quot;disablePause&quot;:false,&quot;seekTime&quot;:10,&quot;startTime&quot;:0,&quot;download&quot;:true,&quot;width&quot;:&quot;100%&quot;,&quot;speed&quot;:{&quot;selected&quot;:1,&quot;speed&quot;:[&quot;0.5&quot;,&quot; 0.75&quot;,&quot; 1&quot;,&quot; 1.25&quot;,&quot; 1.5&quot;,&quot; 1.75&quot;,&quot; 2&quot;,&quot; 4&quot;,&quot; 8&quot;]},&quot;controls&quot;:{&quot;restart&quot;:false,&quot;rewind&quot;:false,&quot;play&quot;:true,&quot;fast-forward&quot;:false,&quot;progress&quot;:true,&quot;duration&quot;:false,&quot;current-time&quot;:true,&quot;mute&quot;:true,&quot;volume&quot;:true,&quot;settings&quot;:true,&quot;download&quot;:false},&quot;isSticky&quot;:false,&quot;CSS&quot;:&quot;&quot;,&quot;multiple_audio&quot;:false,&quot;defaultValue&quot;:{&quot;Default&quot;:{&quot;primaryColor&quot;:&quot;#4A5464&quot;,&quot;controlColor&quot;:&quot;#4A5464&quot;,&quot;bgColor&quot;:&quot;#F5F5F5&quot;},&quot;Fusion&quot;:{&quot;primaryColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;controlColor&quot;:&quot;#fff&quot;},&quot;Stamp&quot;:{&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;},&quot;Wave&quot;:{&quot;primaryColor&quot;:&quot;#fff&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;},&quot;Card-1&quot;:{&quot;primaryColor&quot;:&quot;#fff&quot;,&quot;controlColor&quot;:&quot;#161616&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;},&quot;Card-2&quot;:{&quot;primaryColor&quot;:&quot;#00FBF3&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;},&quot;Simple-1&quot;:{&quot;primaryColor&quot;:&quot;#00dcff&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#00dcff&quot;},&quot;Simple-2&quot;:{&quot;primaryColor&quot;:&quot;#06F7FF00&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;},&quot;Player9&quot;:{&quot;primaryColor&quot;:&quot;#195FF6&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#EDEFF2&quot;},&quot;Player10&quot;:{&quot;primaryColor&quot;:&quot;#195FF6&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#EDEFF2&quot;},&quot;Player11&quot;:{&quot;primaryColor&quot;:&quot;#195FF6&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;}},&quot;i18n&quot;:{&quot;restart&quot;:&quot;Restart&quot;,&quot;rewind&quot;:&quot;Rewind {seektime}s&quot;,&quot;play&quot;:&quot;Play&quot;,&quot;pause&quot;:&quot;Pause&quot;,&quot;fastForward:&quot;:&quot;Forward {seektime}s&quot;,&quot;seek&quot;:&quot;Seek&quot;,&quot;seekLabel&quot;:&quot;{currentTime} of {duration}&quot;,&quot;played&quot;:&quot;Played&quot;,&quot;buffered&quot;:&quot;Buffered&quot;,&quot;currentTime:&quot;:&quot;Current time&quot;,&quot;duration&quot;:&quot;Duration&quot;,&quot;volume&quot;:&quot;Volume&quot;,&quot;mute&quot;:&quot;Mute&quot;,&quot;unmute&quot;:&quot;Unmute&quot;,&quot;enableCaptions&quot;:&quot;Enable captions&quot;,&quot;disableCaptions&quot;:&quot;Disable captions&quot;,&quot;download&quot;:&quot;Download&quot;,&quot;enterFullscreen&quot;:&quot;Enter fullscreen&quot;,&quot;exitFullscreen&quot;:&quot;Exit fullscreen&quot;,&quot;frameTitle&quot;:&quot;Player for {title}&quot;,&quot;captions&quot;:&quot;Captions&quot;,&quot;settings&quot;:&quot;Settings&quot;,&quot;pip&quot;:&quot;PIP&quot;,&quot;menuBack&quot;:&quot;Go back to previous menu&quot;,&quot;speed&quot;:&quot;Speed&quot;,&quot;normal&quot;:&quot;Normal&quot;,&quot;quality&quot;:&quot;Quality&quot;,&quot;loop&quot;:&quot;Loop&quot;,&quot;start&quot;:&quot;Start&quot;,&quot;end&quot;:&quot;End&quot;,&quot;all&quot;:&quot;All&quot;,&quot;reset&quot;:&quot;Reset&quot;,&quot;disabled&quot;:&quot;Disabled&quot;,&quot;enabled&quot;:&quot;Enabled&quot;,&quot;advertisement&quot;:&quot;Ad&quot;,&quot;qualityBadge&quot;:{&quot;2160&quot;:&quot;4K&quot;,&quot;1440&quot;:&quot;HD&quot;,&quot;1080&quot;:&quot;HD&quot;,&quot;720&quot;:&quot;HD&quot;,&quot;576&quot;:&quot;SD&quot;,&quot;480&quot;:&quot;SD&quot;}}}"
    class="wp-block-h5ap-tailwind wp-block-h5ap-audioplayer">
            <div class='h5ap_lp'>
            <div class='bar bar-1'></div>
            <div class='bar bar-1'></div>
        </div>
    </div>


<p>As you can hear, auto and maximum input gain in this case have very similar noise levels (which is to say, perceptually none), but as you reduce the recorder&#8217;s input gain (and instead apply the gain in post) the noise increases substantially and becomes very noticeable.</p>



<p>Granted this is a very big gain application &#8211; 57dB &#8211; which you would <em>hopefully</em> never need to apply to a real recording, but nonetheless it demonstrates that Tascam&#8217;s claims are exaggerations at best; if you <em>actually</em> had the Portacapture X8&#8217;s input gain set to 0dB and recorded quiet sounds, you would in fact have problems with noise &#8211; problems that would be avoided with a correct input gain setting.</p>



<h2 class="wp-block-heading">Clipping still happens if input gain is too high</h2>



<p>The recorder clearly applies actual analog amplification and can still saturate its ADCs, as shown in this composite of three gain levels.  They are (in order):  Auto gain, 35dB, 57dB.</p>



<p><em>Warning</em>: annoying, distorted sound.</p>


<div
    id="h5ap-player-2"
    data-id="h5ap-player-2"
    data-attributes="{&quot;uniqueId&quot;:&quot;h5ap1fcf0b2b&quot;,&quot;source&quot;:&quot;https:\/\/wadetregaskis.com\/wp-content\/uploads\/2025\/07\/Tascam-Portacapture-X8-w-Sennheiser-K6-ME66-clipping-demonstration.wav&quot;,&quot;primaryColor&quot;:&quot;#4A5464&quot;,&quot;controlColor&quot;:&quot;#4A5464&quot;,&quot;bgColor&quot;:&quot;#F5F5F5&quot;,&quot;loader&quot;:true,&quot;preload&quot;:&quot;auto&quot;,&quot;radius&quot;:&quot;50px&quot;,&quot;clientId&quot;:&quot;&quot;,&quot;align&quot;:&quot;&quot;,&quot;alignment&quot;:&quot;left&quot;,&quot;poster&quot;:&quot;&quot;,&quot;title&quot;:&quot;audio title&quot;,&quot;artist&quot;:&quot;&quot;,&quot;color&quot;:&quot;#87ceeb&quot;,&quot;hoverColor&quot;:&quot;#00B3FF&quot;,&quot;skin&quot;:&quot;Default&quot;,&quot;repeat&quot;:false,&quot;autoplay&quot;:false,&quot;muted&quot;:false,&quot;saveState&quot;:false,&quot;disablePause&quot;:false,&quot;seekTime&quot;:10,&quot;startTime&quot;:0,&quot;download&quot;:true,&quot;width&quot;:&quot;100%&quot;,&quot;speed&quot;:{&quot;selected&quot;:1,&quot;speed&quot;:[&quot;0.5&quot;,&quot; 0.75&quot;,&quot; 1&quot;,&quot; 1.25&quot;,&quot; 1.5&quot;,&quot; 1.75&quot;,&quot; 2&quot;,&quot; 4&quot;,&quot; 8&quot;]},&quot;controls&quot;:{&quot;restart&quot;:false,&quot;rewind&quot;:false,&quot;play&quot;:true,&quot;fast-forward&quot;:false,&quot;progress&quot;:true,&quot;duration&quot;:false,&quot;current-time&quot;:true,&quot;mute&quot;:true,&quot;volume&quot;:true,&quot;settings&quot;:true,&quot;download&quot;:false},&quot;isSticky&quot;:false,&quot;CSS&quot;:&quot;&quot;,&quot;multiple_audio&quot;:false,&quot;defaultValue&quot;:{&quot;Default&quot;:{&quot;primaryColor&quot;:&quot;#4A5464&quot;,&quot;controlColor&quot;:&quot;#4A5464&quot;,&quot;bgColor&quot;:&quot;#F5F5F5&quot;},&quot;Fusion&quot;:{&quot;primaryColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;controlColor&quot;:&quot;#fff&quot;},&quot;Stamp&quot;:{&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;},&quot;Wave&quot;:{&quot;primaryColor&quot;:&quot;#fff&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;},&quot;Card-1&quot;:{&quot;primaryColor&quot;:&quot;#fff&quot;,&quot;controlColor&quot;:&quot;#161616&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;},&quot;Card-2&quot;:{&quot;primaryColor&quot;:&quot;#00FBF3&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;},&quot;Simple-1&quot;:{&quot;primaryColor&quot;:&quot;#00dcff&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#00dcff&quot;},&quot;Simple-2&quot;:{&quot;primaryColor&quot;:&quot;#06F7FF00&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;},&quot;Player9&quot;:{&quot;primaryColor&quot;:&quot;#195FF6&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#EDEFF2&quot;},&quot;Player10&quot;:{&quot;primaryColor&quot;:&quot;#195FF6&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#EDEFF2&quot;},&quot;Player11&quot;:{&quot;primaryColor&quot;:&quot;#195FF6&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;}},&quot;i18n&quot;:{&quot;restart&quot;:&quot;Restart&quot;,&quot;rewind&quot;:&quot;Rewind {seektime}s&quot;,&quot;play&quot;:&quot;Play&quot;,&quot;pause&quot;:&quot;Pause&quot;,&quot;fastForward:&quot;:&quot;Forward {seektime}s&quot;,&quot;seek&quot;:&quot;Seek&quot;,&quot;seekLabel&quot;:&quot;{currentTime} of {duration}&quot;,&quot;played&quot;:&quot;Played&quot;,&quot;buffered&quot;:&quot;Buffered&quot;,&quot;currentTime:&quot;:&quot;Current time&quot;,&quot;duration&quot;:&quot;Duration&quot;,&quot;volume&quot;:&quot;Volume&quot;,&quot;mute&quot;:&quot;Mute&quot;,&quot;unmute&quot;:&quot;Unmute&quot;,&quot;enableCaptions&quot;:&quot;Enable captions&quot;,&quot;disableCaptions&quot;:&quot;Disable captions&quot;,&quot;download&quot;:&quot;Download&quot;,&quot;enterFullscreen&quot;:&quot;Enter fullscreen&quot;,&quot;exitFullscreen&quot;:&quot;Exit fullscreen&quot;,&quot;frameTitle&quot;:&quot;Player for {title}&quot;,&quot;captions&quot;:&quot;Captions&quot;,&quot;settings&quot;:&quot;Settings&quot;,&quot;pip&quot;:&quot;PIP&quot;,&quot;menuBack&quot;:&quot;Go back to previous menu&quot;,&quot;speed&quot;:&quot;Speed&quot;,&quot;normal&quot;:&quot;Normal&quot;,&quot;quality&quot;:&quot;Quality&quot;,&quot;loop&quot;:&quot;Loop&quot;,&quot;start&quot;:&quot;Start&quot;,&quot;end&quot;:&quot;End&quot;,&quot;all&quot;:&quot;All&quot;,&quot;reset&quot;:&quot;Reset&quot;,&quot;disabled&quot;:&quot;Disabled&quot;,&quot;enabled&quot;:&quot;Enabled&quot;,&quot;advertisement&quot;:&quot;Ad&quot;,&quot;qualityBadge&quot;:{&quot;2160&quot;:&quot;4K&quot;,&quot;1440&quot;:&quot;HD&quot;,&quot;1080&quot;:&quot;HD&quot;,&quot;720&quot;:&quot;HD&quot;,&quot;576&quot;:&quot;SD&quot;,&quot;480&quot;:&quot;SD&quot;}}}"
    class="wp-block-h5ap-tailwind wp-block-h5ap-audioplayer">
            <div class='h5ap_lp'>
            <div class='bar bar-1'></div>
            <div class='bar bar-1'></div>
        </div>
    </div>

<div class="wp-block-image is-style-default">
<figure class="aligncenter size-full"><img fetchpriority="high" decoding="async" width="883" height="214" src="https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-of-a-dehumidifier-at-three-different-input-gain-settings-auto-0db-and-57db-1.webp" alt="Waveforms from the Final Cut Pro timeline of Tascam Portacapture X8 recordings of a dehumidifier, at three different input gain settings (Auto, 0dB, and 57dB)" class="wp-image-8531" srcset="https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-of-a-dehumidifier-at-three-different-input-gain-settings-auto-0db-and-57db-1.webp 883w, https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-of-a-dehumidifier-at-three-different-input-gain-settings-auto-0db-and-57db-1-256x62.webp 256w, https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-of-a-dehumidifier-at-three-different-input-gain-settings-auto-0db-and-57db-1-768x186.webp 768w, https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-of-a-dehumidifier-at-three-different-input-gain-settings-auto-0db-and-57db-1@2x.webp 1766w, https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-of-a-dehumidifier-at-three-different-input-gain-settings-auto-0db-and-57db-1-256x62@2x.webp 512w" sizes="(max-width: 883px) 100vw, 883px" /></figure>
</div>


<p>It only went over by about 3dB (at 57dB input gain), but that was enough to destroy the input signal and make the recording unusable.</p>



<h2 class="wp-block-heading">Auto Gain still affects the recording</h2>



<p>In the above tests I included the Auto Gain setting, even though it doesn&#8217;t exhibit particularly high noise nor does it clip in these simple sound environments (basically constant sound levels).  And it worked pretty well (not optimal input gain levels, but close enough for my taste).</p>



<p>But, I was curious if it had any effect at all &#8211; again, reading about 32-bit floating-point recording online, you&#8217;d be forgiven for thinking Auto Gain has no impact on the <em>actual</em> recorded data.  Many people liken the format to camera raw files, and some <em>explicitly</em> state that Auto Gain has <em>no</em> impact on the bits that get written to disk.</p>



<p>This is completely false, at least in the case of this Tascam Portacapture X8.  It&#8217;s trivial to see why:</p>


<div
    id="h5ap-player-3"
    data-id="h5ap-player-3"
    data-attributes="{&quot;uniqueId&quot;:&quot;h5ap8de70736&quot;,&quot;source&quot;:&quot;https:\/\/wadetregaskis.com\/wp-content\/uploads\/2025\/07\/Tascam-Portacapture-X8-w-Sennheiser-K6-ME65-Auto-Gain-vs-constant-gain.wav&quot;,&quot;primaryColor&quot;:&quot;#4A5464&quot;,&quot;controlColor&quot;:&quot;#4A5464&quot;,&quot;bgColor&quot;:&quot;#F5F5F5&quot;,&quot;loader&quot;:true,&quot;preload&quot;:&quot;auto&quot;,&quot;radius&quot;:&quot;50px&quot;,&quot;clientId&quot;:&quot;&quot;,&quot;align&quot;:&quot;&quot;,&quot;alignment&quot;:&quot;left&quot;,&quot;poster&quot;:&quot;&quot;,&quot;title&quot;:&quot;audio title&quot;,&quot;artist&quot;:&quot;&quot;,&quot;color&quot;:&quot;#87ceeb&quot;,&quot;hoverColor&quot;:&quot;#00B3FF&quot;,&quot;skin&quot;:&quot;Default&quot;,&quot;repeat&quot;:false,&quot;autoplay&quot;:false,&quot;muted&quot;:false,&quot;saveState&quot;:false,&quot;disablePause&quot;:false,&quot;seekTime&quot;:10,&quot;startTime&quot;:0,&quot;download&quot;:true,&quot;width&quot;:&quot;100%&quot;,&quot;speed&quot;:{&quot;selected&quot;:1,&quot;speed&quot;:[&quot;0.5&quot;,&quot; 0.75&quot;,&quot; 1&quot;,&quot; 1.25&quot;,&quot; 1.5&quot;,&quot; 1.75&quot;,&quot; 2&quot;,&quot; 4&quot;,&quot; 8&quot;]},&quot;controls&quot;:{&quot;restart&quot;:false,&quot;rewind&quot;:false,&quot;play&quot;:true,&quot;fast-forward&quot;:false,&quot;progress&quot;:true,&quot;duration&quot;:false,&quot;current-time&quot;:true,&quot;mute&quot;:true,&quot;volume&quot;:true,&quot;settings&quot;:true,&quot;download&quot;:false},&quot;isSticky&quot;:false,&quot;CSS&quot;:&quot;&quot;,&quot;multiple_audio&quot;:false,&quot;defaultValue&quot;:{&quot;Default&quot;:{&quot;primaryColor&quot;:&quot;#4A5464&quot;,&quot;controlColor&quot;:&quot;#4A5464&quot;,&quot;bgColor&quot;:&quot;#F5F5F5&quot;},&quot;Fusion&quot;:{&quot;primaryColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;controlColor&quot;:&quot;#fff&quot;},&quot;Stamp&quot;:{&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;},&quot;Wave&quot;:{&quot;primaryColor&quot;:&quot;#fff&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;},&quot;Card-1&quot;:{&quot;primaryColor&quot;:&quot;#fff&quot;,&quot;controlColor&quot;:&quot;#161616&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;},&quot;Card-2&quot;:{&quot;primaryColor&quot;:&quot;#00FBF3&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;},&quot;Simple-1&quot;:{&quot;primaryColor&quot;:&quot;#00dcff&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#00dcff&quot;},&quot;Simple-2&quot;:{&quot;primaryColor&quot;:&quot;#06F7FF00&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;},&quot;Player9&quot;:{&quot;primaryColor&quot;:&quot;#195FF6&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#EDEFF2&quot;},&quot;Player10&quot;:{&quot;primaryColor&quot;:&quot;#195FF6&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#EDEFF2&quot;},&quot;Player11&quot;:{&quot;primaryColor&quot;:&quot;#195FF6&quot;,&quot;controlColor&quot;:&quot;#fff&quot;,&quot;bgColor&quot;:&quot;#161616&quot;,&quot;textColor&quot;:&quot;#fff&quot;}},&quot;i18n&quot;:{&quot;restart&quot;:&quot;Restart&quot;,&quot;rewind&quot;:&quot;Rewind {seektime}s&quot;,&quot;play&quot;:&quot;Play&quot;,&quot;pause&quot;:&quot;Pause&quot;,&quot;fastForward:&quot;:&quot;Forward {seektime}s&quot;,&quot;seek&quot;:&quot;Seek&quot;,&quot;seekLabel&quot;:&quot;{currentTime} of {duration}&quot;,&quot;played&quot;:&quot;Played&quot;,&quot;buffered&quot;:&quot;Buffered&quot;,&quot;currentTime:&quot;:&quot;Current time&quot;,&quot;duration&quot;:&quot;Duration&quot;,&quot;volume&quot;:&quot;Volume&quot;,&quot;mute&quot;:&quot;Mute&quot;,&quot;unmute&quot;:&quot;Unmute&quot;,&quot;enableCaptions&quot;:&quot;Enable captions&quot;,&quot;disableCaptions&quot;:&quot;Disable captions&quot;,&quot;download&quot;:&quot;Download&quot;,&quot;enterFullscreen&quot;:&quot;Enter fullscreen&quot;,&quot;exitFullscreen&quot;:&quot;Exit fullscreen&quot;,&quot;frameTitle&quot;:&quot;Player for {title}&quot;,&quot;captions&quot;:&quot;Captions&quot;,&quot;settings&quot;:&quot;Settings&quot;,&quot;pip&quot;:&quot;PIP&quot;,&quot;menuBack&quot;:&quot;Go back to previous menu&quot;,&quot;speed&quot;:&quot;Speed&quot;,&quot;normal&quot;:&quot;Normal&quot;,&quot;quality&quot;:&quot;Quality&quot;,&quot;loop&quot;:&quot;Loop&quot;,&quot;start&quot;:&quot;Start&quot;,&quot;end&quot;:&quot;End&quot;,&quot;all&quot;:&quot;All&quot;,&quot;reset&quot;:&quot;Reset&quot;,&quot;disabled&quot;:&quot;Disabled&quot;,&quot;enabled&quot;:&quot;Enabled&quot;,&quot;advertisement&quot;:&quot;Ad&quot;,&quot;qualityBadge&quot;:{&quot;2160&quot;:&quot;4K&quot;,&quot;1440&quot;:&quot;HD&quot;,&quot;1080&quot;:&quot;HD&quot;,&quot;720&quot;:&quot;HD&quot;,&quot;576&quot;:&quot;SD&quot;,&quot;480&quot;:&quot;SD&quot;}}}"
    class="wp-block-h5ap-tailwind wp-block-h5ap-audioplayer">
            <div class='h5ap_lp'>
            <div class='bar bar-1'></div>
            <div class='bar bar-1'></div>
        </div>
    </div>

<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="590" height="214" src="https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-showing-the-difference-between-auto-gain-and-constant-gain.webp" alt="Waveforms from the Final Cut Pro timeline of Tascam Portacapture X8 recordings showing the difference between Auto Gain and constant gain" class="wp-image-8533" srcset="https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-showing-the-difference-between-auto-gain-and-constant-gain.webp 590w, https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-showing-the-difference-between-auto-gain-and-constant-gain-256x93.webp 256w, https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-showing-the-difference-between-auto-gain-and-constant-gain@2x.webp 1180w, https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-showing-the-difference-between-auto-gain-and-constant-gain-256x93@2x.webp 512w" sizes="(max-width: 590px) 100vw, 590px" /></figure>
</div>


<p>Auto Gain still does exactly what it always does &#8211; it <em>changes</em> the gain in response to the input.  That change <em>is</em> baked into the recorded audio track(s).</p>



<p>So in a nutshell, 32-bit floating-point recording <em>might</em> provide slightly more flexibility in some situations, but it does <em>not</em> mean you can ignore your input level settings, it does <em>not</em> mean you can use Auto Gain in every scenario, and it does <em>not</em> mean you cannot clip.</p>



<h2 class="wp-block-heading">Addendum: Technical details</h2>



<p>I tested post-production gain changes in Final Cut Pro, Logic Pro, &amp; Audacity.  All produced the exact same results (to my ears).  <a href="https://discussions.apple.com/thread/255782592" data-wpel-link="external" target="_blank" rel="external noopener">I had read that Final Cut Pro sometimes &#8216;bakes in&#8217; clipping with 32-bit float inputs</a>, as if it&#8217;s pre-rendering them down to some smaller dynamic range, so I wanted to rule out some Final Cut Pro-specific stupidity.  It&#8217;s possible that <em>all</em> these editors are doing that, but I&#8217;d be flabbergasted if that&#8217;s true.</p>



<p>The &#8220;industrial noise&#8221; sample I used was my dehumidifier, which is about 66dB according to <a href="https://apps.apple.com/us/app/decibel-x-db-sound-level-meter/id448155923" data-wpel-link="external" target="_blank" rel="external noopener">DecibelX</a> on my iPhone 14 Pro.  My office room tone is about 42dB according to the same app.</p>



<p>I used <a href="https://www.sennheiser.com/en-us/catalog/uncategorized/k-6/k-6-003279" data-wpel-link="external" target="_blank" rel="external noopener">Sennheiser K6</a> modules with an <a href="https://www.sennheiser.com/en-us/catalog/uncategorized/me-65/me-65-003283" data-wpel-link="external" target="_blank" rel="external noopener">ME65</a> and <a href="https://www.sennheiser.com/en-us/catalog/uncategorized/me-66/me-66-003284" data-wpel-link="external" target="_blank" rel="external noopener">ME66</a> attached, plugged into the Tascam Portacapture X8 via <a href="https://www.amazon.com/dp/B00KO8VY4O" data-wpel-link="external" target="_blank" rel="external noopener">3&#8242; Cable Matters XLR cables</a>.</p>



<p>I recorded at 96kHz because that&#8217;s what I&#8217;ll use most often.  I like the aliasing headroom above 48kHz (even though of course my final outputs are almost always 44.1kHz or 48kHz), but don&#8217;t see evidence that 192kHz provides meaningful additional benefit (and it also hurts the frequency response significantly, compared with 48kHz and 96kHz, <a href="https://tascam.com/us/product/portacapture_x8/spec#:~:text=Audio%20Performance" data-wpel-link="external" target="_blank" rel="external noopener">according to Tascam</a>).</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/32-bit-float-audio-recording-is-not-a-panacea/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			<media:content url="https://wadetregaskis.com/wp-content/uploads/2025/07/waveforms-from-the-final-cut-pro-timeline-of-tascam-portacapture-x8-recordings-showing-the-difference-between-auto-gain-and-constant-gain.webp" medium="image" />
<post-id xmlns="com-wordpress:feed-additions:1">8524</post-id>	</item>
		<item>
		<title>Copy-on-write on APFS</title>
		<link>https://wadetregaskis.com/copy-on-write-on-apfs/</link>
					<comments>https://wadetregaskis.com/copy-on-write-on-apfs/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Thu, 16 May 2024 21:14:10 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[Howto]]></category>
		<category><![CDATA[APFS]]></category>
		<category><![CDATA[clonefile]]></category>
		<category><![CDATA[copy-on-write]]></category>
		<category><![CDATA[copyfile]]></category>
		<category><![CDATA[FileManager]]></category>
		<category><![CDATA[Foundation]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=8138</guid>

					<description><![CDATA[APFS (like many modern file systems but unlike its predecessor HFS+) supports copy-on-write. This means you can logically copy a file &#8211; it looks and behaves like a distinct file &#8211; but it doesn&#8217;t immediately copy the file&#8217;s contents on disk &#8211; it merely shares them with the original. Only if and as you modify&#8230; <a class="read-more-link" href="https://wadetregaskis.com/copy-on-write-on-apfs/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p><a href="https://en.wikipedia.org/wiki/Apple_File_System" data-wpel-link="external" target="_blank" rel="external noopener">APFS</a> (like many modern file systems but unlike its predecessor <a href="https://en.wikipedia.org/wiki/HFS_Plus" data-wpel-link="external" target="_blank" rel="external noopener">HFS+</a>) supports <a href="https://eclecticlight.co/2017/06/23/what-is-copy-on-write-and-how-is-it-good/" data-wpel-link="external" target="_blank" rel="external noopener">copy-on-write</a>.  This means you can <em>logically</em> copy a file &#8211; it <em>looks</em> and <em>behaves</em> like a distinct file &#8211; but it doesn&#8217;t <em>immediately</em> copy the file&#8217;s contents on disk &#8211; it merely shares them with the original.  Only if and as you modify either version do they start to diverge on disk, with APFS dynamically allocating new storage for the modified parts<sup data-fn="fd9542d5-ca23-49bc-ae00-3d2b57caf906" class="fn"><a href="#fd9542d5-ca23-49bc-ae00-3d2b57caf906" id="fd9542d5-ca23-49bc-ae00-3d2b57caf906-link">1</a></sup>.</p>



<p>This is kind of a sister function to hard links, which similarly avoid copying the file&#8217;s contents <em>but</em> where modifications apply to <em>all</em> copies.  See also <a href="https://eclecticlight.co/2019/01/05/aliases-hard-links-symlinks-and-copies-in-mojaves-apfs/" data-wpel-link="external" target="_blank" rel="external noopener">this article on the differences</a>, including versus aliases and symlinks.</p>



<p>Copy-on-write is beneficial for several reasons:</p>



<ul class="wp-block-list">
<li>Copies don&#8217;t take up any significant space (just whatever tiny amount is necessary for their metadata).</li>



<li>The initial copy operation is practically instantaneous (just a few small metadata writes &amp; updates).</li>



<li>Deferring (if not entirely avoiding) the actual disk I/O reduces wear on the disk.</li>



<li>The copies can share common segments, saving disk space even when they&#8217;re not ultimately identical copies.</li>
</ul>



<p>It does have some potential downsides:</p>



<ul class="wp-block-list">
<li>You don&#8217;t get the increased data redundancy and error resilience that <em>actual</em> copies provide (although if you&#8217;re aiming for data redundancy or backup, you should be using separate physical disks anyway).</li>



<li>It can make subsequent modifications of the file slower, as even just modifying a single byte can trigger the actual copy to be performed.</li>
</ul>



<p>And some basic limitations:</p>



<ul class="wp-block-list">
<li>It&#8217;s only supported on APFS (and <em>maybe</em> additional file systems added by 3rd party extensions, but I haven&#8217;t tested nor can I find any accounts of this).</li>



<li>It only works within individual volumes (it doesn&#8217;t work even between two volumes in the same APFS container, or sharing the same physical disk).</li>
</ul>



<p>Contrary to what I saw online in a few places, copy-on-write works on <em>all</em> APFS volumes, irrespective of whether they are backed by SSDs, HDDs, or some other type of storage.</p>



<h2 class="wp-block-heading">Should I use it?</h2>



<p>Yes!</p>



<p>For most purposes those downsides aren&#8217;t an issue, and the limitations merely mean that it&#8217;s wise to have a fallback option (of just copying the actual file contents) whenever copy-on-write isn&#8217;t available.  And many of the tools &amp; APIs fallback automatically (unless you explicitly require them not to, such as with <code>COPYFILE_CLONE_FORCE</code> to <code>copyfile</code>).</p>



<h2 class="wp-block-heading">How do I use it?</h2>



<figure class="wp-block-table aligncenter"><table><thead><tr><th>Method</th><th class="has-text-align-center" data-align="center">Uses copy-on-write<br>(where possible)</th><th class="has-text-align-center" data-align="center">Does actual copy<br>if copy-on-write<br> isn&#8217;t available</th></tr></thead><tbody><tr><td>cp</td><td class="has-text-align-center" data-align="center">❌</td><td class="has-text-align-center" data-align="center">N/A</td></tr><tr><td>cp -c</td><td class="has-text-align-center" data-align="center">✅</td><td class="has-text-align-center" data-align="center">❌</td></tr><tr><td>ditto</td><td class="has-text-align-center" data-align="center">❌</td><td class="has-text-align-center" data-align="center">N/A</td></tr><tr><td>ditto &#8211;clone</td><td class="has-text-align-center" data-align="center">❌</td><td class="has-text-align-center" data-align="center">N/A</td></tr><tr><td>dd</td><td class="has-text-align-center" data-align="center">❌</td><td class="has-text-align-center" data-align="center">N/A</td></tr><tr><td>scp</td><td class="has-text-align-center" data-align="center">❌</td><td class="has-text-align-center" data-align="center">N/A</td></tr><tr><td>Finder Copy then Paste</td><td class="has-text-align-center" data-align="center">✅</td><td class="has-text-align-center" data-align="center">✅</td></tr><tr><td>Finder Duplicate</td><td class="has-text-align-center" data-align="center">✅</td><td class="has-text-align-center" data-align="center">✅</td></tr><tr><td>Finder ⌥-drag</td><td class="has-text-align-center" data-align="center">✅</td><td class="has-text-align-center" data-align="center">✅</td></tr><tr><td><code><a href="https://www.manpagez.com/man/2/clonefile/" data-wpel-link="external" target="_blank" rel="external noopener">clonefile</a></code><sup data-fn="1bebdb6f-7e69-4ebb-bca6-692795e4e8f7" class="fn"><a href="#1bebdb6f-7e69-4ebb-bca6-692795e4e8f7" id="1bebdb6f-7e69-4ebb-bca6-692795e4e8f7-link">2</a></sup></td><td class="has-text-align-center" data-align="center">✅</td><td class="has-text-align-center" data-align="center">❌</td></tr><tr><td><code><a href="https://keith.github.io/xcode-man-pages/copyfile.3.html" data-wpel-link="external" target="_blank" rel="external noopener">copyfile</a>(…, …, …, COPYFILE_DATA)</code></td><td class="has-text-align-center" data-align="center">❌</td><td class="has-text-align-center" data-align="center">N/A</td></tr><tr><td><code><a href="https://keith.github.io/xcode-man-pages/copyfile.3.html" data-wpel-link="external" target="_blank" rel="external noopener">copyfile</a>(…, …, …, COPYFILE_CLONE)</code></td><td class="has-text-align-center" data-align="center">✅</td><td class="has-text-align-center" data-align="center">✅</td></tr><tr><td><code><a href="https://keith.github.io/xcode-man-pages/copyfile.3.html" data-wpel-link="external" target="_blank" rel="external noopener">copyfile</a>(…, …, …, COPYFILE_CLONE_FORCE)</code></td><td class="has-text-align-center" data-align="center">✅</td><td class="has-text-align-center" data-align="center">❌</td></tr><tr><td><code><a href="https://developer.apple.com/documentation/foundation/filemanager/1412957-copyitem" data-wpel-link="external" target="_blank" rel="external noopener">FileManager.copyItem(at:to:)</a></code></td><td class="has-text-align-center" data-align="center">✅</td><td class="has-text-align-center" data-align="center">✅</td></tr><tr><td><code><a href="https://developer.apple.com/documentation/foundation/filemanager/1407903-copyitem" data-wpel-link="external" target="_blank" rel="external noopener">FileManager.copyItem(atPath:toPath:)</a></code></td><td class="has-text-align-center" data-align="center">✅</td><td class="has-text-align-center" data-align="center">✅</td></tr></tbody></table><figcaption class="wp-element-caption">This is accurate for macOS 14.5 (23F79).  The behaviour might vary across OS releases.</figcaption></figure>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-dots"/>



<h3 class="wp-block-heading">Testing method</h3>



<p>Not that this is interesting, just for posterity and to show my work a bit, in case I made a mistake.</p>



<p>I created large-enough files on each of my test volumes (APFS on SSD, APFS on HDD, HFS+ on SSD) that an actual copy would take tens of seconds at least, using <code>dd</code> e.g.:</p>



<figure class="wp-block-pullquote"><blockquote><p><code>dd if=/dev/random of=/tmp/bigfile oflag=direct status=progress bs=1k count=104857600</code></p></blockquote></figure>



<p>I then ran the various command line tools on these files, attempting to clone the file to a different name in the same folder, and observed disk I/O activity with Activity Monitor and iStat Menus.</p>



<p>For actual copy-on-writes I would observe that the program successfully concluded practically instantly, and there&#8217;d be at most a small blip of disk writes (for metadata modifications).</p>



<p>For failed copy-on-writes I would observe that the program would not exit promptly and I&#8217;d see voluminous disk writes (hundreds of megabytes to gigabytes per second, depending on the disk, sustained for many seconds until I was satisfied with the results and killed the test).</p>



<p>For API tests the overall approach was the same, but the APIs were invoked from inside <code>swift repl</code> where possible, and from a throw-away Swift script otherwise<sup data-fn="4f593842-632c-4279-83ba-435dde24c411" class="fn"><a href="#4f593842-632c-4279-83ba-435dde24c411" id="4f593842-632c-4279-83ba-435dde24c411-link">3</a></sup>.</p>


<ol class="wp-block-footnotes"><li id="fd9542d5-ca23-49bc-ae00-3d2b57caf906">I haven&#8217;t tested it, but as far as I&#8217;ve heard APFS does <em>not</em> actually check if the modifications actually diverge the files.  e.g. if you &#8220;modify&#8221; a byte of the file to the value it already has &#8211; a pointless but technically possible operation that leaves both copies still identical &#8211; APFS <em>will still copy the modified block</em>.<br><br>Furthermore, APFS &amp; Apple&#8217;s operating systems appear to have no tools (nor APIs) to deduplicate files &#8211; e.g. to detect full or partial copies and deduplicate their actual storage on disk.  Not even tools that you could invoke manually if you do the hard work of first determining that two files&#8217; contents are identical.<br><br>APFS / Apple&#8217;s operating systems rely entirely on user applications using copy-on-write explicitly and upfront. <a href="#fd9542d5-ca23-49bc-ae00-3d2b57caf906-link" aria-label="Jump to footnote reference 1">↩︎</a></li><li id="1bebdb6f-7e69-4ebb-bca6-692795e4e8f7">Curiously, <code>clonefile</code> first shipped in macOS 10.12 (Sierra), <a href="https://www.manpagez.com/man/2/clonefile/" data-wpel-link="external" target="_blank" rel="external noopener">according to its man page</a>, which is the release <em>preceding</em> the introduction of APFS in macOS 10.13 (High Sierra).  Yet, as far as I can tell HFS+ doesn&#8217;t and never did support cloning &#8211; nor do any of the other file systems supported by macOS 10.12 (e.g. FAT16 &amp; FAT32, exFAT, NFS).  It&#8217;s possible it was just convenient for Apple to include it in the prior release as they were probably testing APFS with it internally, during APFS&#8217;s development.<br><br><strong>Update</strong>: <a href="https://mjtsai.com" data-wpel-link="external" target="_blank" rel="external noopener">Michael Tsai</a> <a href="https://mastodon.social/@mjtsai/112456863768132668" data-wpel-link="external" target="_blank" rel="external noopener">pointed out</a> that <a href="https://www.pcmag.com/news/what-macos-sierras-new-apfs-file-system-means-to-you" data-wpel-link="external" target="_blank" rel="external noopener">Sierra included APFS support in beta form</a> (e.g. you could create disk images with it, but not use it for a boot disk).  Thus why <code>clonefile</code> (and other APFS-related tools) were included in Sierra.  <a href="#1bebdb6f-7e69-4ebb-bca6-692795e4e8f7-link" aria-label="Jump to footnote reference 2">↩︎</a></li><li id="4f593842-632c-4279-83ba-435dde24c411">While the C APIs worked just fine inside <code>swift repl</code> irrespective of what volumes I was targeting, the Foundation APIs oddly refused to work whenever the files were not on the boot volume, throwing &#8220;Operation not permitted&#8221; errors (NSCocoaErrorDomain 513, with no user info).  If it weren&#8217;t for the C APIs working just fine I&#8217;d think it&#8217;s a sandboxing issue, but clearly it&#8217;s not (and <code>swift repl -disable-sandbox</code> makes no difference). 🤔 <a href="#4f593842-632c-4279-83ba-435dde24c411-link" aria-label="Jump to footnote reference 3">↩︎</a></li></ol>]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/copy-on-write-on-apfs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">8138</post-id>	</item>
		<item>
		<title>Swift sucks at web serving… or does it?</title>
		<link>https://wadetregaskis.com/swift-sucks-at-web-serving-or-does-it/</link>
					<comments>https://wadetregaskis.com/swift-sucks-at-web-serving-or-does-it/#comments</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Thu, 16 May 2024 01:32:06 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[BigInt]]></category>
		<category><![CDATA[FPM]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[kevents]]></category>
		<category><![CDATA[Kotlin]]></category>
		<category><![CDATA[kqueue]]></category>
		<category><![CDATA[macOS kernel]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[Numberick]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Swift]]></category>
		<category><![CDATA[Swift Forums]]></category>
		<category><![CDATA[SwiftNIO]]></category>
		<category><![CDATA[Vapor]]></category>
		<category><![CDATA[web server]]></category>
		<category><![CDATA[wrk]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=8061</guid>

					<description><![CDATA[A few weeks ago, Axel Roest published a simple web server comparison, that turned out to not be doing what it was thought to be doing. Figuring that out was a very interesting discussion that warrants a retrospective, to look at which parts were particularly helpful and which not so much. Tangentially, I want to&#8230; <a class="read-more-link" href="https://wadetregaskis.com/swift-sucks-at-web-serving-or-does-it/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>A few weeks ago, Axel Roest published <a href="https://tech.phlux.us/Juice-Sucking-Servers/" data-wpel-link="external" target="_blank" rel="external noopener">a simple web server comparison</a>, that turned out to not be doing what it was thought to be doing. Figuring that out was a very interesting discussion that warrants a retrospective, to look at which parts were particularly helpful and which not so much.</p>



<p>Tangentially, I want to highlight that Axel&#8217;s comparison is notable because he is interested in <em>efficiency</em>, not mere brute performance. The two are usually correlated but not always the same. He correctly noted that electricity is a major <em>and increasingly large</em> part of server costs (see <a href="https://wadetregaskis.com/the-cost-of-electrical-power-in-servers/" data-wpel-link="internal">my prior post</a> for why it&#8217;s even worse than you likely realise). That said, while he did take RAM and power measurements, his benchmark and analysis didn&#8217;t go into detail about energy efficiency.</p>



<h1 class="wp-block-heading">Benchmark method &amp; apparatus</h1>



<p>Axel wanted to see how a very simple web server performed in:</p>



<ul class="wp-block-list">
<li><a href="https://www.php.net/manual/en/install.fpm.php" data-wpel-link="external" target="_blank" rel="external noopener">FPM</a> w/ <a href="https://www.nginx.com" data-wpel-link="external" target="_blank" rel="external noopener">NGINX</a> (PHP).</li>



<li><a href="https://helidon.io" data-wpel-link="external" target="_blank" rel="external noopener">Helidon</a> (Kotlin / Java<sup data-fn="e7728698-06db-400a-a6b9-01a2ce4f3e5b" class="fn"><a href="#e7728698-06db-400a-a6b9-01a2ce4f3e5b" id="e7728698-06db-400a-a6b9-01a2ce4f3e5b-link">1</a></sup>).</li>



<li><a href="https://nodejs.org/en" data-wpel-link="external" target="_blank" rel="external noopener">Node.js</a> (JavaScript).</li>



<li><a href="https://vapor.codes" data-wpel-link="external" target="_blank" rel="external noopener">Vapor</a> (Swift).</li>
</ul>



<p>He was particularly interested in throughput &amp; latency vs RAM &amp; power usage. All are important metrics in their own right, but are most useful in light of each other.</p>



<p>He chose to use <a href="https://en.wikipedia.org/wiki/Fibonacci_sequence" data-wpel-link="external" target="_blank" rel="external noopener">Fibonacci sequence</a> calculation as the load. Choosing a load for any web server benchmark is always highly contentious, and not the focus of this post. Whether you think Fibonacci&#8217;s a good choice or not, read on to see why really it didn&#8217;t matter.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>☝️ People get hung up on how well benchmarks represent the so-called real world, but I think that&#8217;s often fruitless to argue about and also beside the point. What matters is whether the benchmark is <em>useful</em>. e.g. does it <em>inform</em> and <em>elucidate</em>?</p>
</div></div>



<p>He did use <em>very</em> old hardware, though &#8211; an Intel Core i3-550 from over a decade ago. Fortunately it didn&#8217;t turn out to materially impact the relative results nor behaviours of the benchmark, but it&#8217;s usually unwise to add unnecessary [potential] variables to your setup, like unusual hardware.</p>



<p>In my own debugging and profiling, I used my also very old 10-core iMac Pro. It&#8217;s at least a Xeon? 😅</p>



<h1 class="wp-block-heading">Benchmark results</h1>



<div class="wp-block-group is-content-justification-center is-layout-flex wp-container-core-group-is-layout-64b26803 wp-block-group-is-layout-flex">
<figure class="wp-block-image size-full is-resized"><img decoding="async" width="800" height="586" src="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput.webp" alt="" class="wp-image-8063" style="object-fit:cover;width:400px;height:293px" srcset="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput.webp 800w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput-256x188.webp 256w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput-768x563.webp 768w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput-256x188@2x.webp 512w" sizes="(max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">Requests per second (Y) over concurrent requests (X).<br>From <a href="https://tech.phlux.us/Juice-Sucking-Servers/" data-wpel-link="external" target="_blank" rel="external noopener">Axel&#8217;s first post</a>.</figcaption></figure>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="800" height="617" src="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-failure-rate.webp" alt="" class="wp-image-8064" style="object-fit:cover;width:400px;height:293px" srcset="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-failure-rate.webp 800w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-failure-rate-256x197.webp 256w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-failure-rate-768x592.webp 768w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-failure-rate-256x197@2x.webp 512w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">Success rate (Y) over concurrent requests (X).<br>From <a href="https://tech.phlux.us/Juice-Sucking-Servers/" data-wpel-link="external" target="_blank" rel="external noopener">Axel&#8217;s first post</a>.</figcaption></figure>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" width="800" height="577" src="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-memory-usage.webp" alt="" class="wp-image-8076" style="object-fit:cover;width:400px;height:300px" srcset="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-memory-usage.webp 800w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-memory-usage-256x185.webp 256w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-memory-usage-768x554.webp 768w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-memory-usage-256x185@2x.webp 512w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">RAM usage (Y) over concurrent requests (X).<br>From <a href="https://tech.phlux.us/Juice-Sucking-Servers/" data-wpel-link="external" target="_blank" rel="external noopener">Axel&#8217;s first post</a>.</figcaption></figure>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" width="800" height="575" src="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-power-usage.webp" alt="" class="wp-image-8077" style="object-fit:cover;width:400px;height:300px" srcset="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-power-usage.webp 800w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-power-usage-256x184.webp 256w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-power-usage-768x552.webp 768w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-power-usage-256x184@2x.webp 512w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">Power usage (Y) over concurrent requests (X).<br>From <a href="https://tech.phlux.us/Juice-Sucking-Servers/" data-wpel-link="external" target="_blank" rel="external noopener">Axel&#8217;s first post</a>.</figcaption></figure>
</div>



<p>In words:</p>



<ul class="wp-block-list">
<li>Helidon (Kotlin / Java) had the highest throughput and lowest latency at low (and arguably more reasonable) loads, but used by far the most RAM, and the most power. Consequently it handled the most load before requests started failing (timing out).</li>



<li>Node.js (JavaScript) was qualitatively very similar to Helidon (Kotlin / Java) but less in all metrics &#8211; less throughput, less peak load capacity, but also less RAM and very slightly less power used.</li>



<li>FPM + NGINX (PHP) followed the pattern.</li>



<li>Vapor (Swift) did not &#8211; it had higher throughput than PHP yet requests started failing much sooner as load increased. It used the least RAM and least power, though, and kept on trucking irrespective of the load.</li>
</ul>



<p>Many people would have left it at that &#8211; obviously the results make sense for the first three (&#8220;everyone knows&#8221; that Kotlin / Java&#8217;s faster than JavaScript that&#8217;s faster than PHP) and Vapor / Swift apparently just isn&#8217;t fast and has weird reliability behaviours. QED, right?</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>⚠️ Going in with a specific hypothesis can be helpful, but hypotheses can also end up being just biases. Be careful not to blindly accept apparent confirmation of the hypothesis. Similarly, beware subconscious hypotheses like &#8220;Kotlin / Java is faster than JavaScript&#8221;.</p>
</div></div>



<p>To his credit, Axel wasn&#8217;t so sure &#8211; he felt that the results he was seeing were suspicious, and <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583" data-wpel-link="external" target="_blank" rel="external noopener">he sought help from the Swift Forums</a> in explaining or correcting them.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>✅ Question your results. <em>Understand</em> them. It improves the quality, correctness, and usefulness of your work. <em>Why</em> something behaves the way it does is often more interesting and important than merely how it behaves.</p>



<p>On most platforms it&#8217;s pretty easy to at least do a time profile, and most often that&#8217;s all you need to understand what&#8217;s going on. On Apple platforms you can use <a href="https://www.avanderlee.com/debugging/xcode-instruments-time-profiler/" data-wpel-link="external" target="_blank" rel="external noopener">Instruments</a>, on Windows &amp; Linux tools like <a href="https://www.intel.com/content/www/us/en/docs/vtune-profiler/user-guide/2024-1/basic-hotspots-analysis.html" data-wpel-link="external" target="_blank" rel="external noopener">VTune</a>, among <a href="https://en.wikipedia.org/wiki/List_of_performance_analysis_tools" data-wpel-link="external" target="_blank" rel="external noopener">many other options</a>.</p>



<p>If need be, ask others for help, like Axel did.</p>
</div></div>



<p>While Axel did <em>suspect</em> something was wrong &#8211; noting the oddly small but persistent failure rate &#8211; he missed the most obvious <em>proof</em> of wrongness &#8211; logically impossible results. Doing 80,000 continuous concurrent streams of requests with ~98% of those requests completing within the two second time limit means the server must have a throughput of at least 39,000 requests per second. Yet the benchmark tool reported a mere ~8,000 requests per second.</p>



<p>Sadly, though <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/2" data-wpel-link="external" target="_blank" rel="external noopener">I pointed this out as the very first response to the thread</a>, it seemed to be overlooked by everyone (even myself!), even though it clearly fingered the benchmark tool itself as the problem (which is only partially correct, as we&#8217;ll see later, but in any case was the exact right place to start looking).</p>



<h1 class="wp-block-heading">Debugging the benchmark</h1>



<h2 class="wp-block-heading">Domain experts weigh in</h2>



<p>The Swift Forum post immediately attracted relevant people: folks that work on Vapor and NIO, and folks that have experience using them. However, ironically this didn&#8217;t initially help &#8211; they tended to assume the problem was in Vapor (or its networking library, <a href="https://github.com/apple/swift-nio" data-wpel-link="external" target="_blank" rel="external noopener">SwiftNIO</a>) or how Vapor was being configured. It turned out none of this was really true &#8211; there <em>was</em> <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/49" data-wpel-link="external" target="_blank" rel="external noopener">a small optimisation made to Vapor</a> as a result of all this, which did marginally improve performance (in specific circumstances), but ultimately Vapor &amp; NIO were not the problem, nor was the benchmark&#8217;s configuration and use of them.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>⚠️ It can be all too easy to assume elaborate reasons when you know a lot about something. Don&#8217;t jump to conclusions. Check the most basic and foundational things <em>first</em>.</p>



<p>I say this with humility and I guess technically hypocrisy, because even as professional performance engineer (in the past) I&#8217;ve repeatedly made this mistake myself. We&#8217;re all particularly susceptible to this mistake.</p>
</div></div>



<p>There were some assertions that the results <em>were</em> plausible and just how Vapor performs, and that the &#8220;problem&#8221; was the choice of Vapor rather than some other web server framework (e.g. <a href="https://github.com/hummingbird-project/hummingbird" data-wpel-link="external" target="_blank" rel="external noopener">Hummingbird</a>).</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>⚠️ It&#8217;s not <em>wrong</em> to be interested in additional data, but be careful not to get distracted. Using Vapor was not in any way wrong or unhelpful &#8211; it is the most well-known and probably well-used web server framework in Swift. It might well be that other frameworks are better in some respects, but that&#8217;s a <em>different</em> comparison than what Axel performed.</p>
</div></div>



<p>Others similarly asserted that the results were plausible because Swift uses <a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting/" data-wpel-link="external" target="_blank" rel="external noopener">reference-counting</a> for memory management whereas PHP, JavaScript, and Kotlin / Java use garbage collection. It was presented as &#8220;common knowledge&#8221; that garbage collection has inherent benefits for some programs, like web servers, because it makes memory allocation super cheap.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>⚠️ While it can be useful to speculate a little, in a brainstorming sense, don&#8217;t presume. A <em>lot</em> of mistakes have been made over the years because of this, like that &#8220;linked lists are faster than arrays&#8221; or &#8220;binary search is faster than linear search&#8221;, etc.</p>



<p>Remember that intuition is in large part presumptions and generalisations. That doesn&#8217;t make intuition useless, but always remember that it&#8217;s far from foolproof. Use it to generate hypotheses, not conclusions.</p>
</div></div>



<h2 class="wp-block-heading">Examining the load</h2>



<p>Even though it was clear that something was wrong with the actual measurements, a lot of the early discussion revolved around the load used (Fibonacci sequence calculation), particularly regarding whether it was:</p>



<h3 class="wp-block-heading">The &#8220;right&#8221; load</h3>



<p>A few folks asserted that the CPU-heavy nature of calculating Fibonacci numbers isn&#8217;t representative of web servers generally. Multiple people noted that &#8211; in the Swift implementation, at least &#8211; the majority of the CPU time was spent doing the Fibonacci calculation. Some felt this was therefore not a useful benchmark of Vapor itself.</p>



<p>A lot of this boiled down to <a href="https://en.wikipedia.org/wiki/No_true_Scotsman" data-wpel-link="external" target="_blank" rel="external noopener">the &#8220;no true Scotsman&#8221; problem</a>, which is very common in benchmarking, with a bit of <a href="https://en.wikipedia.org/wiki/Nirvana_fallacy#Perfect_solution_fallacy" data-wpel-link="external" target="_blank" rel="external noopener">perfect world logical fallacy</a> peppered in, trying to identify the One True Representative Benchmark. See the earlier point about fixating on such matters rather than whether the benchmark is <em>useful</em>.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>⚠️ While it&#8217;s not necessarily wrong or unwise to evaluate how well a benchmark represents real world usage (whether generally or against specific cases), it&#8217;s an exercise that suffers from diminishing returns pretty quickly. It&#8217;s usually best to not quibble too much or too long, as long as the benchmark is in the ballpark.</p>



<p>You can always develop &amp; present your own benchmark(s), if you feel there are better or additional ways to go about it. Best of all, the existence of <em>both</em> the original benchmark and your benchmark(s) will be more useful than either alone, since you can compare and contrast them.</p>
</div></div>



<h3 class="wp-block-heading">A &#8220;fair&#8221; load</h3>



<p>Accusations were made pretty quickly that the benchmark is &#8220;unfair&#8221; to Swift because Swift doesn&#8217;t &#8211; it was asserted &#8211; have a properly-optimised &#8220;<a href="https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic" data-wpel-link="external" target="_blank" rel="external noopener">BigInt</a>&#8221; implementation, unlike all the other languages tested.</p>



<p>No real evidence was given for this. Even if it were true, it doesn&#8217;t invalidate the benchmark &#8211; in fact, it just makes the benchmark <em>more</em> successful because it&#8217;s then highlighted an area where Swift is lacking.</p>



<p>The BigInt library that Axel used, <a href="https://github.com/attaswift/BigInt" data-wpel-link="external" target="_blank" rel="external noopener">attaswift/BigInt</a>, is by far the most popular available for Swift, as judged by things like GitHub stars, forks, &amp; contributor counts, ranking in web &amp; GitHub searches, etc. There are <a href="https://swiftpackageindex.com/search?query=bigint" data-wpel-link="external" target="_blank" rel="external noopener">quite a few others</a>, though.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>☝️ There are multiple ways to approach a benchmark, all equally valid because they&#8217;re all useful. Axel chose to use popular packages, in <em>all</em> the languages he tested. That&#8217;s definitely fair. It&#8217;s also useful because it represents what the typical developer will do when building real web servers.</p>



<p>It&#8217;s often also interesting and useful to search out the <em>best</em> packages (whatever that may mean in context, such as fastest). That could represent what a more heavily optimised implementation might do. It <em>might</em> also better represent what is theoretical possible (<em>if</em> optimal packages exist already). Those are interesting things to explore too, just not what Axel happened to be doing.</p>



<p>You can see also more of my thoughts on Axel&#8217;s choice here, <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/39" data-wpel-link="external" target="_blank" rel="external noopener">in the Swift Forums thread</a>.</p>
</div></div>



<p>It wasn&#8217;t until <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/46" data-wpel-link="external" target="_blank" rel="external noopener">actual evidence was presented</a>, that the discussion made progress.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>⚠️ While it&#8217;s true that without the initial blind assertions actual data might never have been gathered, it would have been more effective and efficient to have just gathered the data at the start.</p>



<p>Data is better than supposition.</p>
</div></div>



<p>It was shown that in fact the BigInt implementation in question <em>was</em> significantly slower than it could be, because JavaScript&#8217;s implementation of addition was much faster. <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/62" data-wpel-link="external" target="_blank" rel="external noopener">Some additional simple tests</a> showed even wider performance gaps regarding the other key operation: rendering to strings. It was <em>that</em> data that turned out to be critical &#8211; <a href="https://github.com/apple/swift-foundation/pull/262" data-wpel-link="external" target="_blank" rel="external noopener">I myself happened to have implemented BigInt string rendering for Apple&#8217;s new Foundation</a>, <em>and</em> then <a href="https://github.com/apple/swift-foundation/pull/306" data-wpel-link="external" target="_blank" rel="external noopener">saw it dramatically optimised by</a> <a href="https://github.com/oscbyspro" data-wpel-link="external" target="_blank" rel="external noopener">Oscar Byström Ericsson</a>, whom has his own BigInt package for Swift, <a href="https://github.com/oscbyspro/Numberick" data-wpel-link="external" target="_blank" rel="external noopener">Numberick</a>. So I had a pretty darn good idea of where I might find a faster package… 😆</p>



<p>You can read more about that specific bit of serendipity <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/64" data-wpel-link="external" target="_blank" rel="external noopener">in the Swift Forums thread</a>.</p>



<p>It was trivial to do the package switch, and it quickly improved Vapor/Swift&#8217;s showing in the benchmark manyfold &#8211; in combination with some other simple and reasonable tweaks, it was <em>five times faster</em>!</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>✅ Axel&#8217;s benchmark taught a lot of people that <a href="https://github.com/oscbyspro/Numberick" data-wpel-link="external" target="_blank" rel="external noopener">Numberick</a> is much more performant than <a href="https://github.com/attaswift/BigInt" data-wpel-link="external" target="_blank" rel="external noopener">BigInt</a>, at least in some important operations (addition and string rendering). Granted that knowledge is a little bit niche in its utility, but it&#8217;s still a good outcome.</p>



<p>It also demonstrated that modifying in place can be faster than creating a copy, <em>even if</em> it means having to do a swap. i.e.:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #000000">a += b</span></span>
<span class="line"><span style="color: #795E26">swap</span><span style="color: #000000">(&amp;a, &amp;b)</span></span></code></pre></div>



<p>…instead of:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">let</span><span style="color: #000000"> c = a + b</span></span>
<span class="line"><span style="color: #000000">a = b</span></span>
<span class="line"><span style="color: #000000">b = c</span></span></code></pre></div>



<p>That&#8217;s a tidbit I had picked up through varied experiences, and <a href="https://wadetregaskis.com/swift-tip-the-swap-function/" data-wpel-link="internal">wrote about previously</a>. The <code><a href="https://developer.apple.com/documentation/swift/swap(_:_:)" data-wpel-link="external" target="_blank" rel="external noopener">swap</a></code> function in Swift is under-appreciated and under-utilised. This knowledge may seem esoteric but you&#8217;d be amazed how often it applies (a <em>lot</em> of programming is about combining data, after all).</p>
</div></div>



<p>Axel posted <a href="https://tech.phlux.us/Juice-Sucking-Servers-Part-Deux/" data-wpel-link="external" target="_blank" rel="external noopener">a follow-up with additional data</a> (with the aforementioned changes and optimisations). That showed Swift now beating out the other three frameworks / languages, with the highest throughput and lowest latency (and still the lowest RAM and power usage).</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="800" height="513" src="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput-after-fixes.webp" alt="" class="wp-image-8071" srcset="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput-after-fixes.webp 800w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput-after-fixes-256x164.webp 256w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput-after-fixes-768x492.webp 768w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput-after-fixes-256x164@2x.webp 512w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">From Axel&#8217;s follow-up post. X axis is the number of concurrent requests.</figcaption></figure>
</div>


<p>So, all done, right? Turns out, Vapor/Swift wins, yeah?</p>



<p>Well, maybe.</p>



<h3 class="wp-block-heading" id="do-these-improvements-apply-to-the-other-cases-too">Do these improvements apply to the other cases too?</h3>



<p>That is yet to be examined. Because only Swift seemed to be producing odd results, Axel only put the benchmark to the Swift community for deeper analysis. It&#8217;s quite possible that doing the same with the other web frameworks &amp; languages would similarly reveal potential improvements.</p>



<p>Still, the results are useful as they stand. Some simple and very plausible &#8211; even for a Swift beginner &#8211; optimisations made a big difference, though of course the biggest difference was simply using a different 3rd party package. There are a lot of useful lessons in that, both in the specifics as already covered and as general best practices.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>☝️ Benchmarks are rarely &#8220;done&#8221;, their results rarely &#8220;final&#8221;. At least if you permit optimisations or other changes. How do you <em>know</em> there&#8217;s not something still &#8220;unfair&#8221; about one of the cases?</p>



<p>Again, this speaks to the potential futility of trying to make &#8220;fair&#8221; benchmarks, and reiterates the practical benefit of simply trying to learn instead.</p>
</div></div>



<h2 class="wp-block-heading">…but… why is the success rate still weird?</h2>



<p>Despite the improved performance, a fundamental problem remained: <em>the numbers still didn&#8217;t make sense</em>.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="800" height="512" src="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-success-rate-after-fixes.png" alt="" class="wp-image-8073" srcset="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-success-rate-after-fixes.png 800w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-success-rate-after-fixes-256x164.png 256w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-success-rate-after-fixes-768x492.png 768w, https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-success-rate-after-fixes-256x164@2x.png 512w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">From Axel&#8217;s follow-up post. X axis is the number of concurrent requests.</figcaption></figure>
</div>


<p>The success rates are <em>slightly</em> different but not materially &#8211; as concurrent requests go up, the throughput plateaus very quickly, yet success rate remains about the same. It&#8217;s exactly the same problem as at the outset &#8211; these results cannot possibly be correct.</p>



<p>Despite all the community&#8217;s efforts, we hadn&#8217;t actually figured out the real problem. We&#8217;d merely made Swift <em>look</em> better, without actually providing confidence in the accuracy of the results.</p>



<p>In fairness to myself, I was well aware that we weren&#8217;t done, I was just struggling to understand what was really going on, <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/74" data-wpel-link="external" target="_blank" rel="external noopener">as I noted here</a>.</p>



<h2 class="wp-block-heading">Examining the benchmark tool</h2>



<p>While there&#8217;d been some tangential questions about <code><a href="https://github.com/wg/wrk" data-wpel-link="external" target="_blank" rel="external noopener">wrk</a></code>, the benchmarking tool Axel used, it had largely been ignored thus far.</p>



<p>Ironically (as you&#8217;ll soon see) Axel chose <code>wrk</code> specifically because <a href="https://tech.phlux.us/Juice-Sucking-Servers/#benchmarking-software" data-wpel-link="external" target="_blank" rel="external noopener">he didn&#8217;t like the behaviour he saw</a> with <a href="https://httpd.apache.org/docs/2.4/programs/ab.html" data-wpel-link="external" target="_blank" rel="external noopener">ApacheBench</a>. Mostly its lack of HTTP/1.1 connection reuse (a subjective but valid methodology choice on Axel&#8217;s part) but also because it sounds like he saw some inexplicable results from it too. In hindsight, that might have been a clue that something more pervasive was wrong.</p>



<p>In retrospect there were a few tangential comments in the Swift Forums thread that were on the right track, e.g.:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>…when a new connection comes in, the server needs to make a decision: It can</p>



<ul class="wp-block-list">
<li>Either accept the new connection immediately, slowing the existing connections down a little (because now there are more connections to service with the same resources as before)</li>



<li>Or it can prioritise the existing connections and slow the connection acceptance (increasing the latency of the first request in the new connection which now has to wait).</li>
</ul>
<cite><a href="https://forums.swift.org/u/johannesweiss/summary" data-wpel-link="external" target="_blank" rel="external noopener">Johannes Weiss</a>, <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/77" data-wpel-link="external" target="_blank" rel="external noopener">Swift Forums post</a></cite></blockquote>



<p>As a little spoiler, it seems apparent that the other three web frameworks all accept incoming connections virtually immediately with priority over any existing connections &amp; request handling (even though they don&#8217;t necessarily attempt to <em>serve</em> all those connections&#8217; requests simultaneously). Vapor does not.</p>



<p>Suspicions did [correctly] develop around the opening of the connections themselves, which triggered testing with longer timeouts in a somewhat blind attempt to cover-up the &#8220;spurious&#8221; first moments of the test.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>❌ Trying to essentially just hide inconvenient results is unlikely to help. It may even be successful, which is the worst possible outcome because it&#8217;s basically just burying a time-bomb into the benchmark, <em>and</em> forgoing any real understanding &amp; potential knowledge to be gained from properly investigating the problem.</p>
</div></div>



<h3 class="wp-block-heading">Characterising the failure mode(s)</h3>



<p>Though admittedly I wasn&#8217;t <em>fully</em> conscious of what I was doing at the time, the next breakthrough came from simply <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/85" data-wpel-link="external" target="_blank" rel="external noopener">gathering more data and analysing it <em>qualitatively</em></a>. This helped in two key ways:</p>



<ul class="wp-block-list">
<li>It better defined and pinned down the circumstances in which things appear to go wrong with the benchmark itself.<br><br>It separated out a whole bunch of test configurations that seemingly weren&#8217;t interesting (as they behaved in line with intuition / expectations, and similarly across all four web servers).</li>
</ul>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>✅ When it doubt, try to better define the problem. Eliminate variables. Refine quantitative estimates. Make your life easier by eliminating things that don&#8217;t matter.</p>
</div></div>



<ul class="wp-block-list">
<li>It provided hints and potential insight into the nature of the problem.<br><br>It showed that there was some kind of variability (in time) in the benchmark&#8217;s behaviour, with three very distinct modes (including one which was basically the benchmark actually working as expected, the existence of which had been unknown until that point!).</li>
</ul>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>✅ There are <em>many</em> ways to approach a data set, in terms of analysis methods. It&#8217;s a good idea to always keep that in mind, and to try different analysis mindsets whenever you seem stuck (and also to further validate conclusions).</p>
</div></div>



<p>Interestingly although ultimately only tangentially, this modality finding prompted quite a few &#8220;me too!&#8221; responses from other folks, about a variety of use-cases involving Vapor <em>or</em> NIO. I took that as affirmation that I was onto something real, but in retrospect that should have been an even better clue: the fact that some people had seen this issue <em>without</em> Vapor involved &#8211; the only common denominator was NIO. Even though it turns out NIO itself wasn&#8217;t doing any wrong, it was on the right path to answers. <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/87" data-wpel-link="external" target="_blank" rel="external noopener">This was <em>specifically</em> pointed out to everyone</a>, even.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>☝️ Sometimes, it just comes down to needing to listen better.</p>
</div></div>



<h3 class="wp-block-heading">Overlooked clues</h3>



<p>At this point there were a bunch of discussions about <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/89" data-wpel-link="external" target="_blank" rel="external noopener">benchmark tool configuration</a>, <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/91" data-wpel-link="external" target="_blank" rel="external noopener">hardware arrangement</a>, <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/102" data-wpel-link="external" target="_blank" rel="external noopener">whether TLS should be used</a>, etc. I&#8217;m going to skim over it, because there&#8217;s not much to ultimately say about it &#8211; it turned out to not be on the right track in this case, or purely tangential, but it was entirely reasonable to investigate &amp; discuss those aspects. Such is debug life.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="600" height="600" src="https://wadetregaskis.com/wp-content/uploads/2024/05/Debug-Life-t-shirt.avif" alt="" class="wp-image-8109" style="object-fit:cover" srcset="https://wadetregaskis.com/wp-content/uploads/2024/05/Debug-Life-t-shirt.avif 600w, https://wadetregaskis.com/wp-content/uploads/2024/05/Debug-Life-t-shirt-256x256.avif 256w, https://wadetregaskis.com/wp-content/uploads/2024/05/Debug-Life-t-shirt-256x256@2x.avif 512w" sizes="auto, (max-width: 600px) 100vw, 600px" /><figcaption class="wp-element-caption">I couldn&#8217;t find evidence that anyone&#8217;s gotten the tattoo yet, but you can at least <a href="https://www.redbubble.com/i/t-shirt/Debug-Life-White-Typographic-Design-for-Thug-Programmers-by-ramiro/17317023.FB110" data-wpel-link="external" target="_blank" rel="external noopener">get the wardrobe</a>.</figcaption></figure>
</div>


<p>What&#8217;s interesting is that yet another key clue was mentioned in the Swift Forums thread, yet was overlooked because it was attributed incorrectly and the mechanics miscategorised:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Don&#8217;t test with more than 128 connections. You will get read errors. This is due to the file descriptor limit applied to each process on macOS. As&nbsp;<a href="https://forums.swift.org/u/johannesweiss" data-wpel-link="external" target="_blank" rel="external noopener">@johannesweiss</a>&nbsp;mentioned earlier the default for this is 256. You can change this but it involves disabling the System Integrity Protection.</p>
<cite><a href="https://forums.swift.org/u/adam-fowler/summary" data-wpel-link="external" target="_blank" rel="external noopener">Adam Fowler</a>, <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/99" data-wpel-link="external" target="_blank" rel="external noopener">Swift Forums thread</a></cite></blockquote>



<p>The 128 connections &amp; read errors parts were spot on, in hindsight. But the rest was incorrect (it&#8217;s not about the file descriptor ulimit) and in particular the incorrect statement about having to disable SIP perhaps further distracted readers (corrections were posted in reply, which perhaps steered the thread away from what actually mattered).</p>



<p>I&#8217;m not sure what precisely the lesson is here… if Adam had better understood the behaviour he&#8217;d seen previously (re. 128 connections being the apparent limit) he might have been able to immediately point out one of the key problems. But who can say why he didn&#8217;t quite understand that limit correctly, or whether he should have. This sort of thing happens, and <em>maybe</em> it suggests a failure to properly diagnose problems previously, but mostly I&#8217;d just point out that the discrepancy here &#8211; between 128 and 256 &#8211; <em>should</em> have been noticed, and had it been questioned it would have accelerated progress towards the root cause.</p>



<p>Speaking just for myself, I think I (erroneously) dismissed Adam&#8217;s comment because I already knew that the default file descriptor limit is <em>not</em> actually 256 (it&#8217;s 2,560 on macOS, mostly) and so I assumed the <em>whole</em> comment was wrong and irrelevant.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>⚠️ Partly wrong is not the same as completely wrong (let-alone useless).</p>
</div></div>



<p>Another clue was put forth, yet again essentially by accident (without understanding its significance, at the time):</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Yes, the reason I used&nbsp;<code>wrk</code>, is that it uses pipelining. That&#8217;s why ab (apachebench) had such terrible performance: it opened a new socket for each request. And then it overloaded the system by throwing</p>



<ul class="wp-block-list">
<li><code>socket: Too many open files</code></li>



<li><code>apr_socket_recv: Connection reset by peer&nbsp;</code></li>
</ul>



<p>errors.</p>



<p>I raised the&nbsp;<code>ulimit -n</code>&nbsp;to 10240, but still&nbsp;<code>apr_socket_recv: Connection reset by peer (104)</code>&nbsp;occurred occasionally.</p>
<cite><a href="https://forums.swift.org/u/axello/summary" data-wpel-link="external" target="_blank" rel="external noopener">Axel Roest</a>, <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/122" data-wpel-link="external" target="_blank" rel="external noopener">Swift Forums thread</a></cite></blockquote>



<p>This hinted very directly at the second major problem, but it seems nobody in the forum thread realised it. I think there was still a pre-occupation with the file descriptor ulimit.</p>



<p>A little logic applied at the time of Axel&#8217;s comment <em>should</em> have revealed its mistaken presumption: that opening new TCP connections for each HTTP request will inevitably cause connection failures. Sure, it will if you give it enough concurrent connection attempts, but real-world web servers operate at <em>huge</em> loads that are basically one HTTP request per connection, without any significant reliability problems. In hindsight, it&#8217;s clear that Axel&#8217;s dismissal of this behaviour as in any way normal was a mistake &#8211; as was everyone else in the thread going along with that dismissal.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>⚠️ If a tool isn&#8217;t working the way you expect, maybe that&#8217;s telling you something important. Just switching tools until you find one which doesn&#8217;t exhibit the problem doesn&#8217;t necessarily mean it&#8217;s not still a problem.</p>
</div></div>



<h3 class="wp-block-heading">A misunderstood workaround</h3>



<p>In parallel to all of the above discussion in the Swift Forums thread, I&#8217;d been diving into <code>wrk</code> to see what it was really doing. I discovered <em>a way</em> to eliminate the errors: by opening all the TCP connections in advance in a way that <em>happened</em> to limit how many were attempted concurrently by <code>wrk</code> thread count which <em>happened</em> to be low enough in my use of <code>wrk</code> to not hit the magic 128 limit (more on that later). As you can see in <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/123" data-wpel-link="external" target="_blank" rel="external noopener">my forum post on this</a>, I initially misunderstood how <code>wrk</code> functioned and misattributed the root cause as bugs / bad design in <code>wrk</code>.</p>



<p>In my defence, <code>wrk</code> isn&#8217;t written very well, eschewing such outrageous and bourgeois software engineering practices as, you know, actually checking for errors. So it wasn&#8217;t unreasonable to believe it was ultimately just broken, given plenty of evidence that it was at least partly broken (which it was &amp; is), but it was ultimately a mistake to let that cloud my judgement of each individual behaviour.</p>



<p>Then again, if I hadn&#8217;t been so appalled by the bad code in <code>wrk</code>, and taken it upon myself to rewrite key parts of it, I might not have stumbled onto the above &#8220;fix&#8221; and therefore also not found the true cause, later.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>✅ Improving error handling &amp; reporting is practically always a good idea. And when debugging a problem it can be helpful even if it doesn&#8217;t feel guided &#8211; the whole point of absent or incorrect error reporting is that you don&#8217;t know what you&#8217;re missing, so you may well reveal an important clue &#8220;by accident&#8221;.</p>
</div></div>



<h3 class="wp-block-heading">It&#8217;s never the compiler or the kernel… except when it is</h3>



<p>At the time I did think I&#8217;d actually <em>fixed</em> <code>wrk</code>; I didn&#8217;t realise I&#8217;d merely found an imperfect workaround. I&#8217;d solved the connection errors (not really)! But, I was still curious about one thing &#8211; something pretty much everyone had kinda ignored this whole time:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Though those lingering few read/write errors still bother me. I might look into them later.</p>
<cite>Me, <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/123" data-wpel-link="external" target="_blank" rel="external noopener">Swift Forums thread</a></cite></blockquote>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>✅ Curiosity is <em>powerful</em>. Why did my chocolate bar melt in my pocket when I walked through the lab, <a href="https://www.technologyreview.com/1999/01/01/236818/melted-chocolate-to-microwave/" data-wpel-link="external" target="_blank" rel="external noopener">maybe that&#8217;s interesting</a>? Why did this contaminated Petri dish end up full of fungus instead of bacteria, <a href="https://www.healio.com/news/endocrinology/20120325/penicillin-an-accidental-discovery-changed-the-course-of-medicine" data-wpel-link="external" target="_blank" rel="external noopener">maybe that&#8217;s interesting</a>? Why&#8217;s this unused screen glowing, <a href="https://www.aps.org/publications/apsnews/200111/history.cfm" data-wpel-link="external" target="_blank" rel="external noopener">maybe that&#8217;s interesting</a>? Ow, why did this apple fall on my head… but, <a href="https://education.nationalgeographic.org/resource/isaac-newton-who-he-was-why-apples-are-falling/" data-wpel-link="external" target="_blank" rel="external noopener">maybe that&#8217;s interesting</a>? (<a href="https://www.newscientist.com/article/2170052-newtons-apple-the-real-story/" data-wpel-link="external" target="_blank" rel="external noopener">apocryphal</a>, but close enough)</p>
</div></div>



<p>Tracing those reported errors to their cause was quite a challenge. The only known way to reproduce the errors was to use a very high number of concurrent TCP connections (several thousand), which made it hard to follow any <em>single</em> connection through its lifecycle using any low-brow methods (printf debugging etc). <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/129" data-wpel-link="external" target="_blank" rel="external noopener">I eventually managed</a> using System Trace<sup data-fn="65590619-a219-4fb0-87ea-fd2c98990365" class="fn"><a href="#65590619-a219-4fb0-87ea-fd2c98990365" id="65590619-a219-4fb0-87ea-fd2c98990365-link">2</a></sup> (lamenting, the entire time I used Instruments, that it would have been <a href="https://leopard-adc.pepas.com/documentation/DeveloperTools/Conceptual/SharkUserGuide/SystemTracing/SystemTracing.html" data-wpel-link="external" target="_blank" rel="external noopener">so much easier in Shark</a>).</p>



<p>Unfortunately, what I was seeing &#8211; while in fact correct &#8211; did not make sense to me, so I was hesitant to take it on face value.</p>



<p>The lack of any error reporting on the server side, because Vapor lacks it completely, was also both a known problem at the time and also a problem in hindsight. Had Vapor/NIO actually reported the errors they were encountering, it would have partially validated what I was seeing in the system traces &#8211; in fact, it would probably have saved me from having to capture &amp; analyse system traces.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>❌ Ignoring errors is always a bad idea. I mean, duh, right? But apparently it has to be reiterated.</p>
</div></div>



<p>Alas I don&#8217;t actually remember now precisely what led me to the final answers and root causes. I know it involved many hours of experimenting, exploring hypotheses, and in generally fiddling with everything I could think of.</p>



<p>Somehow or other, I did finally cotton on to a key configuration parameter: <code>kern.ipc.somaxconn</code>.</p>



<p>That controls how many connection requests can be pending (not formally accepted by the server) at one time. It defaults to 128 on macOS. Remember that number, 128?</p>



<p>Once I had figured out that <code>kern.ipc.somaxconn</code> directly controlled the problematic behaviour, the rest followed pretty naturally and quickly &#8211; I realised that what I saw in the system traces was in fact accurate, and that in turn revealed that the macOS kernel contains multiple surprisingly blatant and serious bugs (or at the very least dubious design choices, and lying documentation) regarding TCP sockets in non-blocking mode. I wrote that up in some detail in the second half of <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/132" data-wpel-link="external" target="_blank" rel="external noopener">this Swift Forums post</a>.</p>



<p>As a sidenote, that darn magic number that everyone kept ignoring &#8211; 128 &#8211; cropped up yet again, in <a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/listen.2.html" data-wpel-link="external" target="_blank" rel="external noopener">the <code>listen</code> man page</a>, though by the time I saw it there it was merely a confirmation of what I&#8217;d already discovered, than a helpful clue. Still, perhaps there&#8217;s a lesson there: read the man page. 😆</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>❌ When documenting known bugs and limitations, explain them fully. Don&#8217;t just say e.g. &#8220;more than 128 doesn&#8217;t work&#8221;, say <em>why</em>.</p>
</div></div>



<h2 class="wp-block-heading">Conclusion</h2>



<p>All told, the major problems identified by the benchmark were (and not all of these were mentioned above, but you can find all the details in <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583" data-wpel-link="external" target="_blank" rel="external noopener">the Swift Forums thread</a>):</p>



<ul class="wp-block-list">
<li>The particular 3rd party library used for BigInt support in Swift, <a href="https://github.com/attaswift/BigInt" data-wpel-link="external" target="_blank" rel="external noopener">attaswift/BigInt</a>, performs quite poorly.</li>



<li>Vapor would accept too few connections per cycle of its event loop (promptly fixed, in <a href="https://github.com/vapor/vapor/releases/tag/4.96.0" data-wpel-link="external" target="_blank" rel="external noopener">4.96.0</a>).</li>



<li>The benchmark tool used, <code><a href="https://github.com/wg/wrk" data-wpel-link="external" target="_blank" rel="external noopener">wrk</a></code>, has numerous bugs:
<ul class="wp-block-list">
<li>It doesn&#8217;t always use the configured number of concurrent connections.</li>



<li>It doesn&#8217;t measure latency correctly.</li>



<li>It doesn&#8217;t report errors correctly (in the sense both that it miscategorises them, e.g. connect vs read/write, and that it doesn&#8217;t provide enough detail to understand what they are, such as by including the errno).</li>
</ul>
</li>



<li>The macOS kernel (and seemingly Linux kernel likewise) has multiple bugs:
<ul class="wp-block-list">
<li>Connection errors are reported incorrectly (as <code>ECONNRESET</code> or <code>EBADF</code>, instead of <code>ECONNREFUSED</code>).</li>



<li>kqueue (kevents) behaves as if all connections are always accepted, even when they are not. Put another way, you cannot actually tell if a connection was successful when using non-blocking sockets on macOS.</li>
</ul>
</li>



<li>Key network configuration on macOS &amp; Linux is way too restrictive:
<ul class="wp-block-list">
<li>Maximum file descriptors per process is only 2,560 generally on macOS, and even less (256) in GUI apps. It may vary on Linux, but on Axel&#8217;s particular server it was 1,024.</li>



<li>Maximum number of unaccepted connection requests (the <code>kern.ipc.somaxconn</code> sysctl on macOS, <code>/proc/sys/net/core/somaxconn</code> on Linux) is only 128 on macOS. It may vary on Linux.</li>
</ul>
</li>
</ul>



<p>It <em>appears</em> that the kernel bugs apply to Linux as well (although it&#8217;s not known if kqueue was in use there, as <code>wrk</code> also supports <code>epoll</code> and <code>select</code>), as the behaviour seems to be the same between macOS and Linux.</p>



<p>With the above issues fixed or worked around, <a href="https://tech.phlux.us/Juice-Sucking-Servers-Part-Trois/" data-wpel-link="external" target="_blank" rel="external noopener">his benchmark produces more explicable results</a> (but keep in mind that the difference in connection acceptance behaviour <em>is real</em> and reflects a different design trade-off in Vapor, which <em>may</em> be a problem for real-world use if you don&#8217;t raise <code>somaxconn</code> and the listen backlog limit enough).</p>



<p>And that&#8217;s all just the <em>problems</em> Axel&#8217;s benchmark surfaced &#8211; there was a whole host of other interesting lessons taken away from all this (only a fraction of which were highlighted in this post &#8211; many more can be found in Axel&#8217;s posts and the Swift Forums thread).</p>



<p>Nominally the end result is also a benchmark that shows Vapor (Swift) out-performing other popular web frameworks in other languages. <em>Hugely</em> out-performing them, if you factor in not just throughput &amp; latency but RAM &amp; power usage. But, to reiterate <a href="#do-these-improvements-apply-to-the-other-cases-too">what I pointed out earlier</a>, take that with a grain of salt.</p>



<p>So, for a benchmark that many initially decried as unrealistic or plain poorly conceived, it turned out to be pretty darn useful, I think. And if that doesn&#8217;t make it a successful benchmark, I don&#8217;t know what does.</p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-dots"/>



<h1 class="wp-block-heading">Addendum: post title</h1>



<p>Looking at the <a href="https://news.ycombinator.com/item?id=40374946" data-wpel-link="external" target="_blank" rel="external noopener">comments about this post on HackerNews</a> etc, I feel like I have to explain the title a little. I was quite pleased with myself when I came up with it (admittedly by accident), because it&#8217;s subtle and I think kinda clever, but perhaps too subtle.</p>



<p>&#8220;Swift sucks at web serving… or does it?&#8221; is a [platonic] double entendre.</p>



<p>On face value it&#8217;s alluding to the more typical type of post that is both (a) click-baity and (b) a standard &#8220;turns out&#8221; story where actually Swift is <em>awesome</em> at web server and haha to all those who doubted it. (where one can replace the word &#8220;Swift&#8221; with basically any programming technology, because benchmarking brings out some ugly competitiveness from the community)</p>



<p>But <em>really</em> what it means here, if you read the whole post, is that actually <em>we still don&#8217;t know</em>. It&#8217;s alluding to the oft-overlooked fact that benchmarks are rarely as conclusive as they&#8217;re presented. Which I thought was quite clever because it reiterates, at a meta level, my whole point about learning being more important than competing.</p>



<p>At least, that was the idea. 😆</p>


<ol class="wp-block-footnotes"><li id="e7728698-06db-400a-a6b9-01a2ce4f3e5b"><a href="https://github.com/helidon-io/helidon" data-wpel-link="external" target="_blank" rel="external noopener">Helidon itself is written in Java</a>, but <a href="https://gitlab.com/axello/serverbench/-/tree/main/java/src/main/kotlin?ref_type=heads" data-wpel-link="external" target="_blank" rel="external noopener">Axel used Kotlin for his little web server implementation</a> &#8211; including most crucially the Fibonacci calculations.  Both interoperate atop the <a href="https://en.wikipedia.org/wiki/Java_virtual_machine" data-wpel-link="external" target="_blank" rel="external noopener">JVM</a> and plenty of &#8220;Java&#8221; libraries are partly written in Kotlin, or have dependencies written in Kotlin &#8211; and vice versa.  A little like Objective-C and Swift interoperate such that many Mac / iDevice apps use a rich mix of both and you don&#8217;t typically need to care which language is used for any particular piece. <a href="#e7728698-06db-400a-a6b9-01a2ce4f3e5b-link" aria-label="Jump to footnote reference 1">↩︎</a></li><li id="65590619-a219-4fb0-87ea-fd2c98990365">I always endeavour to link to the things I mention, but in this case there&#8217;s nothing to link to &#8211; Apple don&#8217;t provide any actual documentation of the System Trace tool in Instruments, and there&#8217;s not even any usable 3rd party guide to it, that I can find.  It&#8217;s a sad demonstration of Apple&#8217;s general indifference to performance tools. 😔<br><br>Apple don&#8217;t even have a proper product page for Instruments itself &#8211; the closest you can find is merely <a href="https://help.apple.com/instruments/mac/10.0/#/" data-wpel-link="external" target="_blank" rel="external noopener">its Help</a>. <a href="#65590619-a219-4fb0-87ea-fd2c98990365-link" aria-label="Jump to footnote reference 2">↩︎</a></li></ol>]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/swift-sucks-at-web-serving-or-does-it/feed/</wfw:commentRss>
			<slash:comments>13</slash:comments>
		
		
			<media:content url="https://wadetregaskis.com/wp-content/uploads/2024/05/Web-server-comparison-throughput.webp" medium="image" />
<post-id xmlns="com-wordpress:feed-additions:1">8061</post-id>	</item>
		<item>
		<title>Swift tip: the swap function</title>
		<link>https://wadetregaskis.com/swift-tip-the-swap-function/</link>
					<comments>https://wadetregaskis.com/swift-tip-the-swap-function/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Tue, 14 May 2024 23:43:20 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[Fibonacci]]></category>
		<category><![CDATA[MutableCollection]]></category>
		<category><![CDATA[swap]]></category>
		<category><![CDATA[swapAt]]></category>
		<category><![CDATA[Swift]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=8079</guid>

					<description><![CDATA[The following code prints the Fibonacci sequence. You&#8217;ve probably seen it before. It&#8217;s one of the simplest and most well-known examples of a sliding window operation &#8211; where the next value depends on the preceding two (or more) values. While almost all programs do not calculate the Fibonacci sequence, many do contain similar sliding-window algorithms.&#8230; <a class="read-more-link" href="https://wadetregaskis.com/swift-tip-the-swap-function/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>The following code prints the Fibonacci sequence. You&#8217;ve probably seen it before. It&#8217;s one of the simplest and most well-known examples of a sliding window operation &#8211; where the next value depends on the preceding two (or more) values. While almost all programs do <em>not</em> calculate the Fibonacci sequence, many do contain similar sliding-window algorithms. And code that uses the <code><a href="https://developer.apple.com/documentation/swift/array/reduce(_:_:)" data-wpel-link="external" target="_blank" rel="external noopener">reduce(_:_:)</a></code> / <code><a href="https://developer.apple.com/documentation/swift/array/reduce(into:_:)" data-wpel-link="external" target="_blank" rel="external noopener">reduce(into:_:)</a></code> methods is usually doing a similar thing, too.</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">var</span><span style="color: #000000"> previous = </span><span style="color: #795E26">UIntXL</span><span style="color: #000000">(</span><span style="color: #098658">0</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #0000FF">var</span><span style="color: #000000"> current = </span><span style="color: #795E26">UIntXL</span><span style="color: #000000">(</span><span style="color: #098658">1</span><span style="color: #000000">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #795E26">print</span><span style="color: #000000">(previous)</span></span>
<span class="line"><span style="color: #795E26">print</span><span style="color: #000000">(current)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #AF00DB">while</span><span style="color: #000000"> </span><span style="color: #0000FF">true</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">let</span><span style="color: #000000"> next = previous + current</span></span>
<span class="line"><span style="color: #000000">    previous = current</span></span>
<span class="line"><span style="color: #000000">    current = next</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #795E26">print</span><span style="color: #000000">(next)</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>Short of using a different algorithm entirely (there are <a href="https://r-knott.surrey.ac.uk/Fibonacci/fibFormula.html" data-wpel-link="external" target="_blank" rel="external noopener">much smarter ways to calculate Fibonacci numbers</a>), you&#8217;d think it&#8217;d be optimally fast &#8211; I mean, how can the above example get any better, really?</p>



<p>Well, by doing this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">var</span><span style="color: #000000"> previous = </span><span style="color: #795E26">UIntXL</span><span style="color: #000000">(</span><span style="color: #098658">0</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #0000FF">var</span><span style="color: #000000"> current = </span><span style="color: #795E26">UIntXL</span><span style="color: #000000">(</span><span style="color: #098658">1</span><span style="color: #000000">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #795E26">print</span><span style="color: #000000">(previous)</span></span>
<span class="line"><span style="color: #795E26">print</span><span style="color: #000000">(current)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #AF00DB">while</span><span style="color: #000000"> </span><span style="color: #0000FF">true</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    previous += current</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #795E26">swap</span><span style="color: #000000">(&amp;previous, &amp;current)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #795E26">print</span><span style="color: #000000">(next)</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>Sometimes this version is significantly faster (e.g. <a href="https://forums.swift.org/t/standard-vapor-website-drops-1-5-of-requests-even-at-concurrency-of-100/71583/73" data-wpel-link="external" target="_blank" rel="external noopener">30% in a recent example</a>, and I&#8217;ve seen up to 14x in some of my own, similar cases).</p>



<p>Admittedly, sometimes it&#8217;s merely as fast &#8211; sometimes the Swift compiler optimises the first version into the second for us. But the compiler&#8217;s optimiser is unreliable and unpredictable. So if performance is important to you, it&#8217;s best to use <code>swap</code> explicitly rather than hang your hopes on the compiler.</p>



<h2 class="wp-block-heading">What does <code><a href="https://developer.apple.com/documentation/swift/swap(_:_:)" data-wpel-link="external" target="_blank" rel="external noopener">swap</a></code> do?</h2>



<p>Its purpose is pretty obvious &#8211; it swaps the contents of two variables. <em>Logically</em> it&#8217;s equivalent to<sup data-fn="3245c9e9-e853-452f-9f59-1821ed0d19bc" class="fn"><a href="#3245c9e9-e853-452f-9f59-1821ed0d19bc" id="3245c9e9-e853-452f-9f59-1821ed0d19bc-link">1</a></sup>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="func swap(_ a: inout T, _ b: inout T) {
    let tmp = a
    a = b
    b = tmp
}" style="color:#000000;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">func</span><span style="color: #000000"> </span><span style="color: #795E26">swap</span><span style="color: #000000">(</span><span style="color: #795E26">_</span><span style="color: #000000"> </span><span style="color: #001080">a</span><span style="color: #000000">: </span><span style="color: #0000FF">inout</span><span style="color: #000000"> T, </span><span style="color: #795E26">_</span><span style="color: #000000"> </span><span style="color: #001080">b</span><span style="color: #000000">: </span><span style="color: #0000FF">inout</span><span style="color: #000000"> T) {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">let</span><span style="color: #000000"> tmp = a</span></span>
<span class="line"><span style="color: #000000">    a = b</span></span>
<span class="line"><span style="color: #000000">    b = tmp</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>But that&#8217;s <em>not</em> how it&#8217;s actually implemented. The implementation starts <a href="https://github.com/apple/swift/blob/c1597154a935b0f61646c5accb79a45ce470f944/stdlib/public/core/MutableCollection.swift#L531" data-wpel-link="external" target="_blank" rel="external noopener">here</a>, though the pertinent details are hidden behind compiler built-ins. Suffice to know that it boils down to <em>simply swapping the raw bytes</em>.</p>



<h2 class="wp-block-heading">Why is that better?</h2>



<p>It avoids unnecessary work: temporary object initialisation and deinitialisation, memory allocation (and frees), reference-counting, etc.  That can be from the more efficient swap itself, and/or from mutating one of the values in place rather than creating a temporary third value.</p>



<p>It can also prevent unnecessary copy-on-write behaviour, if you&#8217;re mixing the swap in amongst other mutations on value types that implement reference semantics<sup data-fn="c5986145-3ce7-4086-ade3-ef670ccba9d1" class="fn"><a href="#c5986145-3ce7-4086-ade3-ef670ccba9d1" id="c5986145-3ce7-4086-ade3-ef670ccba9d1-link">2</a></sup> (like the standard <code>Array</code>, <code>Set</code>, etc).  That usually saves time but can also, in some relatively rare circumstances, save memory too (by not leaving duplicate copies of internal buffers in memory indefinitely).</p>



<p>It also works for <code><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md" data-wpel-link="external" target="_blank" rel="external noopener">~Copyable</a></code> types without any extra effort.</p>



<h2 class="wp-block-heading">Is it always optimal?</h2>



<p>For <em>actually</em> swapping two values in RAM, <s>yes</s>.</p>



<p>Well, maybe.  Mostly?  It turns out &#8211; after I initially published this post &#8211; that it&#8217;s a bit more complicated than that.  <em>Sometimes</em> an alternative method is slightly faster:  the &#8220;tuple swap&#8221;.  e.g.:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="var previous = UIntXL(0)
var current = UIntXL(1)

print(previous)
print(current)

while true {
    previous += current
    (previous, current) = (current, previous)

    print(next)
}" style="color:#000000;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">var</span><span style="color: #000000"> previous = </span><span style="color: #795E26">UIntXL</span><span style="color: #000000">(</span><span style="color: #098658">0</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #0000FF">var</span><span style="color: #000000"> current = </span><span style="color: #795E26">UIntXL</span><span style="color: #000000">(</span><span style="color: #098658">1</span><span style="color: #000000">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #795E26">print</span><span style="color: #000000">(previous)</span></span>
<span class="line"><span style="color: #795E26">print</span><span style="color: #000000">(current)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #AF00DB">while</span><span style="color: #000000"> </span><span style="color: #0000FF">true</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    previous += current</span></span>
<span class="line"><span style="color: #000000">    (previous, current) = (current, previous)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #795E26">print</span><span style="color: #000000">(next)</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>In <em>some</em> benchmarks that&#8217;s faster, in some it&#8217;s slower<sup data-fn="f4e9238d-e4d4-416f-90d2-369fd82972bb" class="fn"><a href="#f4e9238d-e4d4-416f-90d2-369fd82972bb" id="f4e9238d-e4d4-416f-90d2-369fd82972bb-link">3</a></sup>. It <em>seems</em> to always be faster than using a named temporary, just like <code>swap</code>, although I don&#8217;t believe that&#8217;s guaranteed behaviour. Furthermore, it relies on the compiler successfully recognising that particular expression of the swap intent and optimising it appropriately. Plus, using <code>swap</code> is shorter, clearer, and less error-prone. So I think an explicit call to <code>swap</code> is still the best option.</p>



<p>In any case, there are sometimes faster methods which eliminate the need to actually swap the bytes around. e.g. using a pointer to toggle between the two values. To my knowledge, Swift doesn&#8217;t provide any way to actually do that for value types, since Swift tries to pretend that pointers don&#8217;t exist for them. 😔</p>



<p>Hypothetically the optimiser could perform that optimisation for any code like the above examples, and others, although I&#8217;ve never seen it do that.</p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-dots"/>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>☝️ <code>swap</code> has a cousin, <code><a href="https://developer.apple.com/documentation/swift/mutablecollection/swapat(_:_:)-8f65z" data-wpel-link="external" target="_blank" rel="external noopener">swapAt</a></code>, on <code><a href="https://developer.apple.com/documentation/swift/mutablecollection" data-wpel-link="external" target="_blank" rel="external noopener">MutableCollection</a></code>s. It&#8217;s even <em>less</em> known than <code>swap</code>, but it&#8217;s potentially even more frequently useful in general code. Its purpose is to likewise ensure that swapping two elements within the same collection is as efficient as possible, irrespective of how the compiler is feeling that day.</p>



<p>But, bizarrely, <a href="https://github.com/apple/swift/blob/c1597154a935b0f61646c5accb79a45ce470f944/stdlib/public/core/MutableCollection.swift#L320" data-wpel-link="external" target="_blank" rel="external noopener">its implementation</a> does <em>not</em> use <code>swap</code> or otherwise contain the same guaranteed optimisations. So it might <em>not</em> be the fastest way to swap two elements. 😕</p>



<p>It might be an unfortunate consequence of Swift&#8217;s exclusive access checking &#8211; specifically its lack of support for non-overlapping access within a collection &#8211; because if you do try to use <code>swap</code> yourself on the elements of a collection, the compiler refuses it:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><code>❌ Overlapping accesses to 'x', but modification requires exclusive access; consider calling MutableCollection.swapAt(_:_:)</code></p>
</blockquote>
</div></div>


<ol class="wp-block-footnotes"><li id="3245c9e9-e853-452f-9f59-1821ed0d19bc">Ignoring support for <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md" data-wpel-link="external" target="_blank" rel="external noopener">~Copyable</a> types, which can be done but requires a more complicated implementation than we need to worry about here. <a href="#3245c9e9-e853-452f-9f59-1821ed0d19bc-link" aria-label="Jump to footnote reference 1">↩︎</a></li><li id="c5986145-3ce7-4086-ade3-ef670ccba9d1">Many value types in Swift are <em>not</em> actually just simple sequences of bytes.  They often contain pointers to potentially-shared <em>reference</em> objects (e.g. classes or actors), or raw memory buffers.  When you copy the value &#8211; which includes simply assigning it to a new variable &#8211; there can be a lot of work involved in incrementing reference counts for these now-shared internal objects &amp; buffers (which also means time spent decrementing those counts some time later).<br><br>And that&#8217;s assuming a type that uses copy-on-write (or similar) optimisations &#8211; otherwise, you&#8217;re going to have to copy all those internal memory buffers as well, which can be tremendously expensive.<br><br>Even mere reference counting operations are actually pretty expensive &#8211; tens to hundreds of instructions each, typically.  <em>Usually</em> in Swift we don&#8217;t notice them because the optimiser works very hard to actually remove them, as much as possible. <a href="#c5986145-3ce7-4086-ade3-ef670ccba9d1-link" aria-label="Jump to footnote reference 2">↩︎</a></li><li id="f4e9238d-e4d4-416f-90d2-369fd82972bb">I put together <a href="https://github.com/wadetregaskis/Swift-Benchmarks/blob/main/Benchmarks/Swap/Swap.swift" data-wpel-link="external" target="_blank" rel="external noopener">some benchmarks</a> in researching &amp; writing this post, with the intent of using them as specific examples of the performance differences.  Unfortunately they are apparently too simple, and the optimiser is generally able to optimise the named temporary method into using <code>swap</code> or the &#8220;tuple swap&#8221; method (even though in real-world code it usually fails to do so, in my experience). <a href="#f4e9238d-e4d4-416f-90d2-369fd82972bb-link" aria-label="Jump to footnote reference 3">↩︎</a></li></ol>]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/swift-tip-the-swap-function/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">8079</post-id>	</item>
		<item>
		<title>The cost of electrical power in servers</title>
		<link>https://wadetregaskis.com/the-cost-of-electrical-power-in-servers/</link>
					<comments>https://wadetregaskis.com/the-cost-of-electrical-power-in-servers/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Sun, 12 May 2024 17:27:41 +0000</pubDate>
				<category><![CDATA[Education]]></category>
		<category><![CDATA[efficiency]]></category>
		<category><![CDATA[electricity]]></category>
		<category><![CDATA[power]]></category>
		<category><![CDATA[servers]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=8055</guid>

					<description><![CDATA[Most people don&#8217;t have any idea what the true cost of server electricity use is. This is for many reasons, but I want to highlight the main factors. Usage is hidden In leasing / colo arrangements electricity cost is often flat-rated, and/or bundled up with many other costs. Many hosts don&#8217;t provide electricity usage reporting&#8230; <a class="read-more-link" href="https://wadetregaskis.com/the-cost-of-electrical-power-in-servers/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>Most people don&#8217;t have any idea what the true cost of server electricity use is.  This is for many reasons, but I want to highlight the main factors.</p>



<h2 class="wp-block-heading">Usage is hidden</h2>



<p>In leasing / colo arrangements electricity cost is often flat-rated, and/or bundled up with many other costs.</p>



<p>Many hosts don&#8217;t provide electricity usage reporting at all.  And those that do sometimes provide it only obliquely as the nominal dollar cost, not in terms of actual <a href="https://en.wikipedia.org/wiki/Joule" data-wpel-link="external" target="_blank" rel="external noopener">Joules</a> used (typically expressed as kWh or MWh, which are just different units for the same measure).</p>



<p>If you build &amp; operate your own datacentre, you do get to see your actual usage &#8211; at least in terms of power (Watts) and energy (Joules) if not more holistic cost &#8211; but <em>very</em> few people have any actual experience with that.</p>



<h2 class="wp-block-heading">The sticker price is not the <em>cost</em></h2>



<p>Most of us have no idea what electricity actually costs in <em>any</em> context, even for our own residential use at home, or for electric car charging.  <a href="https://blogs.edf.org/energyexchange/2016/04/28/the-true-cost-of-electricity-what-were-not-paying-for-through-our-utility-bills/" data-wpel-link="external" target="_blank" rel="external noopener">Practically nobody pays the <em>actual</em> cost of electricity up front</a> (<a href="https://www.sciencedirect.com/science/article/pii/S2214629620304606" data-wpel-link="external" target="_blank" rel="external noopener">more details</a>).  The full cost is usually <em>many</em> times larger than what is claimed on the bill from the utility provider.  It&#8217;s largely disaggregated (mainly as air pollution &amp; greenhouse gas emissions) and often grossly unfair (e.g. loss of lives due dam failures &amp; nuclear accidents, health harms geographically localised around power stations or around rivers downstream, etc).<br><br>This is broadly true for <em>all</em> sources of electricity, &#8220;green&#8221; or not, although it is dramatically less-so for some forms of renewable energy (particularly photovoltaics and wind).</p>



<h2 class="wp-block-heading">Electrical power is mostly not an operating expense</h2>



<p>It&#8217;s often presumed that electricity is only an <em>operating</em> expense, i.e. you pay a power company per kWh and that&#8217;s it.  That&#8217;s actually a small fraction of the cost &#8211; <em>most</em> of the cost is in <em>capital</em> expenses.  Even factoring in the true cost of power, i.e. minus subsidies and including externalities, electricity operating expenses are still only a fraction of the cost.</p>



<p>Every bit of electricity you want to put into a datacentre requires infrastructure to handle it.  The more energy you want to put in, the more it costs to build that capacity (economies of scale help dampen this impact only a little).  Transmission lines, substations, transformers, power distribution throughout the building, rectifiers, backup systems (batteries, fly-wheels, emergency generators), etc.</p>



<p>And every bit of power in means heat out, which has to be removed, which takes yet more energy &amp; money.  Baffles and cabinets, fans, radiators, plumbing, heat exchangers, heat pumps (air conditioners) and evaporative coolers, etc.  It&#8217;s not quite as bad as <a href="https://newspaceeconomy.ca/2023/05/28/the-tyranny-of-the-rocket-equation-an-in-depth-examination/" data-wpel-link="external" target="_blank" rel="external noopener">the tyranny of the rocket equation</a>, but in the same vein.</p>



<p>If you look at a datacentre, physically, <em>most</em> of it is actually about power (and the flip-side, heat).  The servers themselves take up a <em>minority</em> of the space and actually cost <em>less</em> than the infrastructure that supports them.</p>



<h2 class="wp-block-heading">Power costs are dominated by provisioned power, not actual usage</h2>



<p>You pay for power infrastructure whether you use it or not.</p>



<p>If you operate at your power capacity all the time, then you at least have a technically efficient system in that respect.</p>



<p>Almost no datacentres (nor servers individually) operate that way.  Most are actually operating at <em>way</em> below full load, pretty much all the time.  And that means you&#8217;ve paid a pretty penny for your power capacity and yet you&#8217;re wasting most of it (and therefore most of your money).</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>Plus, what really matters is how much <em>useful work</em> you accomplish with that power &#8211; e.g. if you&#8217;re Bitcoin mining, all that power is wasted irrespective of its technical efficiency.</p>



<p>Pathologically wasteful workloads aside, this is made dramatically worse &#8211; even for well-meaning workloads &#8211; by non-linear efficiencies, the most horrific example being <a href="https://en.wikipedia.org/wiki/Intel_Turbo_Boost" data-wpel-link="external" target="_blank" rel="external noopener">Intel&#8217;s Turbo Boost</a> (and AMD&#8217;s equivalent).  Those techniques effectively try to ensure the CPU operates at peak power usage <em>irrespective</em> of its actual performance.  With them enabled you&#8217;re using much more of your provisioned power capacity all the time, but you&#8217;re not actually getting much more done.  It&#8217;s incredibly wasteful and worse than not using the power at all (since while electricity operating expenses are a minor component, they&#8217;re still significant).</p>



<p>There are similar non-linearity problems throughout the server (and datacentre), e.g. RAM that&#8217;s always on and consuming most of its provisioned power even if completely unused.  Though the processors (CPUs, GPUs, etc) are usually the most egregious and significant offenders.</p>
</div></div>



<p>The reasons they usually operate well below capacity are myriad, but in short the keys are:</p>



<ul class="wp-block-list">
<li>Bad software (encompassing everything from top level architecture to implementation details, and fundamental decisions like choice of programming languages).<br><br>This is (in my personal estimation) by far the dominant factor.<br><br>This is a huge subtopic in its own right, which I&#8217;ll expand upon in a future post.</li>



<li>Unused space.  Whether space not <em>yet</em> filled with servers (because installation and growth takes time, and you usually want some spare capacity at all times), or space temporarily bereft of operating servers due to equipment upgrades and the like.</li>



<li>Poor architecture amounting to (in a nutshell) insufficient over-provisioning.<br><br>Server workloads typically aren&#8217;t stable but usually are predictable &#8211; e.g. a diurnal cycle because most people are asleep at night rather than using your services, weekends differing from weekdays due to the typical western work week, etc.  So even with good software it can be impractical to achieve <em>complete</em> utilisation of hardware.  In that case, you can still save money by recognising that you don&#8217;t need every server in your datacentre to operate at peak power simultaneously &#8211; you can essentially move your power capacity around based on moment-by-moment need.<br><br>In a simplistic homogenous environment &#8211; where every server is identical &#8211; this is somewhat pointless, but most non-trivial datacentres are not like that; they have not just many generations of servers, but also different kinds of servers (web servers vs storage servers etc).  During peak traffic you might focus your power on your web servers, but when traffic ebbs you can shift that power to your batch-job servers.<br><br>Arguably you don&#8217;t need to over-provision your hardware if you have a good software architecture, e.g. if you can run any job on any machine <em>and</em> move jobs fluidly, and thus can always efficiently bin-pack your machines.  That&#8217;s practically never <em>perfectly</em> the case, although you can get usefully close &#8211; nonetheless, which approach is fundamentally the best remains a contentious topic and a subject of reasonable debate within server architecture circles.</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/the-cost-of-electrical-power-in-servers/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">8055</post-id>	</item>
		<item>
		<title>Option-dragging while defining the bounds of a screenshot</title>
		<link>https://wadetregaskis.com/option-dragging-while-defining-the-bounds-of-a-screenshot/</link>
					<comments>https://wadetregaskis.com/option-dragging-while-defining-the-bounds-of-a-screenshot/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Thu, 23 Nov 2023 02:00:21 +0000</pubDate>
				<category><![CDATA[Education]]></category>
		<category><![CDATA[Howto]]></category>
		<category><![CDATA[hidden feature]]></category>
		<category><![CDATA[screenshotting]]></category>
		<guid isPermaLink="false">https://blog.wadetregaskis.com/?p=5720</guid>

					<description><![CDATA[I've been taking screenshots on Macs for more than thirty years.  I thought I was pretty much a power user.  I've known forever that you can do not just the whole screen (⇧⌘3) but a specific target area (⇧⌘4), and of course the space trick for capturing a window (including menus!).  But what I didn't know is… <a class="read-more-link" href="https://wadetregaskis.com/option-dragging-while-defining-the-bounds-of-a-screenshot/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve been taking screenshots on Macs for more than thirty years.  I <em>thought</em> I was pretty much a power user.  I&#8217;ve known forever that you can do not just the whole screen (⇧⌘3) but a specific target area (⇧⌘4), and of course the space trick for capturing a window (including menus!).  And all that&#8217;s <a href="https://support.apple.com/en-us/102646" data-wpel-link="external" target="_blank" rel="external noopener">well-documented by Apple</a>.</p>



<p>And yet, when trying to screenshot things with a pixel-perfect even border around them, I&#8217;ve been using trial-and-error in order to get the start point right.  Like a caveman.</p>



<p>In taking a screenshot just now, I accidentally hit the option key while dragging out my target area, and discovered that it lets you change the bounds <em>in all four dimensions</em>, not just the bottom &amp; right!</p>



<figure class="wp-block-video aligncenter"><video height="448" style="aspect-ratio: 1058 / 448;" width="1058" autoplay loop src="https://wadetregaskis.com/wp-content/uploads/2023/11/Demonstration-of-option-dragging-during-screenshot-area-selection.mp4" playsinline></video></figure>



<p class="has-text-align-center head-exploding-emoji" style="font-size:100px">🤯</p>



<p>You can <em>also</em> hold down shift to constraint changes to one of the cardinal directions (horizontal or vertical).  Which of course combines neatly with holding down option.</p>



<p>These are very standard drag modifiers in graphics applications, which I&#8217;ve known about and used for decades.  Yet I had no idea they work with macOS&#8217;s built-in screenshotting system.</p>



<p>How on earth did I not know this existed?!  Was it only recently added?</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/option-dragging-while-defining-the-bounds-of-a-screenshot/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://wadetregaskis.com/wp-content/uploads/2023/11/Demonstration-of-option-dragging-during-screenshot-area-selection.mp4" length="125360" type="video/mp4" />

			<media:content url="https://wadetregaskis.com/wp-content/uploads/2023/11/Demonstration-of-option-dragging-during-screenshot-area-selection.avif" medium="image" />
<post-id xmlns="com-wordpress:feed-additions:1">5720</post-id>	</item>
		<item>
		<title>Zsh has neat shortcuts for decomposing file names &#038; paths</title>
		<link>https://wadetregaskis.com/zsh-has-neat-shortcuts-for-decomposing-file-names-paths/</link>
					<comments>https://wadetregaskis.com/zsh-has-neat-shortcuts-for-decomposing-file-names-paths/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Fri, 22 Sep 2023 03:54:29 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[basename]]></category>
		<category><![CDATA[dirname]]></category>
		<category><![CDATA[file paths]]></category>
		<category><![CDATA[Zsh]]></category>
		<guid isPermaLink="false">https://blog.wadetregaskis.com/?p=5264</guid>

					<description><![CDATA[I only just learned that the venerable basename and dirname are no longer necessary, let-alone the horrible string splitting required for teasing apart names from file extensions. Zsh has some awesomely convenient expansion modifiers which do it all! e.g. given the path &#8220;/Users/me/Documents/Work.zip&#8221;: $file:t → Work.zip $file:h → /Users/me/Documents $file:r → /Users/me/Documents/Work $file:e → zip&#8230; <a class="read-more-link" href="https://wadetregaskis.com/zsh-has-neat-shortcuts-for-decomposing-file-names-paths/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>I only just learned that the venerable <code>basename</code> and <code>dirname</code> are no longer necessary, let-alone the horrible string splitting required for teasing apart names from file extensions.  Zsh has some awesomely convenient expansion modifiers which do it all!  e.g. given the path &#8220;/Users/me/Documents/Work.zip&#8221;:</p>



<pre class="wp-block-preformatted">$file:t → Work.zip

$file:h → /Users/me/Documents

$file:r → /Users/me/Documents/Work

$file:e → zip</pre>



<p>You can also compose these arbitrarily, e.g.:</p>



<pre class="wp-block-preformatted">$file:t:r → Work</pre>



<p>A full list of expansion modifiers is in <a href="https://zsh.sourceforge.io/Doc/Release/Expansion.html#Modifiers" data-wpel-link="external" target="_blank" rel="external noopener">the Zsh Modifiers documentation</a>.</p>



<p>Kudos to <a href="https://zaiste.net/posts/zsh-get-filename-extension-path/" data-type="link" data-id="https://zaiste.net/posts/zsh-get-filename-extension-path/" data-wpel-link="external" target="_blank" rel="external noopener">Zaiste Programming</a> for clueing me into this.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/zsh-has-neat-shortcuts-for-decomposing-file-names-paths/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5264</post-id>	</item>
		<item>
		<title>Attention trumps experience</title>
		<link>https://wadetregaskis.com/attention-trumps-experience/</link>
					<comments>https://wadetregaskis.com/attention-trumps-experience/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Mon, 06 Sep 2021 19:08:53 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[Theories]]></category>
		<category><![CDATA[attention]]></category>
		<category><![CDATA[Computer Science]]></category>
		<category><![CDATA[Electronic Engineering]]></category>
		<category><![CDATA[experience]]></category>
		<category><![CDATA[fresh eyes]]></category>
		<category><![CDATA[interest]]></category>
		<category><![CDATA[new hire]]></category>
		<guid isPermaLink="false">https://blog.wadetregaskis.com/?p=4684</guid>

					<description><![CDATA[It&#8217;s always bemused me that I did better in electronic engineering than computer science. I studied them simultaneously, receiving degrees in both after five years (some classes counted towards both, thus why it wasn&#8217;t seven or eight years). I grew up playing on, dabbling with, and programming computers. From BASIC II to Hypercard to RealBasic&#8230; <a class="read-more-link" href="https://wadetregaskis.com/attention-trumps-experience/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>It&#8217;s always bemused me that I did better in electronic engineering than computer science.</p>



<p>I studied them simultaneously, receiving degrees in both after five years (some classes counted towards both, thus why it wasn&#8217;t seven or eight years).</p>



<p>I grew up playing on, dabbling with, and programming computers.  From BASIC II to Hypercard to RealBasic and onwards.  I don&#8217;t recall <em>ever</em> questioning the apparent inevitability of going to university to study computer science, and then into a permanent career in software development.  It was as if I were born to it.</p>



<p>In contrast, I chose to do electronic engineering basically on a whim, while I was choosing my university course.  I had effectively zero background in it.  It just looked interesting.</p>



<p>Though I observed the curious results &#8211; the unexpected inversion in my academic grades &#8211; it took me quite a while to learn the lesson.  I did better in EE despite my inexperience because I paid attention.  It was interesting <em>and</em> I knew I was starting from scratch there, so I worked hard at it.  I gave it more time.</p>



<p>I&#8217;ve increasingly appreciated the importance of this as I&#8217;ve gathered other life anecdotes.  Why does the new hire straight out of school often do a better job than the senior engineer?  Why do &#8220;fresh eyes&#8221; on a long troublesome area of the project suddenly find all sorts of bugs and flaws that had been overlooked?</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/attention-trumps-experience/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4684</post-id>	</item>
		<item>
		<title>ffmpeg can produce pseudo-corrupt audio when &#8216;copy&#8217;ing to an MP4 container</title>
		<link>https://wadetregaskis.com/ffmpeg-can-produce-pseudo-corrupt-audio-when-copying-to-an-mp4-container/</link>
					<comments>https://wadetregaskis.com/ffmpeg-can-produce-pseudo-corrupt-audio-when-copying-to-an-mp4-container/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Wed, 01 Aug 2018 15:56:08 +0000</pubDate>
				<category><![CDATA[Education]]></category>
		<category><![CDATA[AAC]]></category>
		<category><![CDATA[Bugs!]]></category>
		<category><![CDATA[codec]]></category>
		<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[Finder]]></category>
		<category><![CDATA[H.264]]></category>
		<category><![CDATA[Lightroom]]></category>
		<category><![CDATA[MOV]]></category>
		<category><![CDATA[MP4]]></category>
		<category><![CDATA[Quicktime]]></category>
		<category><![CDATA[Snafu]]></category>
		<category><![CDATA[trail camera]]></category>
		<category><![CDATA[VLC]]></category>
		<guid isPermaLink="false">https://blog.wadetregaskis.com/?p=4181</guid>

					<description><![CDATA[I&#8217;ve been using ffmpeg to trim clips from a trail camera, as most of the time there&#8217;s only a few seconds of anything interesting in frame out of the 30+ seconds of video it records each time, but I don&#8217;t want to re-encode them and lose video quality as a result (or balloon file sizes&#8230; <a class="read-more-link" href="https://wadetregaskis.com/ffmpeg-can-produce-pseudo-corrupt-audio-when-copying-to-an-mp4-container/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[<p>I&#8217;ve been using ffmpeg to trim clips from a trail camera, as most of the time there&#8217;s only a few seconds of anything interesting in frame out of the 30+ seconds of video it records each time, but I don&#8217;t want to re-encode them and lose video quality as a result (or balloon file sizes tremendously with a lossless video coding).  Keeping the whole 30 seconds is not just unnecessary and makes viewing the videos much more tedious, but wasteful of storage space as the encoding quality from the trail camera is very inefficient (file sizes are many times larger than they should be for the quality &#8211; clearly the H.264 encoder used in the trail camera is very cheap and very bad at its job).</p>
<p>I was originally doing something like:</p>
<pre>ffmpeg -ss 00:07 -t 00:03 -i "IMG_0164.MP4" -async 1 -c copy "IMG_0164_TRIMMED.MP4"</pre>
<p>The resulting trimmed MP4s play just fine in Quicktime, the Finder &#8211; anywhere that uses Apple&#8217;s decoding libraries (though I didn&#8217;t test iOS).</p>
<p>However, in VLC, or Lightroom, the audio is completely corrupt &#8211; just incoherent noise.  In Lightroom the video doesn&#8217;t even play correctly, because of Lightroom&#8217;s stupid habit of re-encoding the video &amp; audio into internal caches &#8211; apparently their video decoder is somehow thrown off by the audio channel issues, too.</p>
<p>After much trial and error and many dead-ends (thank you completely bogus &amp; wrong Stack Overflow threads… sigh) I eventually realised that the problem is apparently simply that Lightroom, VLC, etc get offended when you include pcm_s16le audio in an MP4.  ffmpeg itself says that&#8217;s not a valid audio codec for the MP4 container, <em>iff</em> you explicitly tell it to use that as the codec.  If you&#8217;re just copying from an existing audio / video file, however, it makes no mention at all of the concern.  Sigh.</p>
<p>So the apparent solution is simply to switch to the MOV container format instead.</p>
<pre>ffmpeg -ss 00:07 -t 00:03 -i "IMG_0164.MP4" -async 1 -c copy "IMG_0164_TRIMMED.MOV"</pre>
<p>The encoded bits remain identical, but the MOV container apparently accepts PCM audio where MP4 does not.  VLC, Lightroom, etc are now happy (and Quicktime et al remain happy).</p>
<p>(another possibility is that the &#8216;incompatibility&#8217; is related to MP4 levels or some other such junk… I didn&#8217;t try deciphering or exploring that)</p>
<p>It&#8217;s frustrating that VLC &amp; Lightroom can&#8217;t handle this when clearly it&#8217;s technically possible (witness Quicktime), and worse they don&#8217;t even properly recognise that they&#8217;re not handling it properly &#8211; they just play completely corrupt audio that&#8217;s literally painful on the ears.</p>
<p>It&#8217;s also very curious that the trail camera uses PCM audio if that&#8217;s not valid in an MP4 container.  It&#8217;s downright bizarre that VLC &amp; Lightroom can play the <em>unmodified</em> MP4s straight from the trail camera, even though they use the same purportedly invalid audio codec… somehow something ffmpeg is doing during its transmutation is making them angry.  I was unable to determine what that might be, though, through trial-and-error with ffmpeg command line options &amp; rudimentary examination of the input &amp; output files.</p>
<p>P.S.  An alternative is to bitwise-copy only the video stream (i.e. change -c copy to -c:v copy), and let VLC transcode the audio into its default AAC for the MP4 container.  That probably wouldn&#8217;t be a problem for me in my case &#8211; the audio from trail cameras is pretty crappy to begin with &#8211; but at the same time the audio tracks in these files are insignificant in size, so re-encoding them (and lossy as AAC) is pointless to saving disk space.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/ffmpeg-can-produce-pseudo-corrupt-audio-when-copying-to-an-mp4-container/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4181</post-id>	</item>
		<item>
		<title>Apple Mail crashes on launch if connection logging is enabled</title>
		<link>https://wadetregaskis.com/apple-mail-crashes-on-launch-if-connection-logging-is-enabled/</link>
					<comments>https://wadetregaskis.com/apple-mail-crashes-on-launch-if-connection-logging-is-enabled/#comments</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Fri, 04 May 2018 16:11:24 +0000</pubDate>
				<category><![CDATA[Education]]></category>
		<category><![CDATA[Apple Mail]]></category>
		<category><![CDATA[Bugs!]]></category>
		<category><![CDATA[crashtastic]]></category>
		<category><![CDATA[macOS]]></category>
		<guid isPermaLink="false">https://blog.wadetregaskis.com/?p=4112</guid>

					<description><![CDATA[This was a fun one.  Mail started crashing on launch for absolutely no apparent reason &#8211; nothing had changed to its config or similar in a long time.  The crash logs were all fingering an identical culprit &#8211; -[IMAPTaskManager secondaryIdleMailboxName] called on the wrong GCD queue: Process: Mail [19884] Path: /Applications/Mail.app/Contents/MacOS/Mail Identifier: com.apple.mail Version: 11.3&#8230; <a class="read-more-link" href="https://wadetregaskis.com/apple-mail-crashes-on-launch-if-connection-logging-is-enabled/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[<p>This was a fun one.  Mail started crashing on launch for absolutely no apparent reason &#8211; nothing had changed to its config or similar in a long time.  The crash logs were all fingering an identical culprit &#8211; <span style="font-family: Monospace;">-[IMAPTaskManager secondaryIdleMailboxName]</span> called on the wrong GCD queue:</p>
<pre>Process: Mail [19884]
Path: /Applications/Mail.app/Contents/MacOS/Mail
Identifier: com.apple.mail
Version: 11.3 (3445.6.18)
Build Info: Mail-3445006018000000~4
Code Type: X86-64 (Native)
Parent Process: ??? [1]
Responsible: Mail [19884]
User ID: …

Date/Time: 2018-04-19 16:44:45.717 -0700
OS Version: Mac OS X 10.13.4 (17E199)
Report Version: 12
Anonymous UUID: …

Sleep/Wake UUID: …

Time Awake Since Boot: 94000 seconds
Time Since Wake: 530 seconds

System Integrity Protection: enabled

Crashed Thread: 13 Dispatch queue: Task Manager Serialization Queue (QOS: UNSPECIFIED)

Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY

Application Specific Information:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This method should only be called on the serialization queue'
terminating with uncaught exception of type NSException
abort() called

Application Specific Backtrace 1:
0 CoreFoundation 0x00007fff55da96bb __exceptionPreprocess + 171
1 libobjc.A.dylib 0x00007fff7d4c1942 objc_exception_throw + 48
2 CoreFoundation 0x00007fff55daf2a2 +[NSException raise:format:arguments:] + 98
3 Foundation 0x00007fff57ee7340 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 193
4 IMAP 0x00007fff6fe88959 -[IMAPTaskManager secondaryIdleMailboxName] + 216
5 IMAP 0x00007fff6fe880cb -[IMAPTask mailboxIsUserVisibleUsingDataSource:] + 180
6 IMAP 0x00007fff6fe6ab68 -[IMAPMailboxSyncTask _nextNetworkPriorityAndOperation:reservedNetworkPriority:] + 164
7 IMAP 0x00007fff6fe6c4ba -[IMAPMailboxSyncTask recalculatePriorities] + 398
8 IMAP 0x00007fff6fe67dd9 -[IMAPMailboxSyncTask initWithDataSource:taskManager:imapMailbox:fromStatus:forceFullSync:] + 766
9 IMAP 0x00007fff6fe931f7 -[IMAPTaskManager _syncMailboxWithDataSource:withIMAPMailbox:fromStatus:forceFullSync:userInitiated:] + 370
10 IMAP 0x00007fff6fe92e68 -[IMAPTaskManager syncMailboxWithDataSource:withIMAPMailbox:fromStatus:forceFullSync:userInitiated:] + 240
11 IMAP 0x00007fff6fe9631a -[IMAPTaskManager didAddMessagesWithUnknownUID:toDataSource:] + 872
12 Foundation 0x00007fff57e4a5df __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7
13 Foundation 0x00007fff57e4a441 -[NSBlockOperation main] + 68
14 Foundation 0x00007fff57e488ee -[__NSOperationInternal _start:] + 778
15 Foundation 0x00007fff57e44917 __NSOQSchedule_f + 369
16 libdispatch.dylib 0x00007fff7e09ee08 _dispatch_client_callout + 8
17 libdispatch.dylib 0x00007fff7e0b1ed1 _dispatch_continuation_pop + 472
18 libdispatch.dylib 0x00007fff7e0a9783 _dispatch_async_redirect_invoke + 703
19 libdispatch.dylib 0x00007fff7e0a09f9 _dispatch_root_queue_drain + 515
20 libdispatch.dylib 0x00007fff7e0a07a5 _dispatch_worker_thread3 + 101
21 libsystem_pthread.dylib 0x00007fff7e3f0169 _pthread_wqthread + 1387
22 libsystem_pthread.dylib 0x00007fff7e3efbe9 start_wqthread + 13</pre>
<p>Long story short, the issue turns out to be having connection logging enabled.  That&#8217;d been turned on many months before in order to debug a different stupid Mail bug, and had been simply left on (deliberately IIRC, since Mail tends to bug-out quite often, so why not have logs already available when it comes time to debug it yet again?).</p>
<p>Connection logging is enabled or disabled by opening the &#8220;Connection Doctor&#8221; (Window menu &gt; Connection Doctor) and using the checkbox titled &#8220;Log Connection Activity&#8221;.</p>
<p>So how do you get to that checkbox when Mail crashes on launch?  Well, in this specific instance I was able to disable all mail accounts via System Preference&#8217;s Accounts pane, launch Mail, disable logging, quit Mail, re-enable all mail accounts via System Preferences, and then relaunch Mail to have it finally actually work.</p>
<p>From even just brief web searching, it&#8217;s clear that this issue has been present and well-known in Mail for a really long time.  Sigh.  Apple&#8217;s protestations that they care about software quality, or the Mac, are relentlessly undermined by their actual actions.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/apple-mail-crashes-on-launch-if-connection-logging-is-enabled/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4112</post-id>	</item>
		<item>
		<title>Things you find googling yourself</title>
		<link>https://wadetregaskis.com/things-you-find-googling-yourself/</link>
					<comments>https://wadetregaskis.com/things-you-find-googling-yourself/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Sun, 26 Feb 2017 16:21:53 +0000</pubDate>
				<category><![CDATA[Ancient History]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[Build Installer]]></category>
		<category><![CDATA[CoreGraphicsServices]]></category>
		<category><![CDATA[DePC]]></category>
		<category><![CDATA[dial-up]]></category>
		<category><![CDATA[Hotline]]></category>
		<category><![CDATA[Keychain Framework]]></category>
		<category><![CDATA[memorabilia]]></category>
		<category><![CDATA[Minecraft]]></category>
		<category><![CDATA[Mission to the ISS]]></category>
		<category><![CDATA[National Park Foundation]]></category>
		<category><![CDATA[SceneKit]]></category>
		<category><![CDATA[Sourceforge]]></category>
		<category><![CDATA[USF Maritime Law Journal]]></category>
		<guid isPermaLink="false">https://blog.wadetregaskis.com/?p=3865</guid>

					<description><![CDATA[In no particular order. The Hotline File Transfer Protocol v1.1.1.  I presume I was interested in, or actively doing, a third party Hotline client.  I did tend to make lots of data transfer clients back then (e.g. HTTP, FTP, even POP3 &#38; SMTP). My little gallery of childhood toys &#38; memorabilia.  I hadn&#8217;t forgotten about this&#8230; <a class="read-more-link" href="https://wadetregaskis.com/things-you-find-googling-yourself/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[<p>In no particular order.</p>
<ul>
<li>The <a href="https://codebox.net/assets/documents/hotline/HLFileTransferProtocol.pdf" data-wpel-link="external" target="_blank" rel="external noopener">Hotline File Transfer Protocol v1.1.1</a>.  I presume I was interested in, or actively doing, a third party <a href="https://en.wikipedia.org/wiki/Hotline_Communications" data-wpel-link="external" target="_blank" rel="external noopener">Hotline</a> client.  I did tend to make lots of data transfer clients back then (e.g. HTTP, FTP, even POP3 &amp; SMTP).</li>
<li>My <a href="https://wadetregaskis.com/MobileMe/Sites/Photos/1597.html" data-wpel-link="internal">little gallery of childhood toys &amp; memorabilia</a>.  I hadn&#8217;t forgotten about this per se, but it&#8217;d certainly been a long time since I&#8217;d look at it.  In hindsight I&#8217;m really glad I took the time to take these photos &#8211; most of this stuff is long gone now, so the photos are all the remains to pique my nostalgia.</li>
<li>Apparently I discovered <a href="https://www.cnet.com/tech/computing/fix-for-ibooktitanium-powerbook-modem-failure-under-mac-os-x-10-2-4/#!" data-wpel-link="external" target="_blank" rel="external noopener">some &#8216;fix&#8217; for Apple breaking dial-up modems in a Mac OS X Jaguar (10.2.4) update</a>.  I vaguely recall that, though I&#8217;m pretty sure that&#8217;s not the only time I&#8217;ve had to fix stupid regressions in Mac OS X by reinstalling frameworks, kexts, etc from a prior OS build.  Sigh.
<ul>
<li><a href="https://www.cnet.com/tech/computing/mac-os-x-10-2-4-special-report/" data-wpel-link="external" target="_blank" rel="external noopener">Here&#8217;s a second reference</a> to the same thing.</li>
</ul>
</li>
<li>I&#8217;m listed in the <a href="https://web.archive.org/web/20170207100106/https://www.nationalparks.org/sites/default/files/NPF_Donor_Honor_Roll_FY2015.pdf" data-wpel-link="external" target="_blank" rel="external noopener">National Park Foundation&#8217;s 2015 donor list</a>.  I&#8217;m also in the 2016 one too, though it doesn&#8217;t show up in a web search [yet], and expect to be in the 2017 one as well.
<ul>
<li>If you&#8217;re interested, <a href="https://www.nationalparks.org/other-ways-to-give" data-wpel-link="external" target="_blank" rel="external noopener">here&#8217;s the many ways you can contribute to the NPF</a> too.  Not listed prominently, but relevant for others in the tech industry, is that <a href="https://money.usnews.com/money/blogs/the-smarter-mutual-fund-investor/2013/09/26/how-to-give-stock-to-charity-2" data-wpel-link="external" target="_blank" rel="external noopener">you can donate stock without paying capital gains on it, while still receiving the tax benefits of the stock&#8217;s full, current market value</a>.  I wish I&#8217;d known this years ago when I started doing non-trivial charitable giving. 😔</li>
</ul>
</li>
<li><a href="https://web.archive.org/web/20170821035144/https://en.wikipedia.org/wiki/Steven_Troughton-Smith" data-wpel-link="external" target="_blank" rel="external noopener">Steven Troughton-Smith</a> gives a call out to me (among others) in his little <a href="https://github.com/steventroughtonsmith/scenekitgeometry" data-wpel-link="external" target="_blank" rel="external noopener">SceneKit demo program</a> and his pretty impressive &#8220;<a href="https://github.com/steventroughtonsmith/OpenWorldTest" data-wpel-link="external" target="_blank" rel="external noopener">OpenWorldTest</a>&#8221; (i.e. Minecraft tech demo clone).  I don&#8217;t recall have any specific contribution, but way back when Steven &amp; I did chat a bunch about SceneKit, and other topics.
<ul>
<li>It tickles me now, though I haven&#8217;t talked to Steven much in years, that his name appears increasingly often in pretty high profile places (e.g. <a href="https://atp.fm" data-wpel-link="external" target="_blank" rel="external noopener">the ATP podcast</a> mentions him almost every episode lately).  He&#8217;s certainly made a name for himself.</li>
<li>Someone called &#8220;<a href="https://github.com/takataka" data-wpel-link="external" target="_blank" rel="external noopener">Taka Taka</a>&#8221; has also <a href="https://github.com/takataka/OpenWorldTest" data-wpel-link="external" target="_blank" rel="external noopener">run with OpenWorldTest a bit</a>, to add VR support among other things.  Nothing to do with me any more than Steven&#8217;s original version &#8211; I mention it just because it&#8217;s cool.</li>
</ul>
</li>
<li><a href="https://lawblog.usfca.edu/lawreview/wp-content/uploads/2014/09/45-4-C3.pdf" data-wpel-link="external" target="_blank" rel="external noopener">I&#8217;m cited in the USF Maritime Law Journal</a> (alas a broken link as I write).  Yep, I&#8217;m a big mover &amp; shaker in the legal circle… which is to say, <a href="https://www.marisanelson.com" data-wpel-link="external" target="_blank" rel="external noopener">Marisa</a> had an article published there and thanked me for supporting her as she wrote it. 😉
<ul>
<li>And I get a mention on every single page on her website too, for taking <a href="https://web.archive.org/web/20170312175434/https://www.marisanelson.com/about/" data-wpel-link="external" target="_blank" rel="external noopener">one of the photos of her</a> on it.  Booyah!</li>
</ul>
</li>
<li>In reference to <a href="https://wadetregaskis.com/rotated-windows/" data-wpel-link="internal">my hacking on CoreGraphicsServices</a>, Nat! (no other name given, as far as I can see) wrote <a href="https://www.mulle-kybernetik.com/weblog/2004/apple_is_a_harsh_mistress_but.html" data-wpel-link="external" target="_blank" rel="external noopener">a journal entry</a> about how some people in the Apple community can be, well, wet towels.</li>
<li>A bajillion years ago I wrote <a href="https://sourceforge.net/projects/keychain/" data-wpel-link="external" target="_blank" rel="external noopener">the Keychain Framework</a>, a relatively clean &#8216;Cocoa&#8217; (Objective-C &amp; Foundation) framework that wrapped Apple&#8217;s C-based libraries for keychain access &amp; basic security functionality (which is in turn an implementation &amp; based on the <a href="https://web.archive.org/web/20170413002342/http://www.opengroup.org:80/security/l2-cdsa.htm" data-wpel-link="external" target="_blank" rel="external noopener">CDSA</a> &#8216;standard&#8217; that nobody but Apple appears to have ever actually implemented).  Anyway, <a href="https://www.malcolmhardie.com/sqleditor/releases/deployed/readme.html" data-wpel-link="external" target="_blank" rel="external noopener">apparently it&#8217;s used in SQLEditor</a>.  I&#8217;m pretty stoked &#8211; I sunk a spectacular amount of time into that framework, for very dubious benefit in hindsight. 😕</li>
<li>And another old pet project that shows up is <a href="https://sourceforge.net/projects/mailcash/" data-wpel-link="external" target="_blank" rel="external noopener">my Mailcash plug-in for Apple Mail</a>.  This was an implementation of <a href="http://www.hashcash.org" data-wpel-link="external" target="_blank" rel="external noopener">Hashcash</a>, which I still think is a neat idea for reducing email spam, by making senders &#8216;pay&#8217; for every email like a virtual stamp.  I have no idea if anyone ever really adopted it, nor if my Apple Mail plug-in still works at all.  Presumably not &#8211; I recall Apple Mail breaking plug-ins repeatedly over the last ten years or so, since I wrote Mailcash.
<ul>
<li>Oh how easy it is to date an open-source project when it&#8217;s still hosted on Sourceforge.  Poor, sad, lost-its-way Sourceforge.</li>
</ul>
</li>
</ul>
<p>Curiously, there&#8217;s several other open-source projects of mine on Sourceforge that <em>don&#8217;t</em> show up in a web search for my name:</p>
<ul>
<li><a href="https://sourceforge.net/projects/buildinstaller/" data-wpel-link="external" target="_blank" rel="external noopener">Build Installer</a>, a template for software installers that build the software from source on the user&#8217;s machine &#8211; assuming they&#8217;ve installed Apple&#8217;s developer tools, that is.  From memory this was inspired by the tedious and error-prone nature of various open-source projects, w.r.t. how they distributed &amp; installed their software on Mac OS X.  Sadly, nothing much has changed &#8211; there&#8217;s now <a href="https://brew.sh" data-wpel-link="external" target="_blank" rel="external noopener">brew</a> &amp; such package managers, but in my experience they make just as a big a mess as doing it manually &amp; ad-hoc did.</li>
<li><a href="https://sourceforge.net/projects/depc/" data-wpel-link="external" target="_blank" rel="external noopener">DePC</a>, a tool for stripping files of those nasty DOS-based file extensions, and instead setting the files&#8217; type &amp; creator codes correctly.  Sigh… I lost that battle.  To this day I still think that was a bad choice, that Apple made, to cave in to file extensions.  They&#8217;re still ugly and error-prone.</li>
<li><a href="https://sourceforge.net/projects/miss/" data-wpel-link="external" target="_blank" rel="external noopener">Mission to the ISS</a>, the horrible group project I did with various folks in university, for the CSE32PRO (3rd-year Computer Science Project) class.  Back in 2004, I&#8217;d guess, based on the open-source release being in January 2005.  That was a fun project in many ways, though the end result was a bit embarrassing &#8211; we ran out of time, during the class, to actually finish it properly, so for example the underlying algorithms that control the simulation are fundamentally step-based, but are stepped every time the user provides any input, so high user interaction makes the simulation run faster than intended, with silly and sometimes outright broken results.  Sad panda.</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/things-you-find-googling-yourself/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3865</post-id>	</item>
		<item>
		<title>Encrypted RAID volumes in El Capitan</title>
		<link>https://wadetregaskis.com/encrypted-raid-volumes-in-el-capitan/</link>
					<comments>https://wadetregaskis.com/encrypted-raid-volumes-in-el-capitan/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Tue, 21 Jun 2016 14:59:32 +0000</pubDate>
				<category><![CDATA[Education]]></category>
		<category><![CDATA[CoreStorage]]></category>
		<category><![CDATA[Disk Utility]]></category>
		<category><![CDATA[RAID]]></category>
		<category><![CDATA[Sad]]></category>
		<category><![CDATA[Time Machine]]></category>
		<guid isPermaLink="false">https://blog.wadetregaskis.com/?p=3649</guid>

					<description><![CDATA[Apple crippled Disk Utility in El Capitan, in their usual name of making good functional things pretty &#38; pretty useless. Luckily I&#8217;m far from the first person to need to create RAID and/or encrypted CoreStorage volumes, in El Capitan.  Florian Knapp has a concise summary of how to set up an encrypted RAID volume.  Tom&#8230; <a class="read-more-link" href="https://wadetregaskis.com/encrypted-raid-volumes-in-el-capitan/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>Apple crippled Disk Utility in El Capitan, in their usual name of making good functional things pretty &amp; pretty useless.</p>



<p>Luckily I&#8217;m far from the first person to need to create RAID and/or encrypted CoreStorage volumes, in El Capitan.  Florian Knapp has a <a href="https://web.archive.org/web/20210617051730/https://nerd.one/how-to-encrypt-appleraid-volumes-using-corestorage/" data-wpel-link="external" target="_blank" rel="external noopener">concise summary of how to set up an encrypted RAID volume</a>.  Tom Nelson (of About.com) has a slightly more detailed <a href="https://web.archive.org/web/20200806110538/https://www.lifewire.com/use-terminal-managing-raid-0-striped-array-2260098" data-wpel-link="external" target="_blank" rel="external noopener">tutorial for managing the RAID part</a>.</p>



<p>Now I just wish the hard drive industry would actually push capacities up, like they once did, so that I don&#8217;t have to resort to striped RAID sets just to make a disk big enough for Time Machine backups. &nbsp;It feels like we&#8217;ve been effectively stuck at 6 TB for many years now, and&nbsp;<em>affordable</em> 8+ TB drives aren&#8217;t really on the horizon (Seagate &amp; Western Digital have offerings, but historically have been bad brands for drive reliability, e.g. <a href="https://www.backblaze.com/blog/hard-drive-reliability-stats-q1-2016/" data-wpel-link="external" target="_blank" rel="external noopener">Backblaze&#8217;s data</a>, plus my own personal experience with their drives).</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p><strong>Update:</strong> &nbsp;macOS Sierra partially restores Disk Utility&#8217;s functionality, though not enough to be useful. &nbsp;It adds a &#8220;RAID Assistant&#8221;&nbsp;which lets you create <em>unencrypted</em>&nbsp;RAID volumes. &nbsp;The core Disk Utility app can also initiate manual repair of RAID mirrors, and delete RAID volumes.</p>



<p>It&#8217;s something of a mystery why you&nbsp;cannot create&nbsp;<em>encrypted</em> RAID volumes with the RAID Assistant. &nbsp;It doesn&#8217;t offer any encrypted file systems as initialisation options, and attempting to erase the unencrypted RAID volume in Disk Utility, to replace it with an encrypted version, fails with the bullshit error message:</p>



<pre class="wp-block-preformatted">An internal state error occurred
Operation failed…</pre>



<p>No shit.</p>



<p>Furthermore, encrypted RAID volumes&nbsp;(or more precisely, any RAID volume that&#8217;s part of a CoreStorage Logical Volume Group)&nbsp;don&#8217;t get recognised as RAID volumes in Sierra&#8217;s Disk Utility&nbsp;<em>unless</em> you connect the underlying drives <em>while</em> Disk Utility is running. &nbsp;Even then it&#8217;s hit or miss whether it&#8217;ll correctly recognise not just that it is a RAID set but also that there&#8217;s an&nbsp;encrypted CoreStorage volume on the set. &nbsp;And I&#8217;m not even going to try testing if it can actually repair a RAID mirror in that configuration.</p>



<p>To be clear, RAID volumes that&nbsp;<em>don&#8217;t</em> have CoreStorage volumes atop them seem to work fine. &nbsp;It&#8217;s evident that Apple simply don&#8217;t support encrypted RAID volumes. &nbsp;Maybe in next year&#8217;s macOS &#8211; it must be hard adding support for things&nbsp;<em>you already fucking supported until you pointlessly removed support for it.</em></p>



<p>FWIW, here&#8217;s a <a href="https://www.macworld.com/article/228398/how-to-configure-a-software-raid-in-macos-sierra-s-disk-utility.html" data-wpel-link="external" target="_blank" rel="external noopener">howto&nbsp;from Macworld on how to use the new RAID&nbsp;Assistant</a>, if encryption isn&#8217;t something you want.</p>
</div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/encrypted-raid-volumes-in-el-capitan/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3649</post-id>	</item>
		<item>
		<title>Stanford&#8217;s Game Theory online course</title>
		<link>https://wadetregaskis.com/stanfords-game-theory-online-course/</link>
					<comments>https://wadetregaskis.com/stanfords-game-theory-online-course/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Tue, 20 Mar 2012 07:00:33 +0000</pubDate>
				<category><![CDATA[Education]]></category>
		<guid isPermaLink="false">http://blog.wadetregaskis.com/?p=2378</guid>

					<description><![CDATA[In the interests of SCIENCE! I signed up for the Machine Learning course that Stanford was planning to offer online.  That apparently fell through, so I jumped ship to Game Theory instead.  Which started as of a weekish ago.  It&#8217;s an interesting idea, and I suspect that the existence of tests and problem sets will&#8230; <a class="read-more-link" href="https://wadetregaskis.com/stanfords-game-theory-online-course/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>In the interests of SCIENCE! I signed up for the <a href="https://web.archive.org/web/20120311040416/http://www.ml-class.org/course/auth/welcome" data-wpel-link="external" target="_blank" rel="external noopener">Machine Learning</a> course that Stanford was planning to offer online.  That apparently fell through, so I jumped ship to <a href="https://www.game-theory-class.org/" data-wpel-link="external" target="_blank" rel="external noopener">Game Theory</a> instead.  Which started as of a weekish ago.  It&#8217;s an interesting idea, and I suspect that the existence of tests and problem sets will aid retention (in every sense), as opposed to e.g. Khan Academy, which has always seemed like a really cool idea but nonetheless never really occupied my time.  Though I did download the new app the other day.  Haven&#8217;t launched it yet.</p>



<p>I guess my reasons for signing up include:</p>



<ul class="wp-block-list">
<li>A curiosity as to whether I can still handle a traditional academic learning environment. &nbsp;I have warm, fuzzy nostalgia for my uni days, for the most part. &nbsp;And when I was at uni I was nostalgic over high school&#8230;. ad infinitum, I suspect.</li>



<li>Determining whether I have any meaningful interest in furthering my academic credentials in future (I&#8217;ve toyed with the idea of getting a Masters or PhD from Stanford for a while; if it weren&#8217;t so damn expensive I probably have started by now&#8230; maybe this will convince me it&#8217;s worth the cost).</li>



<li>A desire for betterment, and a realisation that a formal schedule and a familiar paradigm will be more likely to stick, long-term, than haphazard or irregular approaches.</li>
</ul>



<p>At first glance I was tempted to sign up for a whole lot of the courses &#8211; basically all that looked at all interesting &#8211; but I decided against it in the end, reasoning that it wouldn&#8217;t do any real harm to just take one to begin with, and see what the workload really is, and how much I enjoy it.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/stanfords-game-theory-online-course/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2378</post-id>	</item>
	</channel>
</rss>
