<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: fluffy</title>
    <description>The latest articles on DEV Community by fluffy (@fluffy).</description>
    <link>https://dev.to/fluffy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F119947%2Ff2055327-f05e-4ecc-b4da-b7010949615c.jpg</url>
      <title>DEV Community: fluffy</title>
      <link>https://dev.to/fluffy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fluffy"/>
    <language>en</language>
    <item>
      <title>My least favorite question in all of tech recruiting</title>
      <dc:creator>fluffy</dc:creator>
      <pubDate>Sun, 16 Apr 2023 23:02:01 +0000</pubDate>
      <link>https://dev.to/fluffy/my-least-favorite-question-in-all-of-tech-recruiting-11bi</link>
      <guid>https://dev.to/fluffy/my-least-favorite-question-in-all-of-tech-recruiting-11bi</guid>
      <description>&lt;p&gt;“Are you frontend, backend, or full-stack?”&lt;/p&gt;

&lt;p&gt;I really hate this question, for so many reasons.&lt;/p&gt;

&lt;p&gt;First of all, it presupposes that there’s only two sorts of things that are done in software anymore: either you’re making websites (frontend) or services called by them (backend), or you’re someone who does both, but still using the frontend/backend dichotomy.&lt;/p&gt;

&lt;p&gt;There are so many other kinds of software out there. Not all the world is Building Websites. Just off the top of my head there’s the &lt;em&gt;extremely broad&lt;/em&gt; categories of graphics, platform, audio, gameplay, automation, embedded, infrastructure, distributed systems, and so much more.&lt;/p&gt;

&lt;p&gt;Even in today’s dystopian push towards blockchain and machine learning, what kinds of engineer works on the underlying systems there? It’s neither backend nor frontend.&lt;/p&gt;

&lt;p&gt;“Are you frontend, backend, or full-stack?”&lt;/p&gt;

&lt;p&gt;There’s a lot of assumptions baked into this question. It assumes that all engineers are making use of existing libraries and gluing them together, just to build websites. It doesn’t have any room for the people building the frameworks behind those disciplines. Would the people who implemented ReactJS or Node be considered any of those categories? What about people who build language bindings for libraries? Or game engines? Or device drivers and kernel modules? What about the people who are making the basis for how all those other things exist?&lt;/p&gt;

&lt;p&gt;Where in frontend, backend, or full-stack do you see people who are building web browsers, or home automation equipment, or devising algorithms that solve problems of scalability? Where do image processing experts go? What about people who are solving problems in computational biology or large-scale storage or information security and privacy?&lt;/p&gt;

&lt;p&gt;“Are you frontend, backend, or full-stack?”&lt;/p&gt;

&lt;p&gt;While I have done some web development throughout my career it’s never been a focus. I have no interest in building websites professionally, and I’ve only used web technology as part of application platforms where the problems I was solving were more about how to make applications easy to develop for a wide variety of software platforms, &lt;em&gt;including&lt;/em&gt; (but hardly limited to) the web. When I do build web stuff I’m doing it largely in plain, hand-written HTML and CSS, with as little Javascript as I can get away with, and as much as possible rendered server-side, without having a fleet of microservices exchanging data around so that I need a billion servers just to render a single page of HTML.&lt;/p&gt;

&lt;p&gt;I’ve built frameworks for media applications that run directly on devices; while some of them used Javascript as their language, none of them were really web-based. I’ve built game engines for small, low-powered systems. I’ve built augmented and virtual reality experiences for PCs and mobile devices. I’ve worked on data warehouses, I’ve solved problems of computational complexity, I’ve implemented physics for interactive experiences.&lt;/p&gt;

&lt;p&gt;Even when I worked at web-oriented companies I wasn’t building stuff “for the web.” At Amazon I worked on the original Kindle, converting scanned books into display-agnostic eBooks. I worked on the caching team, increasing the performance and decreasing the operational costs of a fleet of tens of thousands of servers. I worked on the image service team, managing the catalog image metadata and writing the image scaler and image processing routines that power major portions of the website. Is any of that frontend? Backend? Full-stack?&lt;/p&gt;

&lt;p&gt;"Are you frontend, backend, or full-stack?"&lt;/p&gt;

&lt;p&gt;Oh, you're a chef? Do you make pizza, salads, or both?&lt;/p&gt;

&lt;p&gt;Oh, you're a mechanic? Do you work on tanks, motorcycles, or both?&lt;/p&gt;

&lt;p&gt;Oh, you're an architect? Do you design dog houses, malls, or both?&lt;/p&gt;

&lt;p&gt;“Are you frontend, backend, or full-stack?”&lt;/p&gt;

&lt;p&gt;The only answer I can truthfully give: No.&lt;/p&gt;

</description>
      <category>recruiting</category>
      <category>career</category>
    </item>
    <item>
      <title>Code: Radix sort revisited</title>
      <dc:creator>fluffy</dc:creator>
      <pubDate>Sun, 14 Mar 2021 20:40:03 +0000</pubDate>
      <link>https://dev.to/fluffy/code-radix-sort-revisited-2i8p</link>
      <guid>https://dev.to/fluffy/code-radix-sort-revisited-2i8p</guid>
      <description>&lt;p&gt;Around two years ago I wrote an article on &lt;a href="https://dev.to/fluffy/code-the-danger-of-big-o-notation-4j3g"&gt;the perils of relying on big-O notation&lt;/a&gt;, and in it I focused on a comparison between comparison-based sorting (via &lt;code&gt;std::sort&lt;/code&gt;) and &lt;a href="https://en.wikipedia.org/wiki/Radix_sort"&gt;radix sort&lt;/a&gt;, based on the common bucketing approach.&lt;/p&gt;

&lt;p&gt;Recently I came across a &lt;a href="https://youtu.be/ujb2CIWE8zY"&gt;video on radix sort&lt;/a&gt; which presents an alternate counting-based implementation at the end, and claims that the tradeoff point between radix and comparison sort comes much sooner. My intuition said that even counting-based radix sort would still be slower than a comparison sort for any meaningful input size, but it’s always good to test one’s intuitions.&lt;/p&gt;

&lt;p&gt;So, hey, it turns out I was wrong about something. (But my greater point still stands.)&lt;/p&gt;

&lt;p&gt;Here are my implementations of bucket-based and count-based radix sort, in C++:&lt;/p&gt;
 big-o-2.cpp (excerpt)





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;radix_bucket_sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;value_type&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;value_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Bucket&lt;/span&gt; &lt;span class="n"&gt;radixes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;radixes&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;radixes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;radix_count_sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;value_type&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;value_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;TestCase&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;accum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;accum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;accum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;iter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rbegin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;iter&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rend&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;iter&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the full source, see &lt;a href="https://beesbuzz.biz/_file/11134/big-o-2.cpp"&gt;&lt;code&gt;big-o-2.cpp&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here are the time comparisons between &lt;code&gt;std::sort&lt;/code&gt;, and both bucket and counting radix sort using a 4- and 8-bit radix: (&lt;a href="https://beesbuzz.biz/code/1159-Radix-sort-revisited"&gt;raw data&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://beesbuzz.biz/code/1159-Radix-sort-revisited"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dBifXaBj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://beesbuzz.biz/static/_img/96/fce3/big-o-2-5e4_52d5c28863_640x360.png" alt="big-o-2-5e4.png" title="Five sorts up to 5e4" width="640" height="360"&gt;&lt;/a&gt;&lt;a href="https://beesbuzz.biz/code/1159-Radix-sort-revisited"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w0RKoceI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://beesbuzz.biz/static/_img/d8/a002/big-o-2-1e5_37cbb3dc87_640x360.png" alt="big-o-2-1e5.png" title="Up to 1e5" width="640" height="360"&gt;&lt;/a&gt;&lt;a href="https://beesbuzz.biz/code/1159-Radix-sort-revisited"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xe7hjTPc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://beesbuzz.biz/static/_img/a4/05af/big-o-2-1e6_d7a71f0713_640x360.png" alt="big-o-2-1e6.png" title="Up to 1e6" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, what’s going on here? And why are even the bucket-radix sort graphs different than last time?&lt;/p&gt;

&lt;p&gt;It’s hard to do a like-for-like comparison with the previous set of implementations; this time around was running on a very different computer (a Mac mini running on the M1 chip), a newer version of the C++ compiler, a newer version of the C++ standard library, and who knows how many other differences. It’s pretty interesting that the power-of-two allocation overhead from bucketed radix, in particular, has more or less gone away; there’s possibly something about the M1 architecture which makes the vector resize take much less time, and also making use of clang’s robust C++17 support may have also reduced some of the copy overhead due to implicit move semantics being used.&lt;/p&gt;

&lt;p&gt;But it’s pretty interesting to me that the following things are pretty apparent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 4-bit radix+bucket sort breaks even with &lt;code&gt;std::sort&lt;/code&gt; at around N=13000&lt;/li&gt;
&lt;li&gt;An 8-bit radix+bucket sort breaks even at around N=44000&lt;/li&gt;
&lt;li&gt;Both 4- and 8-bit radix+count sort break even pretty much immediately (around N=600 and N=100, respectively)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, all that said, this still demonstrates a problem with just assuming that a lower big-O factor is better. All four of those radix sort implementations are (\mathcal{O}(N)), but the bucket-based ones still are slower than the (\mathcal{O}(N lg N)) &lt;code&gt;std::sort&lt;/code&gt; until fairly large input sizes, even with all of the overall performance improvements which have happened (after all, &lt;code&gt;std::sort&lt;/code&gt; has gotten faster too).&lt;/p&gt;

&lt;p&gt;And, of course, all four radix sorts have the same time complexity as each other, but they all scale with different factors; in particular, it doesn’t take that long for the radix+bucket sorts to overtake the 4-bit counting sort (which is, frankly, pretty surprising to me).&lt;/p&gt;

&lt;p&gt;As always, the fact that an algorithm scales better in the long term doesn’t mean it’s suitable for all input sizes. Even in this best-case situation, &lt;code&gt;std::sort&lt;/code&gt; still wins for input sizes of a few hundred values, and of course the maintenance overhead of using &lt;code&gt;std::sort&lt;/code&gt; is &lt;em&gt;way&lt;/em&gt; lower.&lt;/p&gt;

&lt;p&gt;It’s also important to remember that these sorting functions can still only work on unsigned integers, or signed integers with a little tweaking. They are not applicable to floating-point values, much less things with more complicated tiebreaking rules (such as database rows).&lt;/p&gt;

&lt;p&gt;And, heck, it’s also really easy to write code which is (\mathcal{O}(N)) but not optimal! As we saw with the previous article.&lt;/p&gt;

&lt;p&gt;So, my conclusion is still the same: practical concerns trump theoretical complexity. But as a bonus conclusion, it’s okay to revisit things to see if they’ve changed.&lt;/p&gt;

&lt;p&gt;Oh, and you might also want to consider that just because your parts of the algorithm have a certain complexity factor doesn’t mean that’s what the runtime performance will be; it’s easy to make something &lt;a href="https://accidentallyquadratic.tumblr.com/"&gt;accidentally quadratic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://beesbuzz.biz/code/1159-Radix-sort-revisited#comments"&gt;comments&lt;/a&gt;&lt;/p&gt;

</description>
      <category>code</category>
      <category>complexity</category>
      <category>performance</category>
    </item>
    <item>
      <title>Advice to young web developers</title>
      <dc:creator>fluffy</dc:creator>
      <pubDate>Mon, 15 Jun 2020 16:33:58 +0000</pubDate>
      <link>https://dev.to/fluffy/plaidophile-advice-to-young-web-developers-325m</link>
      <guid>https://dev.to/fluffy/plaidophile-advice-to-young-web-developers-325m</guid>
      <description>&lt;p&gt;I’ve been making websites in some form or another since 1995. After 25 years of experience I think I’ve accumulated enough knowledge to know a few things. Here’s some things I’d like younger developers to think about, in no particular order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sometimes a website is just a website.&lt;/li&gt;
&lt;li&gt;The browser is already a client; HTML is its language.&lt;/li&gt;
&lt;li&gt;The web is built around server-side rendering.&lt;/li&gt;
&lt;li&gt;You can provide your data in more than one way; consider HTML to be one of several possible data representations.&lt;/li&gt;
&lt;li&gt;Scaling your server helps everyone. Expecting client-side scaling only helps people with the fastest computers and Internet connections.&lt;/li&gt;
&lt;li&gt;Not everyone has (or can use) a mouse.&lt;/li&gt;
&lt;li&gt;Not everyone has (or can use) a keyboard.&lt;/li&gt;
&lt;li&gt;Not everyone has (or can use) a touchscreen.&lt;/li&gt;
&lt;li&gt;Not everyone can see colors or pictures the same way you can.&lt;/li&gt;
&lt;li&gt;Not everyone can process information the same way you do.&lt;/li&gt;
&lt;li&gt;It is inhumane to move things around on people.&lt;/li&gt;
&lt;li&gt;The browser’s native HTML parsing is far faster than anything you can write in JavaScript.&lt;/li&gt;
&lt;li&gt;HTML is already an ideal representation of DOM nodes.&lt;/li&gt;
&lt;li&gt;HTML &lt;em&gt;is&lt;/em&gt; a rich framework.&lt;/li&gt;
&lt;li&gt;You can probably do that layout change in CSS.&lt;/li&gt;
&lt;li&gt;Before you roll your own UI component, consider that HTML probably provides it. If it doesn’t provide it, that’s probably for a reason. Attaching DOM events to a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; is probably not the best way of doing things.&lt;/li&gt;
&lt;li&gt;Not everything has to be a “single-page application.”&lt;/li&gt;
&lt;li&gt;Even if you need to preserve client state between page loads (for e.g. music or video playback) you can let the browser do most of the heavy lifting by &lt;code&gt;fetch()&lt;/code&gt;ing a new page and replacing your content container at the DOM level.&lt;/li&gt;
&lt;li&gt;Infinite scrolls are inhumane. People need to be able to reach “the end.” There are forms of eternal torment described in religious texts that are less mean.&lt;/li&gt;
&lt;li&gt;If you &lt;em&gt;must&lt;/em&gt; do an infinite scroll (and you don’t), make sure that there’s nothing you need to reach at the bottom.&lt;/li&gt;
&lt;li&gt;Give people consistent but random stimulus and you will be habit-forming. Getting people hooked on your product might seem like a good idea, but the tobacco industry feels the same way.&lt;/li&gt;
&lt;li&gt;If you design with CDNs in mind, then a server round-trip won’t be slow.&lt;/li&gt;
&lt;li&gt;It is okay to use multiple languages in a thing. Not everything has to be isomorphic.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Always&lt;/em&gt; validate your data server-side; anything that comes from the client is suspect.&lt;/li&gt;
&lt;li&gt;To the developer, “isomorphic” code breaks down the barrier between client and server. To a malicious client, it means they have control over the server too.&lt;/li&gt;
&lt;li&gt;Browsers change. Relying on browser-specific behavior means you’re relying on that one browser at that one point in time. Code to the standard, and test everywhere.&lt;/li&gt;
&lt;li&gt;Use polyfills to support browsers that don’t yet support the standard you’re using.&lt;/li&gt;
&lt;li&gt;It’s okay to copy others; it’s how we learn things. Just remember to learn from it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://beesbuzz.biz/blog/2934-Advice-to-young-web-developers#comments"&gt;comments&lt;/a&gt;&lt;/p&gt;

</description>
      <category>web</category>
      <category>html</category>
      <category>curmudgeon</category>
      <category>a11y</category>
    </item>
    <item>
      <title>A peculiar argument regarding accessibility</title>
      <dc:creator>fluffy</dc:creator>
      <pubDate>Thu, 11 Jun 2020 00:51:04 +0000</pubDate>
      <link>https://dev.to/fluffy/a-peculiar-argument-regarding-accessibility-3i3m</link>
      <guid>https://dev.to/fluffy/a-peculiar-argument-regarding-accessibility-3i3m</guid>
      <description>&lt;p&gt;I was reading the article &lt;a href="https://www.rallyhealth.com/advocating-for-a-compassionate-ui"&gt;Advocating for a Compassionate UI&lt;/a&gt; from Rally Health, a tech company who runs a benefits portal for my insurance company. I was reading it specifically because I’ve had various accessibility issues with their website and I wanted to see what their thoughts were regarding accessibility.&lt;/p&gt;

&lt;p&gt;I found some of their arguments to be &lt;em&gt;interesting&lt;/em&gt; but not compelling. For example, they talk about how accessibility needs are more prevalent than browser marketshare:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It might surprise you to see that many types of disabilities are more frequent than browsers typically given first-class support from developers and product owners. There’s no sense in supporting IE 11 if you haven’t already given full support to the colorblind and those with visual impairments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One problem with this line of thinking is that many reasons why people prefer to use alternate browsers is because they have extension ecosystems which allow them to adapt the web to their accessibility needs. For example, I vastly prefer to use &lt;a href="https://mozilla.org/"&gt;Firefox&lt;/a&gt;, largely because it has extensions which allow me to address some of my motor and attention dysfunctions (I run an ad blocker for a reason and it’s not to screw over publishers!), and many of the very features they talk about are things that browsers-which-aren’t-Chrome have support for, such as being able to override color schemes and input methods.&lt;/p&gt;

&lt;p&gt;Unfortunately, because Firefox doesn’t enjoy first-class support anymore, many, many sites break on it now, and those sites claim to be “accessible” because they support a handful of Chrome-specific accessibility features (which still don’t actually work well with adaptive technologies and the like). Even if more people knew about the accessibility features and add-ons that Firefox offers, they wouldn’t be able to use it as their full-time browser specifically because so many sites break on it.&lt;/p&gt;

&lt;p&gt;The web itself is a platform; targeting a specific browser is shortsighted and terrible, and leads to a vicious chicken-and-egg cycle. Supporting marginal browsers is a really good proxy for supporting accessibility, as well. One of the things I do when I design a new site is to make sure that it’s usable from text-only browsers like &lt;a href="http://lynx.browser.org/"&gt;lynx&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/W3m"&gt;w3m&lt;/a&gt;; I figure that if it works there, there’s a pretty good chance it’ll work on anything non-Firefox. (I also test on Safari, since that covers the parts of the WebKit universe I care about. Typically if something works on Safari it’ll work on Chrome, but the opposite is not true.)&lt;/p&gt;

&lt;p&gt;There’s a few other things that are troublesome about that Rally article; for example, it talks a lot about visual accessibility, but uses medium-gray text and light-orange links (with no underlines!) on a white background.&lt;/p&gt;

&lt;p&gt;Rally’s site itself also makes use of a bunch of things that fly in the face of this advice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Designs, implicitly or explicitly, often require the use of custom elements such as dropdowns to allow for greater control over the appearance of the UI to match the desired brand guidelines. It’s often possible to leverage CSS to reskin native browser controls to match the required designs, but when that’s impossible, you may need to create a custom component.&lt;/p&gt;

&lt;p&gt;Push back against these custom components as strongly as possible in favor of their native browser counterparts. The native implementations already support all the required accessibility features, and replicating that in a custom component will not be a trivial task. For reference, see &lt;a href="https://www.w3.org/TR/wai-aria-practices/examples/listbox/listbox-collapsible.html"&gt;the keyboard interactions that a collapsible dropdown needs to support&lt;/a&gt; to achieve AAA compliance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When first signing in from a new browser, it prompts you for a one-time code. This one-time code entry is done using a custom control, that is not very respectful of the fact that people make typos.&lt;/p&gt;

&lt;p&gt;Many elements within their frontend framework are based on dynamically-created, &lt;a href="https://beesbuzz.biz/blog/7456-Modern-web-design-antipatterns"&gt;&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;-based components&lt;/a&gt; which do not respect tab cycling or browser-native autocomplete, and which move around as pages fill in, which messes both with my focus issues (hello, ADHD, listed as the top single “market share” on that chart!) and with my motor issues (I &lt;em&gt;really&lt;/em&gt; hate having to use the mouse for things like that).&lt;/p&gt;

&lt;p&gt;To be fair, this isn’t limited to Rally Health’s site. It seems like every single healthcare-related website is like this lately; &lt;a href="https://pridestudy.org/"&gt;PRIDE Study&lt;/a&gt; is particularly egregious about these issues as well, where their web forms are very much Designed For Mobile™ and involve a lot of scrolling (often without it being clear where one is supposed to scroll &lt;em&gt;to&lt;/em&gt;) and commits many other accessibility sins, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Making checkboxes and radio buttons look the same&lt;/li&gt;
&lt;li&gt;Not having labels act as click targets for their respective controls&lt;/li&gt;
&lt;li&gt;Not supporting tabkey selection&lt;/li&gt;
&lt;li&gt;Forms done as dynamically-animating, changing “single page application” stuff when it isn’t necessary or desirable&lt;/li&gt;
&lt;li&gt;Having entire sections of forms appear or disappear dynamically based on the selections you’re making (without any indication that they’re going to have such side effects)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The web has an input problem. Please, people, stop reinventing the wheel the long, and wrong, way around.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>standards</category>
      <category>web</category>
    </item>
    <item>
      <title>The danger of big-O notation</title>
      <dc:creator>fluffy</dc:creator>
      <pubDate>Thu, 03 Oct 2019 21:42:12 +0000</pubDate>
      <link>https://dev.to/fluffy/code-the-danger-of-big-o-notation-4j3g</link>
      <guid>https://dev.to/fluffy/code-the-danger-of-big-o-notation-4j3g</guid>
      <description>&lt;p&gt;A common pitfall I see programmers run into is putting way too much stock into &lt;a href="https://en.wikipedia.org/wiki/Big_O_notation" rel="noopener noreferrer"&gt;Big O notation&lt;/a&gt; and using it as a rough analog for overall performance. It’s important to understand what the Big O represents, and what it doesn’t, before deciding to optimize an algorithm based purely on the runtime complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Big O?
&lt;/h2&gt;

&lt;p&gt;Big O notation is a commonly-used notation in the field of complexity. Roughly, saying that an algorithm is &lt;code&gt;O(f(N))&lt;/code&gt; means that for an input of size &lt;code&gt;N&lt;/code&gt;, the overall time taken will grow at a rate that is at worst &lt;code&gt;f(N)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Or, in other words, if you zoom out on the graph of time-taken by an algorithm, you can overlay a curve of &lt;code&gt;f(N)&lt;/code&gt; that is scaled in some way such that the time taken by the algorithm will always be underneath that curve.&lt;/p&gt;

&lt;p&gt;The way I learned it (which is slightly different than the formulation given on Wikipedia) is that &lt;code&gt;f(x)&lt;/code&gt; is &lt;code&gt;O(g(x))&lt;/code&gt; if there are some values &lt;code&gt;k&lt;/code&gt; and &lt;code&gt;C&lt;/code&gt; such that &lt;code&gt;f(x) ≤ k∙g(x)&lt;/code&gt; for all &lt;code&gt;x ≥ C&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does being “big-O something” tell you?
&lt;/h2&gt;

&lt;p&gt;Not that much, in practical terms.&lt;/p&gt;

&lt;p&gt;If a function is &lt;code&gt;O(N)&lt;/code&gt;, then it means that for a sufficiently-large input of size &lt;code&gt;N&lt;/code&gt;, if you double the input then it will take roughly &lt;code&gt;2N&lt;/code&gt; as long to execute.&lt;/p&gt;

&lt;p&gt;Similarly, if it’s &lt;code&gt;O(N²)&lt;/code&gt;, then for a sufficiently-large input of size &lt;code&gt;N&lt;/code&gt;, if you double the input then it will take roughly &lt;code&gt;4N&lt;/code&gt; as long to execute.&lt;/p&gt;

&lt;p&gt;And so on.&lt;/p&gt;

&lt;p&gt;It allows you to sort various algorithms based on their overall complexity class; whatever term is most-dominating in an algorithm is what will eventually dominate in terms of runtime, &lt;em&gt;for a sufficiently-large input&lt;/em&gt;. If your algorithm has two phases, one that’s &lt;code&gt;O(N)&lt;/code&gt; and one that’s &lt;code&gt;O(N²)&lt;/code&gt;, then over the long term, for a sufficiently-large input, the &lt;code&gt;O(N²)&lt;/code&gt; portion will dominate in terms of runtime, and so the algorithm as a whole is &lt;code&gt;O(N²)&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  So if I have two algorithms, one &lt;code&gt;O(N)&lt;/code&gt; and one &lt;code&gt;O(N²)&lt;/code&gt;, the &lt;code&gt;O(N)&lt;/code&gt; one will be faster, right?
&lt;/h2&gt;

&lt;p&gt;Oh &lt;em&gt;heck no&lt;/em&gt;. Remember, these are just expressions of the &lt;em&gt;dominant factor&lt;/em&gt; over &lt;em&gt;long-term increases&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Bubble sort is &lt;code&gt;O(N²)&lt;/code&gt; so we should never use it, right?&lt;/p&gt;

&lt;p&gt;Well, not necessarily. If you’re only ever sorting small arrays – like on the order of 10 elements or so – it can quite likely be a lot faster than something like merge sort or quicksort. (Incidentally, quicksort is also &lt;code&gt;O(N²)&lt;/code&gt; by a strict definition; the &lt;em&gt;average case&lt;/em&gt; tends towards &lt;code&gt;O(N lg N)&lt;/code&gt; but that’s assuming pivot selections that cut your sets in half every time. And doing a perfect pivot selection turns out to be &lt;em&gt;really hard&lt;/em&gt;.)&lt;/p&gt;

&lt;p&gt;Remember, &lt;code&gt;O&lt;/code&gt; only describes the &lt;em&gt;long-term growth&lt;/em&gt; of an algorithm. For an extreme example, trying to find a string that generates a specific fixed-size hash (colloquially called “reversing a hash” but that’s also a terrible misnomer) is &lt;code&gt;O(1)&lt;/code&gt; – after all, the search space is always a constant size. There will only ever be 2¹²⁸ possible md5 hashes, so all you have to do to find a string that produces a particular md5sum (given no other inputs) is iterate over every 128-bit number represented as strings, right?&lt;/p&gt;

&lt;p&gt;Well, that’ll take quite a long time to do. But it’ll always take on average the same amount of time! (Technically, a random amount which takes on average the same amount of time as generating 2¹²⁷ hashes.)&lt;/p&gt;

&lt;p&gt;For a more practical example: general-purpose comparison-based sorting is provably only ever going to be, at best, &lt;code&gt;O(N lg N)&lt;/code&gt;. But there are actually sorting algorithms that execute with lower complexity; for example, &lt;a href="https://en.wikipedia.org/wiki/Radix_sort" rel="noopener noreferrer"&gt;radix sort&lt;/a&gt; can be implemented to take only &lt;code&gt;O(N)&lt;/code&gt; time, at least given a fixed maximum key size. So, why don’t we always use radix sort when possible? Because in the general case, it’s &lt;em&gt;hecking slow&lt;/em&gt;. It just happens to appear to scale very nicely.&lt;/p&gt;

&lt;p&gt;For example, here’s a graph of &lt;code&gt;std::sort&lt;/code&gt; vs. a radix sort using both a 4-bit and an 8-bit radix, for various input sizes; consider that regardless of radix size, radix sort is &lt;code&gt;O(N)&lt;/code&gt; while &lt;code&gt;std::sort&lt;/code&gt; is &lt;code&gt;O(N lg N)&lt;/code&gt;: (&lt;a href="http://beesbuzz.biz/_file/f84fb/big-o.cpp" rel="noopener noreferrer"&gt;source code&lt;/a&gt;, &lt;a href="https://beesbuzz.biz/_file/58b30/big-o.dat" rel="noopener noreferrer"&gt;raw data&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fp98eppjz8pbwgbevi02d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fp98eppjz8pbwgbevi02d.png" alt="Graph of std::sort vs. radix sort, N=100000"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flrjc1ky5ey69wrk6bobq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flrjc1ky5ey69wrk6bobq.png" alt="Graph of std::sort vs. radix sort, N=524288"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fx885pu1jk0vvoy50e1te.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fx885pu1jk0vvoy50e1te.png" alt="Graph of std::sort vs. radix sort, N=1000000"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcud8bpcnxe79kcgk6fhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcud8bpcnxe79kcgk6fhb.png" alt="Graph of std::sort vs. radix sort, N=1000000, zoomed vertically"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, at least with this simple test, it takes an input size of around 40,000 before a 4-bit radix beats &lt;code&gt;std::sort&lt;/code&gt;, and around 200,000 before 8-bit radix beats &lt;code&gt;std::sort&lt;/code&gt; – even though both radix sorts are &lt;code&gt;O(N)&lt;/code&gt; compared to the &lt;code&gt;O(N lg N)&lt;/code&gt; of &lt;code&gt;std::sort&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And it only gets worse since radix sort has various time jumps up around certain power-of-two boundaries; this is because as the buckets get bigger, more allocations (and reallocations) need to occur as they hit power-of-two boundary sizes, as well as the various cache thrashing that occurs as a result. This also reflects the fact that radix sort also requires a lot more memory than a comparison sort; radix sort can take up to two times the size of the input array in addition to the input itself, whereas the comparison sorts generally do as much in-place as possible.&lt;/p&gt;

&lt;p&gt;The bucket-size overhead of 4-bit radix also makes it scale up much more quickly than 8-bit, and after a certain point it’s not even worth looking at the 4-bit radix time since it’s so much slower. But at least it scales linearly! Maybe after a few billion numbers it’ll start to beat &lt;code&gt;std::sort&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;And yes, &lt;code&gt;std::sort&lt;/code&gt; does scale with &lt;code&gt;N lg N&lt;/code&gt;, and radix does scale linearly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmpa0128xd782hnfm7bor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmpa0128xd782hnfm7bor.png" alt="std::sort vs. N lg N"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxewutzwdt8avdg8m1m6y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxewutzwdt8avdg8m1m6y.png" alt="Radix sort vs. N"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Other code metrics
&lt;/h2&gt;

&lt;p&gt;Another thing to note is that the lower-big O algorithms tend to also be much more complicated to implement and maintain. &lt;code&gt;std::sort&lt;/code&gt; is literally one line of code; my radix sort implementation is 15 lines &lt;em&gt;just&lt;/em&gt; for sorting numbers, without comments, and using an algorithm that doesn’t make intuitive sense to most people.&lt;/p&gt;

&lt;p&gt;A general rule of thumb is that the more lines of code you’re implementing yourself, the slower it’s likely to be in the common case, and the worse it is to maintain. Standard library implementations and idioms exist for a reason. Sure, it isn’t always the case that shorter code will be faster (after all, bubble sort is easily done in around 5 lines of code but is way worse than radix sort, performance-wise) but why implement something that at best only slightly improves performance while taking on extra maintenance burden?&lt;/p&gt;

&lt;p&gt;This gets even more extreme in scripting languages like Python; an idiomatic sort of a list in Python is just &lt;code&gt;sorted(input)&lt;/code&gt; and goes through mostly-native code, which has been optimized to bits by compilers or even going through standard library functions. Implementing your own sort, no matter how efficient, is going to still go entirely through the Python interpreter.&lt;/p&gt;

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

&lt;p&gt;I hope that rather than worrying &lt;em&gt;only&lt;/em&gt; about the theoretical computational complexity of an algorithm, you’ll consider using it as a guideline for implementation and pay more attention to the practical performance.&lt;/p&gt;

</description>
      <category>performance</category>
    </item>
    <item>
      <title>Where do I go from here?</title>
      <dc:creator>fluffy</dc:creator>
      <pubDate>Sun, 02 Jun 2019 01:21:23 +0000</pubDate>
      <link>https://dev.to/fluffy/where-do-i-go-from-here-215i</link>
      <guid>https://dev.to/fluffy/where-do-i-go-from-here-215i</guid>
      <description>&lt;p&gt;So, I’m finding that I’m not particularly happy at my current job, but my various constraints make it very hard for me to find something else that would suit me better.&lt;/p&gt;

&lt;p&gt;In particular, I have fibromyalgia which makes it difficult for me to do a huge amount of typing and, even moreso, limits what I can do in terms of oncall or crunch-type work. I’m also at a point in my career where I’m no longer enthusiastic about relearning everything under the sun for the flavor-of-the-week UI framework or whatever, and the chronic-pain brain fog makes it hard for me to juggle a bunch of external dependencies or manage other people.&lt;/p&gt;

&lt;p&gt;I love programming and problem-solving from a “How do I do this difficult thing?” standpoint but the day-to-day of actually Getting Stuff Done is getting harder and harder for me to do. What are my options?&lt;/p&gt;

&lt;p&gt;I definitely don’t have the right brain skills to be a manager, for example. And while architecting a new system from the ground up is absolutely in my wheelhouse, that's not really something that's a career path so much as the initiation of a new project — something you do when you're already established at a place.&lt;/p&gt;

&lt;p&gt;I already tried the "do my own thing, try to make a career out of it" thing, for two years. While I got a lot of stuff done that I care about (particularly my &lt;a href="http://publ.beesbuzz.biz/"&gt;web publishing pet project&lt;/a&gt; and &lt;a href="https://fluffy.itch.io"&gt;a bunch of games&lt;/a&gt; and &lt;a href="https://sockpuppet.bandcamp.com"&gt;music&lt;/a&gt;, I never managed to turn any of it into a sustainable source of income. I have a decent amount of savings, but not enough to last me the rest of my life by any means.&lt;/p&gt;

&lt;p&gt;I'm at a loss for what to do next. I'm in my early 40s, and I'm tired — &lt;em&gt;exhausted&lt;/em&gt;, even — and burned out and disabled. I have so many things of my own that I want to make but I don't have the energy to do any of them, and I don't have the wherewithal to make them things that other people do either. I've worked hard all my life — harder than I should have been capable of, if anything — but now I feel completely spent and I don't know what to do.&lt;/p&gt;

&lt;p&gt;There are many things I'd &lt;em&gt;love&lt;/em&gt; to do and which I think I'd be good at, were it not for the issues my disability brings, but anything that puts me on a rigid schedule that can't accommodate how my body is doing from a day-to-day basis is an absolute no-go.&lt;/p&gt;

&lt;p&gt;So, given that, does anyone have any ideas for what I possibly &lt;em&gt;can&lt;/em&gt; do?&lt;/p&gt;

&lt;p&gt;(As a note, please spare me any medical or ergonomics advice; I'm already working with doctors and trying to address the fibro issues as best I can. There is no one perfect cure for this poorly-understood condition, and if you haven't lived with this condition you &lt;em&gt;definitely&lt;/em&gt; don't understand its implications on what I can or cannot do.)&lt;/p&gt;

</description>
      <category>career</category>
      <category>life</category>
      <category>disability</category>
    </item>
    <item>
      <title>Memories</title>
      <dc:creator>fluffy</dc:creator>
      <pubDate>Wed, 10 Apr 2019 19:57:49 +0000</pubDate>
      <link>https://dev.to/fluffy/memories-412o</link>
      <guid>https://dev.to/fluffy/memories-412o</guid>
      <description>&lt;p&gt;Much has been written about how Electron apps take a lot of memory; after all, each one is running its own instance of a web browser, and pulling in all of the overwhelming amounts of support code that implies. Slack can easily end up taking over 1GB of RAM, and Discord usually takes a few hundred as well. As someone who used to use IRC back in the 90s, when a single task taking even 1 MB of RAM was considered &lt;em&gt;a lot&lt;/em&gt;, this feels rather horrifying:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--On72yVyn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/6g4pzetmwo3dmu8rfjlg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--On72yVyn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/6g4pzetmwo3dmu8rfjlg.png" alt="Slack taking about 1.2GB" width="800" height="226"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aX4j3fYn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/8j0uygw18xslr1xdejqb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aX4j3fYn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/8j0uygw18xslr1xdejqb.png" alt="Discord taking about 400MB" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On my iMac, with 24GB of RAM, that means that chat apps — doing the equivalent of an IRC client (granted, with a bit more visual stuff, but not &lt;em&gt;that&lt;/em&gt; much) — were taking about 6% of my RAM!&lt;/p&gt;

&lt;p&gt;But come to think of it, back in the mid 90s, when a typical computer had 8MB, an IRC client probably took around 400KB of RAM, which is also 6%. So have things really grown proportionally in that way?&lt;/p&gt;

&lt;p&gt;Well, I've figured out a way of getting these chat apps to take half as much of my total RAM overall, but first, let's talk about my personal history of memory usage.&lt;/p&gt;




&lt;p&gt;In 1983, my family got our first computer. It was a &lt;a href="https://en.wikipedia.org/wiki/Commodore_64"&gt;Commodore 64&lt;/a&gt;, an 8-bit microcomputer with 64KB of RAM (with no separate video memory) and while we started out with the Datasette tape drive, we soon upgraded to the &lt;a href="https://en.wikipedia.org/wiki/Commodore_1541"&gt;1541 disk drive&lt;/a&gt;, which let us store a whole whopping 170KB per side (340KB per disk) — albeit with the use of a hole punch.&lt;/p&gt;

&lt;p&gt;I mostly used this machine for making art and music, and playing games.&lt;/p&gt;

&lt;p&gt;A few years later we upgraded to a &lt;a href="https://en.wikipedia.org/wiki/Commodore_64"&gt;Commodore 128&lt;/a&gt;, although we kept using the 1541 disk drive. This had 128KB of memory, although I mostly just ran C64 apps on it (although as the family word processor, the C128-enhanced version of Pocket Writer was a huge upgrade for us).&lt;/p&gt;

&lt;p&gt;In the late 80s we got a PC AT clone. 80286 at 12MHz (I think), with a whopping &lt;em&gt;megabyte&lt;/em&gt; of RAM. This was the machine where I first got online, as well; we had a modem for the C64 but it was only 300 baud, and the 2400 baud modem on the 286 was actually useful. All online access was through a dialup account, and I doubt the modem program used more than 64KB of RAM (because DOS). So the ability to chat used (effectively) around 6%, sort of. We also had a 40MB hard drive for it.&lt;/p&gt;

&lt;p&gt;I mostly used this machine for making art, playing games, and chatting online. (I still used the C64 for making music. Not the C128 — its SID chip sadly met a tragic end due to an incident with static electricity.)&lt;/p&gt;

&lt;p&gt;In the early 90s we got a 486, with 4 MB of RAM. It mostly ran Windows, and we got online via AOL. I don't know how much RAM the client used, but it seems credible that it would have used around 250KB, which would have been 6%. We eventually upgraded to 8MB (that extra 4 MB of SIMMs cost something like $180 in 1994 dollars!). I believe we started out with a 100MB hard drive and eventually upgraded to a whopping 200MB.&lt;/p&gt;

&lt;p&gt;I mostly used this machine for making art, playing games, making music (Pro Audio Spectrum 16, heck yes), and chatting online.&lt;/p&gt;

&lt;p&gt;When I went off to college in 1995 I built myself a 486/100. I went &lt;em&gt;all out&lt;/em&gt;, equipping it with 16MB of RAM, and had it dual-boot Windows and Linux. I think the hard drive was 1.2GB! It was so amazing.&lt;/p&gt;

&lt;p&gt;I mostly used this machine for making art, playing games, chatting online (using mIRC, which I wouldn't be at all surprised if that used around 1MB of RAM — i.e. 6%), and making music.&lt;/p&gt;

&lt;p&gt;Okay, so, life moves forward. More and more computers happen, storage gets bigger, RAM gets bigger.&lt;/p&gt;

&lt;p&gt;Come forward to 2017 and I buy my current machine, an iMac, with 2TB of built-in storage, with a 1TB external drive for my media and a... rather large NAS. I'm &lt;em&gt;swimming&lt;/em&gt; in storage capacity, and it &lt;em&gt;still&lt;/em&gt; doesn't feel like enough space.&lt;/p&gt;

&lt;p&gt;And I mostly use this machine for... making art and music, playing games, and chatting online.&lt;/p&gt;

&lt;p&gt;And the chat clients still take 6% of my memory.&lt;/p&gt;

&lt;p&gt;But wait! In the introduction, I said I had a means of making the chat clients only take 3% of my memory. And it was an easy fix!&lt;/p&gt;

&lt;p&gt;Did I switch to one of the alternate clients like &lt;a href="https://www.sblack.online"&gt;Sblack&lt;/a&gt; or &lt;a href="https://cancel.fm/ripcord/"&gt;Ripcord&lt;/a&gt;? Well, I've tried those out, but their UX doesn't quite work for my accessibility needs.&lt;/p&gt;

&lt;p&gt;Did I switch to an IRC-based frontend? No, that removes too much of the functionality to be useful.&lt;/p&gt;

&lt;p&gt;How about exotic things like forcing macOS to compress my RAM? Again, no!&lt;/p&gt;

&lt;p&gt;The solution was much simpler than that...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NsyQ5lEd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/dy5uazy03cjlxca0p8l1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NsyQ5lEd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/dy5uazy03cjlxca0p8l1.png" alt="...I upgraded my machine to 48GB." width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyway, hopefully this will help me make art and music, play games, and chat online.&lt;/p&gt;

</description>
      <category>electron</category>
    </item>
    <item>
      <title>The problem with select() vs poll()</title>
      <dc:creator>fluffy</dc:creator>
      <pubDate>Mon, 04 Feb 2019 23:21:39 +0000</pubDate>
      <link>https://dev.to/fluffy/the-problem-with-select-vs-poll-2k1h</link>
      <guid>https://dev.to/fluffy/the-problem-with-select-vs-poll-2k1h</guid>
      <description>&lt;p&gt;The UNIX &lt;code&gt;select()&lt;/code&gt; API should have been deprecated years ago. While unsafe operations like &lt;code&gt;sscanf()&lt;/code&gt;, &lt;code&gt;sprintf()&lt;/code&gt;, &lt;code&gt;gets()&lt;/code&gt;, and so forth all provide compile-time deprecation warnings, &lt;code&gt;select()&lt;/code&gt; is also incredibly dangerous and has a more modern, safer replacement (&lt;code&gt;poll()&lt;/code&gt;), but yet people continue to use it.&lt;/p&gt;

&lt;p&gt;The problem is that it doesn't scale. In this case, "not scaling" doesn't just mean it's bad for performance, "not scaling" means it can destroy your call stack, crash your process, and leave it in a state that is &lt;em&gt;incredibly&lt;/em&gt; difficult to debug.&lt;/p&gt;

&lt;p&gt;There are actually two problems, and they are closely-related. The hint to both of them comes from the actual signature of the select function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;FD_CLR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fdset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;FD_COPY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fdset_orig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fdset_copy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;FD_ISSET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fdset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;FD_SET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fdset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;FD_ZERO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fdset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;nfds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kr"&gt;restrict&lt;/span&gt; &lt;span class="n"&gt;readfds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kr"&gt;restrict&lt;/span&gt; &lt;span class="n"&gt;writefds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kr"&gt;restrict&lt;/span&gt; &lt;span class="n"&gt;errorfds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;timeval&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kr"&gt;restrict&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Do you see that &lt;strong&gt;&lt;code&gt;nfds&lt;/code&gt;&lt;/strong&gt; parameter? Here is what the BSD libc documentation has to say about it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The first &lt;code&gt;nfds&lt;/code&gt; descriptors are checked in each set; i.e., the descriptors from 0 through nfds-1 in the descriptor sets are examined. (Example: If you have set two file descriptors "4" and "17", nfds should not be "2", but rather "17 + 1" or "18".)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What this is hinting at is that fd_set's implementation is not a container that contains a maximum number of sparsely-populated file descriptors; instead, it is a bit vector saying whether to check the fds at a given offset. That is, in pseudocode, people expect it to be something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;fd_record&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;FD_SETSIZE&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;but it's really more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;FD_SETSIZE&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So this immediately shows what the two problems are, but I'll still explain it further (since this wasn't enough when I was helping diagnose a problem with some critical production code that was breaking). Both of them involve what happens when your process has a large number of file descriptors open (which is very common in large-scale services).&lt;/p&gt;

&lt;h3&gt;
  
  
  First problem: performance
&lt;/h3&gt;

&lt;p&gt;Let's say you are trying to check the state of a single socket with fd of 907 — which happens to be within &lt;code&gt;FD_SETSIZE&lt;/code&gt; on a modern Linux. So your code looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;FD_ZERO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;FD_SET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;907&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;908&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here is what select() is doing internally (again, pseudocodeishly):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;908&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readfds&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;readfds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;readfds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_readable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writefds&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;writefds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;writefds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_writeable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errorfds&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;errorfds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;errorfds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_errored&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this case, since only fd 907 is actually being checked, it's looping futilely over 906 entries which don't need to be checked. If you're doing &lt;code&gt;select()&lt;/code&gt; a lot, this can add up (remember that the structures described above are just pseudocode, and in reality there's a lot of bitfiddling/masking/etc. going on as well).&lt;/p&gt;

&lt;p&gt;But this isn't the &lt;em&gt;dire&lt;/em&gt; problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 2: How it can destroy your stack
&lt;/h3&gt;

&lt;p&gt;So let's look at the same thing as above, except now the fd in question is, say, 2048, which is quite a lot larger than &lt;code&gt;FD_SETSIZE&lt;/code&gt;. Theoretically this is not possible, but in practice it is — it's pretty simple to raise the socket ulimit for circumstances where you need a lot of connections open. (For example, in a large-scale distributed cache. Again, speaking from experience on this.)&lt;/p&gt;

&lt;p&gt;Let's annotate the code that calls it, first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// allocates a 1024-bit vector on the stack&lt;/span&gt;
&lt;span class="n"&gt;FD_ZERO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// clears the 1024-bit vector&lt;/span&gt;
&lt;span class="n"&gt;FD_SET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Sets the 2048th bit; congratulations, you've corrupted your stack a little bit&lt;/span&gt;
&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2049&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// congratulations, you've just BLOWN AWAY your stack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Why does this blow away the stack? Well, keep in mind what &lt;code&gt;select()&lt;/code&gt; is doing internally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2049&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readfds&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;readfds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;readfds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_readable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writefds&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;writefds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;writefds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_writeable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errorfds&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;errorfds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;errorfds&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_errored&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That is, for all the values up to 2048 (which, remember, is outside of the &lt;code&gt;fd_set&lt;/code&gt; and well into your stack at this point), it's checking a bit to see if a garbage fd needs to be checked (which is relatively harmless), checking that fd (which is relatively harmless but kills your performance), and then sets that value in memory based on whether the fd is in that particular state (which is, no matter what, essentially randomizing the stack).&lt;/p&gt;

&lt;p&gt;Congratulations, now not only will you get a weird error, you won't even be able to tell where it comes from because your debugger will give you complete garbage on the call trace.&lt;/p&gt;

&lt;h3&gt;
  
  
  The easy fix: use poll()
&lt;/h3&gt;

&lt;p&gt;Hindsight is 20/20, of course, but &lt;code&gt;poll()&lt;/code&gt; is the API that &lt;code&gt;select()&lt;/code&gt; should have been in the first place. When &lt;code&gt;select()&lt;/code&gt; was designed, UNIX had no asynchronous I/O mechanism at all, and the whole concept of a large-scale network server was unheard of. Everything was its own process, and you'd do something like &lt;code&gt;accept(8)&lt;/code&gt; to accept only 8 simultaneous connections at a time, and each of those connections would likely open up a handful of files to serve up static content or whatever. In the early days, &lt;code&gt;FD_SETSIZE&lt;/code&gt; was only 16; Winsock raised it to 64, and early Linux raised it to 256. That was still more than adequate for a reasonably-sized server at the time, and it was still generally the case that you'd be checking all of your fds at once in a single-threaded event loop (heck, back then, the whole concept of "thread" was just an abuse of &lt;code&gt;vfork()&lt;/code&gt; and not something that any reasonable programmer would expect to use) and that there wouldn't be that many to check; the overhead of managing a variable-sized buffer would be greater than the savings of not having the conditional loop.&lt;/p&gt;

&lt;p&gt;But nowadays, any given Internet server can be easily handling thousands, if not tens of thousands, of connections at once, and the &lt;code&gt;FD_SET&lt;/code&gt;/&lt;code&gt;select()&lt;/code&gt; API is fundamentally broken in a way which can never, ever make this safe.&lt;/p&gt;

&lt;p&gt;Fortunately, there's another API, &lt;code&gt;poll()&lt;/code&gt;, which is much more like what people expect out of &lt;code&gt;select()&lt;/code&gt; in the first place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;pollfd&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt;    &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="cm"&gt;/* file descriptor */&lt;/span&gt;
    &lt;span class="kt"&gt;short&lt;/span&gt;  &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="cm"&gt;/* events to look for */&lt;/span&gt;
    &lt;span class="kt"&gt;short&lt;/span&gt;  &lt;span class="n"&gt;revents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="cm"&gt;/* events returned */&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;pollfd&lt;/span&gt; &lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;nfds_t&lt;/span&gt; &lt;span class="n"&gt;nfds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Essentially, you tell it exactly how many fds you're asking about, and for each fd, exactly which events you want to know about. So now the &lt;code&gt;poll()&lt;/code&gt; call only has to scan the actual specified fds, and only check the actual specified events — no checking readable, writable, and error conditions for ALL fds that are ≤ the highest one you're asking about.&lt;/p&gt;

&lt;p&gt;In the common case, &lt;code&gt;poll()&lt;/code&gt; is a simple drop-in replacement, and is generally simpler and easier to read; this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;fd_set&lt;/span&gt; &lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;FD_ZERO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd_set&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;FD_SET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;timeval&lt;/span&gt; &lt;span class="n"&gt;tv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;tv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tv_sec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;tv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tv_usec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;501&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tv&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FD_ISSET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;pollfd&lt;/span&gt; &lt;span class="n"&gt;pfd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;pfd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;pfd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POLLIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pfd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;revents&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;POLLIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And, obviously, if you want to check multiple types of events on a socket, it becomes even simpler, as you only need a single set of pollfd structures and simple bit masking on the calling side.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do people still write code with &lt;code&gt;select()&lt;/code&gt; then?
&lt;/h3&gt;

&lt;p&gt;I have a couple of theories. &lt;code&gt;select()&lt;/code&gt; is the legacy API that was provided in all of the classic textbooks; universities possibly still teach networking using it, and there's a lot of sample code out there that uses &lt;code&gt;select&lt;/code&gt; and even the best programmers still have a tendency to copy-paste at times.&lt;/p&gt;

&lt;p&gt;Additionally, the fact that gcc doesn't issue a deprecation warning on it means that people haven't had any reason to change their practices. The man pages tend to contain warnings, but people generally just read the signature and return codes.&lt;/p&gt;

&lt;p&gt;Current versions of certain libc variants will return an error if nfds ≥ &lt;code&gt;FD_SETSIZE&lt;/code&gt; (for example, the one on OSX 10.8 will, although there is apparently no warning to this effect on even current Linux glibc), but at that point the stack is already corrupted and if your server has gotten to the point that a subtle hard-to-understand glibc error is occurring, you're already having a bad day and probably don't realize where the issue is coming from.&lt;/p&gt;

&lt;p&gt;For what it's worth, one version of the Linux manpage for &lt;code&gt;select()&lt;/code&gt; has this to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An &lt;code&gt;fd_set&lt;/code&gt; is a fixed size buffer. Executing &lt;code&gt;FD_CLR()&lt;/code&gt; or &lt;code&gt;FD_SET()&lt;/code&gt; with a value of &lt;code&gt;fd&lt;/code&gt; that is negative or is equal to or larger than &lt;code&gt;FD_SETSIZE&lt;/code&gt; will result in undefined behavior. Moreover, POSIX requires &lt;code&gt;fd&lt;/code&gt; to be a valid file descriptor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Undefined behavior" is putting it lightly. (And it makes no mention of how &lt;code&gt;select()&lt;/code&gt; itself is also a ticking time bomb in that situation.)&lt;/p&gt;

&lt;h3&gt;
  
  
  What about &lt;code&gt;epoll&lt;/code&gt; and libEvent?
&lt;/h3&gt;

&lt;p&gt;I purposefully did not discuss them in this article. While both are far superior to &lt;code&gt;poll&lt;/code&gt;, they aren't simple drop-in replacements to &lt;code&gt;select&lt;/code&gt;, and at least in the basic case of an Internet server the more important thing is not crashing mysteriously. Obviously with a better asynchronous eventing mechanism you will scale better, but scaling better is often a longer-term goal than scaling at all.&lt;/p&gt;

</description>
      <category>c</category>
      <category>cpp</category>
      <category>networking</category>
      <category>systemprogramming</category>
    </item>
    <item>
      <title>#ifndef guards vs. #pragma once</title>
      <dc:creator>fluffy</dc:creator>
      <pubDate>Fri, 07 Dec 2018 02:04:58 +0000</pubDate>
      <link>https://dev.to/fluffy/ifndef-guards-vs-pragma-once-m6k</link>
      <guid>https://dev.to/fluffy/ifndef-guards-vs-pragma-once-m6k</guid>
      <description>&lt;p&gt;After getting in an extended discussion about the supposed performance tradeoff between &lt;code&gt;#pragma once&lt;/code&gt; and &lt;code&gt;#ifndef&lt;/code&gt; guards vs. the argument of correctness or not (I was taking the side of &lt;code&gt;#pragma once&lt;/code&gt; based on some relatively recent indoctrination to that end), I decided to finally test the theory that &lt;code&gt;#pragma once&lt;/code&gt; is faster because the compiler doesn't have to try to re-&lt;code&gt;#include&lt;/code&gt; a file that had already been included.&lt;/p&gt;

&lt;p&gt;For the test, I automatically generated 500 header files with complex interdependencies, and had a &lt;code&gt;.c&lt;/code&gt; file that &lt;code&gt;#include&lt;/code&gt;s them all. I ran the test three ways, once with just &lt;code&gt;#ifndef&lt;/code&gt;, once with just &lt;code&gt;#pragma once&lt;/code&gt;, and once with both. I performed the test on a fairly modern system (a 2014 MacBook Pro running OSX, using XCode's bundled Clang, with the internal SSD).&lt;/p&gt;

&lt;p&gt;First, the test code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;//#define IFNDEF_GUARD&lt;/span&gt;
&lt;span class="c1"&gt;//#define PRAGMA_ONCE&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;FILE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="n"&gt;snprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"include%d.h"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;fp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cp"&gt;#ifdef IFNDEF_GUARD
&lt;/span&gt;        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"#ifndef _INCLUDE%d_H&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;#define _INCLUDE%d_H&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cp"&gt;#endif
#ifdef PRAGMA_ONCE
&lt;/span&gt;        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"#pragma once&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"#include &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;include%d.h&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"int foo%d(void) { return %d; }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cp"&gt;#ifdef IFNDEF_GUARD
&lt;/span&gt;        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"#endif&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;
        &lt;span class="n"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;fp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main.c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"#include &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;include%d.h&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"int main(void){int n;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"n += foo%d();&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"return n;}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, my various test runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;gcc pragma.c &lt;span class="nt"&gt;-DIFNDEF_GUARD&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./a.out
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;gcc &lt;span class="nt"&gt;-E&lt;/span&gt; main.c  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="go"&gt;
real    0m0.164s
user    0m0.105s
sys 0m0.041s
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;gcc &lt;span class="nt"&gt;-E&lt;/span&gt; main.c  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="go"&gt;
real    0m0.140s
user    0m0.097s
sys 0m0.018s
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;gcc &lt;span class="nt"&gt;-E&lt;/span&gt; main.c  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="go"&gt;
real    0m0.193s
user    0m0.143s
sys 0m0.024s
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;gcc pragma.c &lt;span class="nt"&gt;-DPRAGMA_ONCE&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./a.out
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;gcc &lt;span class="nt"&gt;-E&lt;/span&gt; main.c  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="go"&gt;
real    0m0.153s
user    0m0.101s
sys 0m0.031s
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;gcc &lt;span class="nt"&gt;-E&lt;/span&gt; main.c  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="go"&gt;
real    0m0.170s
user    0m0.109s
sys 0m0.033s
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;gcc &lt;span class="nt"&gt;-E&lt;/span&gt; main.c  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="go"&gt;
real    0m0.155s
user    0m0.105s
sys 0m0.027s
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;gcc pragma.c &lt;span class="nt"&gt;-DPRAGMA_ONCE&lt;/span&gt; &lt;span class="nt"&gt;-DIFNDEF_GUARD&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./a.out
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;gcc &lt;span class="nt"&gt;-E&lt;/span&gt; main.c  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="go"&gt;
real    0m0.153s
user    0m0.101s
sys 0m0.027s
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;gcc &lt;span class="nt"&gt;-E&lt;/span&gt; main.c  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="go"&gt;
real    0m0.181s
user    0m0.133s
sys 0m0.020s
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;gcc &lt;span class="nt"&gt;-E&lt;/span&gt; main.c  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="go"&gt;
real    0m0.167s
user    0m0.119s
sys 0m0.021s
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;gcc &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="go"&gt;Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include/c++/4.2.1
Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin17.0.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the versions with &lt;code&gt;#pragma once&lt;/code&gt; were indeed slightly faster to preprocess than the &lt;code&gt;#ifndef&lt;/code&gt;-only one, &lt;em&gt;but&lt;/em&gt; the difference was quite negligible, and would be far overshadowed by the amount of time that actually building and linking the code would take. Perhaps with a large enough codebase it might actually lead to a difference in build times of a few seconds, but between modern compilers being able to optimize &lt;code&gt;#ifndef&lt;/code&gt; guards, the fact that OSes have good disk caches, and the increasing speeds of storage technology, it seems that the performance argument is moot, at least on a typical developer system in this day and age. Older and more exotic build environments (e.g. headers hosted on a network share, building from tape, etc.) may change the equation somewhat but in those circumstances it seems more useful to simply make a less fragile build environment in the first place.&lt;/p&gt;

&lt;p&gt;The fact of the matter is, &lt;code&gt;#ifndef&lt;/code&gt; is standardized with standard behavior whereas &lt;code&gt;#pragma once&lt;/code&gt; is not, and &lt;code&gt;#ifndef&lt;/code&gt; also handles weird filesystem and search path corner cases whereas &lt;code&gt;#pragma once&lt;/code&gt; can get very confused by certain things, leading to incorrect behavior which the programmer has no control over. The main problem with &lt;code&gt;#ifndef&lt;/code&gt; is programmers choosing bad names for their guards (with name collisions and so on) and even then it's quite possible for the consumer of an API to override those poor names using &lt;code&gt;#undef&lt;/code&gt; - not a perfect solution, perhaps, but it's &lt;em&gt;possible&lt;/em&gt;, whereas &lt;code&gt;#pragma once&lt;/code&gt; has no recourse if the compiler is erroneously culling an &lt;code&gt;#include&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Thus, &lt;em&gt;even though&lt;/em&gt; &lt;code&gt;#pragma once&lt;/code&gt; is demonstrably (slightly) faster, I don't agree that this in and of itself is a reason to use it over &lt;code&gt;#ifndef&lt;/code&gt; guards.&lt;/p&gt;

</description>
      <category>c</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Making a hash of data</title>
      <dc:creator>fluffy</dc:creator>
      <pubDate>Thu, 06 Dec 2018 17:26:51 +0000</pubDate>
      <link>https://dev.to/fluffy/making-a-hash-of-data-11bc</link>
      <guid>https://dev.to/fluffy/making-a-hash-of-data-11bc</guid>
      <description>&lt;p&gt;When I was &lt;a href="http://publ.beesbuzz.biz/blog/272-v0-3-0-released"&gt;replacing peewee with PonyORM in my web publishing engine&lt;/a&gt;, I was evaluating a few options, including moving away from an ORM entirely and simply storing the metadata in indexed tables in memory. This would have also helped to solve a couple of minor annoying design issues (such as improper encapsulation of the actual content state into the application instance), but I ended up not doing this.&lt;/p&gt;

&lt;p&gt;A big reason why is that there don't actually seem to be any useful in-memory indexed table libraries for Python. Or many other languages.&lt;/p&gt;

&lt;p&gt;Back in the 90s when I was a larval software engineer, the common practice for teaching data structures always started with the basics: linked lists, then binary trees (first naïve, then self-balancing ones such as &lt;a href="https://en.wikipedia.org/wiki/AVL_tree"&gt;AVL&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Red%E2%80%93black_tree"&gt;red-black trees&lt;/a&gt;, possibly with a foray into &lt;a href="https://en.wikipedia.org/wiki/B-tree"&gt;B-trees&lt;/a&gt;), and &lt;em&gt;then&lt;/em&gt; onto hash tables, typically starting with fixed hashes and then moving on to dynamic hashes. Hash tables themselves were often even treated as an afterthought, a performance optimization that wasn't useful in the general case! In the meantime, algorithms based on &lt;a href="https://en.wikipedia.org/wiki/Binary_search_algorithm"&gt;binary search&lt;/a&gt; on sorted lists and the like would also be a major part of the curriculum.&lt;/p&gt;

&lt;p&gt;Somewhere along the lines, though, it seems like everyone has moved away from ordered, indexed structures and towards everything being based on hash tables. Somehow as an industry we've all decided that it's only important to be able to do O(1) constant-time lookup on fixed keys with no ordering necessary between them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As an aside, I don't think it has anything to do with experience, and everything to do with the environment in which people are being taught data structures; my CS education was &lt;em&gt;very much&lt;/em&gt; focused on the way that data structures and algorithms work, but it seems like all of the younger programmers I talk to (oh god I sound so old now) were taught that data structures themselves aren't that important beyond being an implementation detail or the basic theory, and furthermore that computers have such vast resources available that you don't &lt;em&gt;really&lt;/em&gt; need to care about this (oh god I &lt;em&gt;am&lt;/em&gt; so old now).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;C++ and Java both provide ordered associative structures. &lt;a href="http://www.cplusplus.com/reference/map/map/"&gt;&lt;code&gt;std::map&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://docs.oracle.com/javase/7/docs/api/java/util/TreeMap.html"&gt;&lt;code&gt;TreeMap&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt;, for example. But whenever you ask a programmer (especially a younger one) about them these days, people just point out that they're "slow" because they're O(log₂ N) for key lookup, and that you should only use &lt;code&gt;std::unordered_map&lt;/code&gt; or &lt;code&gt;HashMap&amp;lt;T&amp;gt;&lt;/code&gt; instead, because they're O(1).&lt;/p&gt;

&lt;p&gt;But focusing on this forgets a few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The big-O complexity factor matters for really large values of N, ignoring the constant overhead of the underlying computations (which, in the case of string hashing, can be pretty slow, especially compared to a handful of lexical comparisons)&lt;/li&gt;
&lt;li&gt;Single-key lookup isn't the only dang thing you might want to be doing on your data!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ordered indexes are really freaking useful for a lot of stuff. For example, trying to find all of the keys matching a prefix, or finding values which are greater than or less than a key (or doing a range query in general). Or quickly finding the next or previous entry in a content store.&lt;/p&gt;

&lt;p&gt;All of these things require a full table scan -- meaning an O(N) operation -- in a hash-only scenario. Or if you want to do a range query and then sort it at the end, it takes O(N log₂ N), as you have to first filter the table (which is O(N)) and then sort it (which is O(N log₂ N)). How is this more efficient than just using an O(log₂ N) one-time lookup?&lt;/p&gt;

&lt;h3&gt;
  
  
  Interviewing candidates
&lt;/h3&gt;

&lt;p&gt;One of my recurring duties as a full-time software engineer was to interview other engineers as job candidates. One of my go-to problems was writing a &lt;a href="https://en.wikipedia.org/wiki/Boggle"&gt;Boggle&lt;/a&gt; solver. There are generally three phases to the solution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Determine an overall algorithm&lt;/li&gt;
&lt;li&gt;Given a function for determining if a prefix exists in the word list, traverse the board to find the solutions&lt;/li&gt;
&lt;li&gt;Implement the function for determining if the prefix exists&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Phase 3 is usually the hard part that most candidates have the most trouble with. There are very simple solutions to this problem, though. You could start out by sorting the wordlist in an array (O(N log₂ N)) and then do an O(log₂ N) prefix search for each word, or you can store it in a tree-type map (also O(N log₂ N) for the initial storage) and then do an O(log₂ N) lower-bound search for each word, or you can do what most candidates go with and either store the word list in a hash table (O(N)) and do a search through the whole table for every check (also O(N)), or they build a &lt;a href="https://en.wikipedia.org/wiki/Trie"&gt;trie&lt;/a&gt; to store all the words with a flag as to whether a node is a leaf (i.e. if the word is complete), which is essentially an O(N) initial phase and an O(L) lookup (where L is the length of the word). These are all acceptable solutions, &lt;em&gt;but&lt;/em&gt; the amount of code you have to write for each thing is... highly variable.&lt;/p&gt;

&lt;p&gt;For example, in C++, here is how you do a prefix search on a &lt;code&gt;std::set&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;has_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;wordlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;iter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wordlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower_bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;iter&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;wordlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;iter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Or here is how you do it in Java with a &lt;code&gt;TreeSet&amp;lt;String&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;has_prefix&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TreeSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wordlist&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wordlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;floor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Or if you're in a language without a tree-based set concept, such as Python, and you store your dictionary in a sorted list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;has_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wordlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;bisect&lt;/span&gt;
    &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bisect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bisect_left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wordlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wordlist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;wordlist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I'm not sure where along the line this concept got lost, or why whenever I ask people about indexed data structures in their language of choice they look at me like I've grown an extra head or two. Even among engineers who are aware of indexed data structures, the pushback I get is that it's not really relevant, and that we should all just be building everything out of hash tables or doing full table scans all the time. I feel like this all came from about a decade ago when suddenly Map-Reduce thinking took over the industry, and all software development was based around Big Data and Massive Scaling and parallel clustering of stuff. Which is just &lt;em&gt;really weird&lt;/em&gt; to me. Like, not all problems exist at that scale, you know?&lt;/p&gt;

&lt;p&gt;(And I mean Map-Reduce is useful at small scales too, it's just not the &lt;em&gt;universal&lt;/em&gt; way of reasoning about things. Especially when the excuse for it boils down to, "Eh, there's CPU power to spare, why bother?")&lt;/p&gt;

&lt;h3&gt;
  
  
  Further interview frustrations
&lt;/h3&gt;

&lt;p&gt;When trying to introspect into software engineers who just rely on a hash table for everything, I try to figure out if they even know how a hash table works.&lt;/p&gt;

&lt;p&gt;Almost &lt;em&gt;without fail&lt;/em&gt;, candidates who are asked about the underlying data structure end up starting out by taking the key, building a hash on it, and then &lt;em&gt;insert the key into a binary tree&lt;/em&gt;. This is pretty maddening! It means that they're paying for the complexity of a binary tree &lt;em&gt;and&lt;/em&gt; having the limited capabilities of a hash table -- a worst of both worlds situation. No in-order traversal combined with O(log₂ N) lookups. And when I complain to others about candidates not understanding these fundamentals, the feedback I get is that maybe I shouldn't be expecting people to remember introductory computer science courses (usually with an implication that the person I'm talking to doesn't know it either).&lt;/p&gt;

&lt;p&gt;How have we gotten to this point?&lt;/p&gt;

&lt;h3&gt;
  
  
  C++
&lt;/h3&gt;

&lt;p&gt;Anyway, just out of curiosity I decided to do a timing comparison between a few different ways of implementing a Boggle solver; the only difference between these algorithms is the implementation of &lt;code&gt;has_prefix&lt;/code&gt; based on the data structure in question. (Also note that the code itself is crusty stuff I wrote many years ago for a very specific challenge and all I've done with it for this article is to change the data structures in use. It is certainly not high-quality code and I suspect if I were to actually look at most of the code I'd be a bit aghast at it right now.)&lt;/p&gt;

&lt;p&gt;Using an &lt;a href="http://beesbuzz.biz/static/blog/hashing/boggle-orderedset.cpp"&gt;ordered set&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ g++ -O3 -Wall --std=c++11 boggle-orderedset.cpp
$ time ./a.out &amp;lt; board.txt &amp;gt; /dev/null

real    0m0.173s
user    0m0.160s
sys     0m0.010s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using a &lt;a href="http://beesbuzz.biz/static/blog/hashing/boggle-sortedvector.cpp"&gt;sorted vector&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ g++ -O3 -Wall --std=c++11 boggle-sortedvector.cpp
$ time ./a.out &amp;lt; board.txt &amp;gt; /dev/null

real    0m0.048s
user    0m0.039s
sys     0m0.007s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using a &lt;a href="http://beesbuzz.biz/static/blog/hashing/boggle-hashtable.cpp"&gt;hash table&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ g++ -O3 -Wall --std=c++11 boggle-hashtable.cpp
$ time ./a.out &amp;lt; board.txt &amp;gt; /dev/null

real    0m44.075s
user    0m43.867s
sys     0m0.110s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using a &lt;a href="http://beesbuzz.biz/static/blog/hashing/boggle-trie.cpp"&gt;hand-rolled trie&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ g++ -O3 -Wall --std=c++11 boggle-trie.cpp
$ time ./a.out &amp;lt; board.txt &amp;gt; /dev/null

real    0m0.362s
user    0m0.320s
sys     0m0.038s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the above solutions, the surprising thing is that the sorted vector is so much faster than the ordered set; however, what's not surprising is that both of those are many orders of magnitude faster than the hash table approach, and that the trie approach is somewhat slower than the ordered set. Granted, the trie implementation could be a bit better (for example, the actual search algorithm could track where it is in the trie rather than searching from root every time), but the amount of code written is also important:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wc -l *.cpp | sort -n
     131 boggle-orderedset.cpp
     132 boggle-sortedvector.cpp
     137 boggle-hashtable.cpp
     161 boggle-trie.cpp
     561 total
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So, the sorted vector is only slightly more complicated to implement (in fact the only line count difference is the call to &lt;code&gt;std::sort&lt;/code&gt; after the dictionary is loaded -- which is actually not even necessary if your dictionary is sorted to begin with), &lt;em&gt;and yet&lt;/em&gt; it's the fastest of all these by far.&lt;/p&gt;

&lt;p&gt;Okay, so that algorithm doesn't actually make use of an indexed data structure. But the thought process that leads to implementing it is along the same lines as the thought processes that leads to using an ordered indexed data structure; in effect, the &lt;code&gt;vector&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; an index, viewed in the greater sense. And, whenever I've interviewed a candidate with this problem, &lt;em&gt;not one&lt;/em&gt; has gone with a sorted array and a binary search! (I have had a couple at least go with an ordered &lt;code&gt;set&lt;/code&gt; though. But nearly everyone goes with the trie -- and most of them have never even heard of a trie before, and just sort of, like, invent it on the spot. Which is cool, but &lt;em&gt;still&lt;/em&gt;...)&lt;/p&gt;

&lt;p&gt;Also, the sorted &lt;code&gt;vector&lt;/code&gt; approach only really works performance-wise if your input set is static. Once you start adding stuff to it, well, each addition is a potentially O(N) operation, which can end up becoming incredibly costly very fast. For example, if you're writing, say, a software load balancer, and your table keeps track of your backing servers' load levels, every update to that requires changing a row in the index, and if you have a lot of rows (say, you're working at the sort of scale where map-reduce thinking takes over), every single update starts to add up very quickly.&lt;/p&gt;

&lt;p&gt;Anyway, with just standard C++ you can build your own indexes yourself, &lt;em&gt;or&lt;/em&gt; you can use &lt;a href="https://www.boost.org/doc/libs/1_62_0/libs/multi_index/doc/index.html"&gt;Boost.MultiIndex&lt;/a&gt;, which maintains arbitrarily many indexes for you. It's pretty neat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python
&lt;/h3&gt;

&lt;p&gt;Anyway, back to my original conundrum. I wanted to investigate moving Publ's data store into an in-memory table, with various indexes for the necessary sort criteria. The easiest approach was to stick with a database, despite that having very poor encapsulation (since the object storage is essentially global state). But what else could I have done?&lt;/p&gt;

&lt;p&gt;When I was asking around, everyone kept on pointing me towards &lt;a href="https://docs.python.org/3/library/collections.html#collections.OrderedDict"&gt;&lt;code&gt;collections.OrderedDict&lt;/code&gt;&lt;/a&gt;, which is a &lt;code&gt;dict&lt;/code&gt; which maintains the order of its keys. But by "maintains the order" it actually means maintaining the &lt;em&gt;insertion&lt;/em&gt; order, not any ongoing sort order. This isn't actually useful for the purpose of maintaining an index. (And even if it were, it doesn't provide any primitives for performing a range query or sibling iteration or whatever.)&lt;/p&gt;

&lt;p&gt;However, the &lt;a href="http://stutzbachenterprises.com/blist/"&gt;&lt;code&gt;blist&lt;/code&gt; package&lt;/a&gt; is a rather comprehensive B-tree implementation, and in particular it provides a &lt;a href="http://stutzbachenterprises.com/blist/sorteddict.html"&gt;&lt;code&gt;sorteddict&lt;/code&gt;&lt;/a&gt;, which does indeed maintain the sort order of its keys. It also provides a &lt;code&gt;KeysView&lt;/code&gt; which allows you to &lt;code&gt;bisect_left&lt;/code&gt; the keys themselves, in order to traverse items starting at a particular point. It's certainly not the worst thing to do. So, in the future, if I ever decide to get rid of an ORM entirely, this is what I will probably focus on using. However, this adds a bit more complexity in that if you know your sort key is going to change at all, you need to remember to remove your item from the b-tree and then readd it after it's been updated. (Fortunately this isn't all that different from a typical CRUD mechanism anyway.)&lt;/p&gt;

&lt;p&gt;And of course, Publ's use case calls for a lot of reads and not a lot of writes, so it's also not that big a deal to periodically rebuild indexes as a sorted list and just use &lt;code&gt;bisect_left&lt;/code&gt; that way. It's still something that needs to be managed directly, though.&lt;/p&gt;

&lt;p&gt;Maybe if/when I decide to de-ORMify Publ I'll just end up writing a library to make indexed tables easier to manage. I sure as heck can't find anything that exists as it is. (If anyone does know of anything, please let me know!)&lt;/p&gt;

&lt;p&gt;(I mean, unless I can find something that's similar to &lt;a href="https://en.wikipedia.org/wiki/Berkeley_DB"&gt;Berkeley DB&lt;/a&gt; and is actually supported in Python 3 and is MIT-license-compatible...)&lt;/p&gt;

&lt;p&gt;(note to self: &lt;a href="https://lmdb.readthedocs.io/en/release/"&gt;lmdb&lt;/a&gt; is a good candidate)&lt;/p&gt;

&lt;h3&gt;
  
  
  Java
&lt;/h3&gt;

&lt;p&gt;As mentioned previously, Java provides both &lt;code&gt;TreeMap/TreeSet&lt;/code&gt; and &lt;code&gt;HashMap/HashSet&lt;/code&gt;. But for some reason people are constantly taught to only use the &lt;code&gt;HashMap/HashSet&lt;/code&gt; versions, and this leads to people never even knowing that &lt;code&gt;TreeMap/TreeSet&lt;/code&gt; even &lt;em&gt;exist&lt;/em&gt; or even consider why they might want to use them. I find this incredibly baffling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lua and JavaScript
&lt;/h3&gt;

&lt;p&gt;Lua and JavaScript are very popular because of their simplicity; both of them make the same simplifying assumption in that &lt;em&gt;all&lt;/em&gt; data structures are hash tables -- including basic arrays. In many cases these get optimized under the hood to act as basic arrays but there are also many situations where that ends up falling apart, and this is why in Lua in particular you generally want to use &lt;code&gt;ipairs&lt;/code&gt; instead of &lt;code&gt;pairs&lt;/code&gt;, especially if order matters.&lt;/p&gt;

&lt;p&gt;The result of this is that there's also absolutely no concept of an indexed, ordered associative array. Either your array traversal is out-of-order (or, in JavaScript, insertion-order, as JS's array acts more or less like Python's &lt;code&gt;collections.OrderedDict&lt;/code&gt;, except when it doesn't), or you're getting all of your keys out of the array, sorting that, and then iterating on that array instead. Which adds even more layers of complexity, and reduces performance further.&lt;/p&gt;

&lt;p&gt;In Lua you're not likely to be writing anything which makes use of indexed structures (and if you are, you're probably implementing that stuff in C or C++ and calling into it with &lt;code&gt;ffi&lt;/code&gt;), but JavaScript? That's used to run a &lt;em&gt;lot&lt;/em&gt; of web services, and while node.js provides an &lt;code&gt;ffi&lt;/code&gt; binding, that's generally seen as a last resort. So what do people do when they need to handle indexes in node.js services?&lt;/p&gt;

&lt;p&gt;Well, in my experience, they seem to just defer it to an ORM, or shrug and suffer the poor performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go
&lt;/h3&gt;

&lt;p&gt;I haven't touched Go in a long time, but the last time I did, the golang orthodoxy was that you don't need ordered indexes. From some cursory websearches I'm finding that this appears to &lt;a href="https://nathanleclaire.com/blog/2014/04/27/a-surprising-feature-of-golang-that-colored-me-impressed/"&gt;still be the case&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Fortunately, there &lt;em&gt;are&lt;/em&gt; &lt;a href="https://github.com/emirpasic/gods"&gt;container libraries&lt;/a&gt; which correct this. It looks like &lt;code&gt;TreeMap&lt;/code&gt; even &lt;a href="https://github.com/emirpasic/gods/pull/82"&gt;now provides &lt;code&gt;floor&lt;/code&gt; and &lt;code&gt;ceiling&lt;/code&gt;&lt;/a&gt; which can then be used to implement range queries. It looks like it only gained this functionality incredibly recently (i.e. a month ago as of this writing).&lt;/p&gt;

&lt;h3&gt;
  
  
  C#
&lt;/h3&gt;

&lt;p&gt;C# provides a &lt;code&gt;Dictionary&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;You know what you can do with a real-life dictionary? You can quickly find a word you want, and then see which words come before and after it.&lt;/p&gt;

&lt;p&gt;You know what you can't do with a C# &lt;code&gt;Dictionary&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Okay, so C# does also provide &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.collections.specialized.ordereddictionary?view=netframework-4.7.2"&gt;&lt;code&gt;OrderedDictionary&lt;/code&gt;&lt;/a&gt; but this doesn't, as far as I can tell, provide any iteration methods for getting the next or previous entries, or any sort of range queries at all. Presumably these are possible via &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/getting-started-with-linq"&gt;LINQ&lt;/a&gt;, though.&lt;/p&gt;

&lt;h3&gt;
  
  
  Oh I guess I forgot to write a conclusion
&lt;/h3&gt;

&lt;p&gt;So yeah uh. It's so weird to me that this is the way software has gone. Everything's a big pile of hash tables and nobody is expected to treat anything differently or learn any algorithms that make use of other storage representations. I find that very sad.&lt;/p&gt;

</description>
      <category>datastructures</category>
      <category>python</category>
      <category>rant</category>
      <category>cpp</category>
    </item>
  </channel>
</rss>
