{"@attributes":{"version":"2.0"},"channel":{"title":"DHO is Mostly Confused","link":"https:\/\/9vx.org\/index.xml","description":"Recent content on DHO is Mostly Confused","generator":"Hugo -- gohugo.io","language":"en-us","copyright":"This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. (http:\/\/creativecommons.org\/licenses\/by-nc-nd\/4.0\/)","lastBuildDate":"Sun, 22 Apr 2018 00:40:00 +0000","item":[{"title":"Wait-free Monitors for Concurrent Batching Workloads","link":"https:\/\/9vx.org\/post\/wait-free-monitors\/","pubDate":"Sun, 22 Apr 2018 00:40:00 +0000","guid":"https:\/\/9vx.org\/post\/wait-free-monitors\/","description":"<p><span class=\"newthought\">Monitors are hard to use.<\/span>\n Per <a href=\"https:\/\/en.wikipedia.org\/wiki\/Monitor_%28synchronization%29\">Wikipedia&rsquo;s entry on the topic<\/a>, &ldquo;a monitor is a synchronization construct that allows threads to have both mutual exclusion and the ability to wait (block) for a certain condition to become true.&rdquo; The use case for such a construct is often to signal other threads that new work may be completed. Unfortunately, they&rsquo;re really easy to use incorrectly, largely because many APIs for them are terrible.<\/p>\n\n<p>In POSIX, monitors are provided through the API for condition variables. A new condition variable is created with <code>pthread_cond_init(3)<\/code>; conditions are waited on and signalled using <code>pthread_cond_wait(3)<\/code> \/ <code>pthread_cond_timedwait(3)<\/code> and <code>pthread_cond_signal(3)<\/code> \/ <code>pthread_cond_broadcast(3)<\/code>, respectively. To use the <code>pthread_cond_wait(3)<\/code> interface, you also need a mutex (because what you really have is a monitor); this mutex must be held before calling <code>pthread_cond_wait(3)<\/code>, which will unlock the mutex before waiting on the condition, and return with the mutex still held. If this doesn&rsquo;t sound subtle enough, consider the really crappy bit: the <code>pthread_cond_signal(3)<\/code> API does not accept a mutex as an argument at all! What you get from this is a ton of confusion about lost signals when people call <code>pthread_cond_signal(3)<\/code> without holding the mutex around signalling the condition.<\/p>\n\n<p>And what happens in this scenario? You lose messages: notifications on a condition are not persistent. This API assumes that the only time the mutex will be unlocked is when a thread is waiting on a condition, and that signals will only occur with that mutex held. The problem is that, although the API had an opportunity to enforce this, it did not do so.<\/p>\n\n<p>Apologists for the API say that one should &ldquo;just know&rdquo; how to use it correctly: these requirements are common knowledge. But misuse appears even in popular software, written by experienced individuals. One such example can be found in the source code of the widely popular <a href=\"http:\/\/varnish-cache.org\/\">Varnish Cache<\/a>: the condvar is <a href=\"https:\/\/github.com\/varnishcache\/varnish-cache\/blob\/17f4c18ee8d1f25d77c41168da7274e7331815b7\/bin\/varnishd\/cache\/cache_wrk.c#L541-L552\">waited on with the lock held<\/a>, but notifications <a href=\"https:\/\/github.com\/varnishcache\/varnish-cache\/blob\/2061f887203ec454719fe760419f4562ca8876e6\/bin\/varnishd\/cache\/cache_pool.c#L207-L220\">are sent without the lock held<\/a>. In fact, even though the condvar is waited on with the lock held, that lock is also released for portions of the loop that waits on the condition, so it&rsquo;s still possible for the mutex to be acquired when the condition isn&rsquo;t being waited upon. And it&rsquo;s done this way because that mutex is conflated between the condition and shared mutable state in the structure it lives in. This misuse of condvars has existed in Varnish for over a decade.<\/p>\n\n<p><span class=\"newthought\">Let&rsquo;s take a step back<\/span>\n and consider a couple use-cases for monitors.<\/p>\n\n<p>First, let&rsquo;s consider a log producer where many worker threads are producing log messages, and a consumer thread is persisting those to some kind of storage in batches. When no work is being done, we don&rsquo;t want the consumer thread to spin waiting for logs to write. So our producers acquire a lock and signal a condition that the consumer waits on. This is problematic in high-volume services, because there&rsquo;s significant contention on the monitor&rsquo;s mutex. Additionally, if we have misused the API, it&rsquo;s possible that a low-volume service with a burst of traffic will have logs delayed until the next log item occurs &ndash; which may be a while, since the service doesn&rsquo;t have much volume.<\/p>\n\n<p>Consider a sharded worker pool where one or more producers may generate work items, and a pool of workers must each do something different with these work items. In this scenario, every worker thread must see every message. You might be tempted to use <code>pthread_cond_broadcast(3)<\/code>, but this turns out to be an anti-pattern. First of all, each waiter on the condvar must have acquired the mutex to return from <code>pthread_cond_wait(3)<\/code>, so this means that your worker jobs actually execute serially. And if you&rsquo;ve decided to solve this by unlocking the mutex while you perform your work, another producer may decide to broadcast while no workers are waiting. In reality, you have two options if you want to maintain correctness while also parallelizing your work: either you use N condition variables and N mutexes to signal N workers, or you need to wait on a semaphore (that is also protected by the monitor&rsquo;s lock!) to ensure all your workers are waiting before you broadcast. Neither of these solutions are particularly elegant.<\/p>\n\n<p><span class=\"newthought\">My final gripe with monitors<\/span>\n is that they require lock-based synchronization. Lock-based synchronization is nearly the canonical way to protect shared mutable state from concurrent access, but this approach has several drawbacks.<\/p>\n\n<p>Locks are not composable. When we talk about composability with regard to an API, we really mean a couple of things. First, a composable interface is <em>self-contained<\/em>: it does not have dependencies on external state for its correctness. Mutexes are not self-contained: their correct use depends on consuming the mutex API at every appropriate place in code, and the API is also sensitive to ordering. Ordering issues don&rsquo;t sound bad when all you have to consider are lock and unlock operations, but sometimes mutable state must be protected by multiple mutexes. At this point, the ordering of which mutex is locked first is important, as is the ordering of unlock operations &ndash; again, at every point that these mutexes have to operate. Apologists will say that it&rsquo;s easier to use locks than anything else; I have yet to see or work on a large, multi-threaded codebase that hasn&rsquo;t run into issues directly related to lack of composability.<\/p>\n\n<p>Another issue with locks is effectively how they work. If you ask folks what a lock does, they&rsquo;ll typically say something like, &ldquo;A lock protects concurrent access to shared memory within its critical section.&rdquo; But this isn&rsquo;t really what locks do. Locks are a construct that serialize execution through their critical section such that only a single process may execute that code at a time. This has the side-effect of achieving the goal of protecting concurrent access to shared mutable state, but unless you consider <em>how<\/em> it achieves that, you&rsquo;re missing numerous downsides. While a lock is held, no other concurrent task may acquire the lock. This means that resources dedicated to running code are effectively stalled waiting for the critical section to finish. Maybe this doesn&rsquo;t seem so bad; critical sections are typically pretty short. The problem is that modern schedulers only allow tasks to run for so long. Once you&rsquo;ve used your timeshare, it&rsquo;s going to want to run something else. If you&rsquo;ve lost your timeshare in the middle of a critical section, literally every other process waiting on that lock is <em>also<\/em> not runnable, and your system makes no progress.<\/p>\n\n<p>These issues are fundamental to the monitor API provided by pthreads. (Other monitor implementations may address the composability problem a bit better, but the issue with progress guarantees remains by virtue of mutual exclusion.) The pthread monitor interface introduces additional dependencies between a mutex and data, imposes additional requirements on both lock ordering and critical section size, and does nothing to solve the problem of system progress (which is ostensibly one reason you&rsquo;d want to use threads at all).<\/p>\n\n<p><span class=\"newthought\">Can we do better?<\/span>\n At my previous job, I had a few situations similar to the log producer situation I mentioned above: multiple threads producing work items, and a single thread acting upon that work in batches. I wanted to use some lock-free datastructures from <a href=\"http:\/\/concurrencykit.org\/\">Concurrency Kit<\/a> to create a queue of work that the consumer could easily batch, but there were some problems. With monitors, I&rsquo;d be blocking work producers while the batch work was being completed, and in some cases that could have taken a non-trivial amount of time. In other cases, the consumer was able to operate quickly, but the production was high-volume, and I didn&rsquo;t want to have producers blocking each other in their fast path. So I made a wait-free monitor for MPSC workloads.<\/p>\n\n<p>I had wanted to write about this monitor while I worked there, but never got around to it. Now that I don&rsquo;t work there anymore, I don&rsquo;t have the code. And I didn&rsquo;t remember what clever tricks I had used. So I decided to re\u00efnvent it. My new version is portable to any POSIX system with support for <a href=\"http:\/\/concurrencykit.org\/\">Concurrency Kit&rsquo;s<\/a> atomic primitives; I seem to recall that what I&rsquo;d done in the original code was Linux-specific. I may implement system-specific versions later; POSIX&rsquo;s lack of an interface like <code>eventfd(2)<\/code> in Linux, and its lack of ability to report time slept in <code>poll(2)<\/code> on <code>EINTR<\/code> make the POSIX implementation a bit more heavyweight than it really needs to be. The remainder of this article will be a discussion of my new implementation.<\/p>\n\n<p>The notification system can exist in one of three states.<\/p>\n\n<pre><code>enum monitor_state {\n    STATE_WORKING,\n    STATE_PENDING,\n    STATE_BLOCKED,\n};\n<\/code><\/pre>\n\n<p><code>STATE_WORKING<\/code> is the initial state of the system, and is the state of the monitor when it is created. This state indicates that no waiters are currently listening to the monitor. The second state of the monitor is <code>STATE_PENDING<\/code>, which indicates that work is pending. The final state is <code>STATE_BLOCKED<\/code>, which indicates that the consumer wanted to do some work, but none was available.<\/p>\n\n<p>The structure itself is relatively simple: it contains some file descriptors for a pipe (to block when no work is pending), the state of the monitor, and an unfortunate flag as to whether the pipe needs draining (this is a side-effect of POSIX lacking anything <code>eventfd(2)<\/code>-like). It also contains a generation counter that is used to avoid having multiple producers write to the pipe to unblock a consumer. This turns out to be rather important, for reasons discussed below.<\/p>\n\n<pre><code>struct monitor {\n    union {\n        struct {\n            int r;\n            int w;\n        } pipe;\n        int fds[2];\n    };\n    uint8_t state;\n    uint8_t drain;\n    uint64_t gen;\n};\n<\/code><\/pre>\n\n<p>Creating a monitor is as you would expect:<\/p>\n\n<pre><code>struct monitor *\nmonitor_create(void)\n{\n    struct monitor *m = malloc(sizeof *m);\n\n    if (m == NULL) {\n        return NULL;\n    }\n\n    m-&gt;state = STATE_WORKING;\n    if (pipe(m-&gt;fds) == -1) {\n        perror(&quot;pipe&quot;);\n        free(m);\n        return NULL;\n    }\n\n    return m;\n}\n<\/code><\/pre>\n\n<p>Destroying a monitor is a bit of a hassle because I wanted to actually handle errors with <code>close(2)<\/code>. I had hoped this would make things idempotent such that you could retry destroying the thing when <code>close(2)<\/code> failed, but I&rsquo;m not sure what the caller would do here anyway. I might just ignore these errors anyway; I&rsquo;m not sure whether it&rsquo;s really feasible to get <code>EIO<\/code>, and I&rsquo;m not sure I care about any realistic ways to get <code>EBADF<\/code>.<\/p>\n\n<pre><code>bool\nmonitor_destroy(struct monitor *m)\n{\n    bool fail = false;\n\n    int r;\n    if (m-&gt;pipe.r != -1) {\n        do {\n            r = close(m-&gt;pipe.r);\n        } while (r == -1 &amp;&amp; errno == EINTR);\n\n        if (r == -1) {\n            return false;\n        }\n    }\n\n    m-&gt;pipe.r = -1;\n\n    if (m-&gt;pipe.w != -1) {\n        do {\n            r = close(m-&gt;pipe.w);\n        } while (r == -1 &amp;&amp; errno == EINTR);\n\n        if (r == -1) {\n            return false;\n        }\n    }\n\n    m-&gt;pipe.w = -1;\n\n    free(m);\n    return true;\n}\n<\/code><\/pre>\n\n<p>The actual cool stuff happens when we start thinking about how a waiter should behave when there&rsquo;s no mutex involved. First, some function boilerplate:<\/p>\n\n<pre><code>bool\nmonitor_wait(struct monitor *n, int timeout_ms)\n{\n    enum monitor_state cur;\n    bool rv = true;\n\n    assert(n != NULL);\n<\/code><\/pre>\n\n<p>When we enter this function, the first question we&rsquo;re trying to answer is whether any work was produced while the worker wasn&rsquo;t listening. Since we start out in <code>STATE_WORKING<\/code>, this means that if we enter the function in that state, no work is pending for us to do, and so we should block. Alternatively, if we enter in <code>STATE_PENDING<\/code>, that means work <em>does<\/em> exist, and we shouldn&rsquo;t bother blocking. If we guarantee that this function is the only function that can move the state to <code>STATE_BLOCKED<\/code> and that it always exits in <code>STATE_WORKING<\/code>, we can achieve this by doing a simple atomic compare-and-swap to move our state from <code>STATE_WORKING<\/code> to <code>STATE_BLOCKED<\/code>. If the CAS operation fails, we know that work is pending.<\/p>\n\n<pre><code>    if (!ck_pr_cas_8(&amp;m-&gt;state, STATE_WORKING, STATE_BLOCKED)) {\n        ck_pr_store_8(&amp;m-&gt;state, STATE_WORKING);\n        ck_pr_fence_store();\n        goto drain;\n    }\n<\/code><\/pre>\n\n<p>Alternatively, when the CAS operation succeeds, that means no work was available for us to complete. In this case, we need to wait for a notification; we&rsquo;ll receive this over the read side of our <code>pipe(2)<\/code>. Because we may not want to wait forever, we allow the user to specify a timeout that we pass to <code>poll(2)<\/code>.<\/p>\n\n<pre><code>    struct pollfd pfd[1] = {{\n        .fd = m-&gt;pipe.r,\n        .events = POLLIN|POLLPRI,\n    }};\n\n    errno = 0;\n    int p = poll(pfd, 1, timeout);\n    if (p == -1 &amp;&amp; errno == EINTR) {\n        errno = EAGAIN;\n        rv = false;\n        goto drain;\n    } else if (p == -1) {\n        perror(&quot;poll&quot;);\n        rv = false;\n        goto drain;\n    } else if (p == 0 || (pfd[0].revents &amp; (POLLHUP|POLLERR)) != 0) {\n        rv = false;\n        goto drain;\n    }\n<\/code><\/pre>\n\n<p>You&rsquo;ve noticed that we&rsquo;re doing <code>goto drain;<\/code> instead of ever returning anything so far. Some errors returned by syscalls we call can&rsquo;t easily be dealt with inside this code, so it&rsquo;s possible that we exit without any work to do anyway. However, this does mean that it&rsquo;s possible that someone had to write to the pipe to wake us, and there&rsquo;s data stuck there. We detect this condition and try to drain the pipe when it has been written to, even when we didn&rsquo;t need to block for work. This does mean that there are cases where we might do syscalls when we didn&rsquo;t need to, but this is only really likely to occur when there&rsquo;s a timeout. If <code>poll(2)<\/code> is told to block forever, this is only likely to happen on <code>EINTR<\/code>, or for (probably catastrophic) errors. If everything goes well here, we clear our draining flag, which will only be set again if we block.<\/p>\n\n<pre><code>drain:\n    if (ck_pr_load_8(&amp;m-&gt;drain) == 1) {\n        ssize_t r; \n        uint8_t buf;\n        do {\n            p = poll(pfd, 1, 0);\n            if (p == 0) {\n                break;\n            } else if (p == -1 &amp;&amp; errno == EINTR) {\n                continue;\n            } else if (p == -1) {\n                perror(&quot;drain poll&quot;);\n                rv = false;\n                goto ret;\n            } else if ((pfd[0].revents &amp; (POLLHUP|POLLERR)) != 0) {\n                rv = false;\n                goto ret;\n            }\n\n            do {\n                r = read(m-&gt;pipe.r, &amp;buf, sizeof buf)\n            } while (r == -1 &amp;&amp; errno == EINTR);\n\n            if (r == -1) {\n                perror(&quot;read&quot;);\n                goto ret;\n            }\n        } while (p == 1);\n    }\n\n    ck_pr_store_8(&amp;m-&gt;drain, 0);\n<\/code><\/pre>\n\n<p>Whenever we leave this function, we have to leave in <code>STATE_WORKING<\/code>. However, we do return to the caller whether we left because work was available (in which case <code>rv<\/code> is <code>true<\/code>). At some point, I&rsquo;d like to add an error API so the caller can figure out exactly <em>what<\/em> failed when this function returns <code>false<\/code> &ndash; so that&rsquo;ll probably happen before this makes it to GitHub. In any case, transitioning to <code>STATE_WORKING<\/code> whenever we return is really the big trick to making this wait-free.<\/p>\n\n<pre><code>ret:\n    ck_pr_store_8(&amp;m-&gt;state, STATE_WORKING);\n    ck_pr_fence_store();\n\n    return rv;\n}\n<\/code><\/pre>\n\n<p>Waking up a waiter is pretty simple. First, our function prologue&hellip;<\/p>\n\n<pre><code>bool\nmonitor_wake(struct monitor *m)\n{\n    enum monitor_state cur;\n\n    assert(m != NULL);\n<\/code><\/pre>\n\n<p>Whenever we enter this function, it is always OK to set the state to <code>STATE_PENDING<\/code>. We set this state using an atomic fetch-and-set operation, which allows us to move the state forward while also knowing what the previous state was. If the consumer is working, the state will either have already been set to <code>STATE_PENDING<\/code> by another producer, or it will still be in <code>STATE_WORKING<\/code>. In either case, we can be assured that when the consumer is ready, it will notice that we&rsquo;ve produced work.<\/p>\n\n<pre><code>    cur = ck_pr_fas_8(&amp;m-&gt;state, STATE_PENDING);\n    if (cur == STATE_PENDING || cur == STATE_WORKING) {\n        ck_pr_fence_store();\n        return true;\n    }\n<\/code><\/pre>\n\n<p>When the consumer is blocked, however, the state we read will be <code>STATE_BLOCKED<\/code>, and we will have set that to <code>STATE_PENDING<\/code>. This turns out to be just fine. If the consumer is blocked indefinitely, it doesn&rsquo;t care about what the value is until its woken up again via its pipe. If it&rsquo;s blocked on a timeout, one of a few benign scenarios can happen:<\/p>\n\n<ol>\n<li><p>The timeout occurs, and the consumer re-enters without any new work having been produced. The state will remain <code>STATE_WORKING<\/code>, and the consumer will block again.<\/p><\/li>\n\n<li><p>The timeout occurs, and a producer sets the state to <code>STATE_PENDING<\/code> above after the consumer sets the state to <code>STATE_WORKING<\/code>. The consumer re-enters and notices the new work. Nothing is written to the pipe, and nothing is read from the pipe.<\/p><\/li>\n\n<li><p>The timeout occurs, and several producers set the state to <code>STATE_PENDING<\/code> above before the consumer sets the state to <code>STATE_WORKING<\/code>. The producers race on updating the generation count below; the winner will write to the pipe. The consumer sets the state to <code>STATE_WORKING<\/code> and returns. No new work is produced. When it re-enters, it will set its state to <code>STATE_BLOCKING<\/code> and immediately read the value written by the producer, picking up that work.<\/p><\/li>\n\n<li><p>The same as above, but new work is produced before the consumer re-enters. The consumer enters and sees the state as <code>STATE_PENDING<\/code>. It also notices that the drain flag is set, and drains the pipe. It will have picked up the work that was produced by the subsequent producers as well as the one that wrote to the pipe. Even if there is a race on the drain flag being set, the consumer will eventually notice the drain and clean it up.<\/p><\/li>\n<\/ol>\n\n<p>None of these cases are terrible: in all cases, the consumer is eventually notified of the work. When an error occurs in a syscall, the producer is free to try again.<\/p>\n\n<pre><code>    uint64_t gen = ck_pr_load_64(&amp;m-&gt;gen);\n    if (!ck_pr_cas_8(&amp;m-&gt;gen, gen, gen + 1)) {\n        ck_pr_fence_store();\n        return true;\n    }\n\n    ssize_t r;\n    do {\n        uint8_t buf = 0;\n        r = write(m-&gt;pipe.w, &amp;buf, sizeof buf);\n    } while (r == -1 &amp;&amp; errno == EINTR);\n\n    if (r == -1) {\n        return false;\n    }\n\n    ck_pr_store_8(&amp;m-&gt;drain, 1);\n    ck_pr_fence_store();\n\n    return true;\n}\n<\/code><\/pre>\n\n<p>The system described above is wait-free when work is available, which is really the only situation in which non-blocking behavior is helpful for a monitor, since the goal is to block when no work can be done. This is ideal for concurrent batching workloads of nearly any volume. The composability of the interface means that, even for low volume systems, there&rsquo;s no chance that forgetting about implementation subtleties will result in missed notifications. And the wait-free nature means that with high-volume workloads, your producer&rsquo;s fast-path only takes the hit of a single atomic operation on average, and you will almost never incur the penalty of a syscall.<\/p>\n\n<p><span class=\"newthought\">This is a stepping stone for additional work.<\/span>\n The system presented here is really only appropriate for single-consumer workloads. In particular, multiple consumers sharing the same monitor would notice the monitor in <code>STATE_BLOCKED<\/code> upon entry to the <code>monitor_wait<\/code> function. This would cause them to erroneously think work was available, and result in spinning. Before I put this code on Github, I&rsquo;d really like to solve the multiple-consumer problem. In particular, I&rsquo;d like to have interfaces for signalling any consumer, signalling a specific consumer, and broadcasting to all consumers. I believe that this is all possible to do wait-free, assuming that the multiple-consumer versions simply slap an interface around managing N different monitors.<\/p>\n\n<p>As a final note, I haven&rsquo;t tested this code yet at all; it&rsquo;s entirely possible that it&rsquo;s broken. I&rsquo;m not writing any C in my new job, which is a bit of a bummer. This also means that I don&rsquo;t really have any use-cases for this code at the moment, making it a bit more difficult to test. I&rsquo;ll of course spend a bit more time on this before publishing anything to Github.<\/p>\n"},{"title":"A Malloc Idiom in Go","link":"https:\/\/9vx.org\/post\/a-malloc-idiom-in-go\/","pubDate":"Thu, 22 Feb 2018 19:45:00 +0000","guid":"https:\/\/9vx.org\/post\/a-malloc-idiom-in-go\/","description":"<p><span class=\"newthought\">I&rsquo;m finally writing Go.<\/span>\n Although I was very active in contributing to the project for the first two-and-a-bit years, I&rsquo;ve been pretty absent since 2012. Initially, I contributed because I am a fan of <a href=\"http:\/\/9p.io\/plan9\/\">Plan 9 from Bell Labs<\/a> and of <a href=\"https:\/\/www.freebsd.org\">FreeBSD<\/a>. I loved the idea of having a usable, CSP-inspired language, but the initial release of Go only ran on Linux and OS X. I only had a FreeBSD system at the time, so I ported the compiler toolchain, runtime, and standard library to FreeBSD (with a good bit of education on debugging from <a href=\"https:\/\/swtch.com\/~rsc\/\">Russ Cox<\/a>).<\/p>\n\n<p>However, most of my work has been on low-latency systems software &ndash; mostly written in C &ndash; and FreeBSD has not been supported by any of my employers since 2007. Since I wasn&rsquo;t really getting a chance to write new software in Go, and I eventually was no longer interested in the overhead of maintaining knowledge of an OS I was only using for fun, my use of and contributions to Go fell by the wayside.<\/p>\n\n<p>Now that I work at Google, I&rsquo;ve finally had some opportunities to write code in Go. While I still enjoy the language, there are a few <a href=\"https:\/\/github.com\/golang\/go\/wiki\/ExperienceReports\">experience report<\/a>-style things that ended up keeping me from using the language for the last 5-6 years, and things I currently find a little cumbersome. At the suggestion of some colleagues, I thought I&rsquo;d document at least one of them.<\/p>\n\n<p><span class=\"newthought\">One thing I miss<\/span>\n is something like C&rsquo;s <code>malloc<\/code> idiom. In C, allocated memory generally comes from calls to <code>malloc<\/code>-family functions, which give you at least enough memory to fit at least one of the thing you want, or none at all. The idiom for doing this looks like:<\/p>\n\n<pre><code>T *p = malloc(sizeof *p);\n<\/code><\/pre>\n\n<p>Notice how the type of p (<code>T *<\/code>) only appears once. This line of code exploits how <code>sizeof<\/code> behaves when its operand appears to dereference a pointer &ndash; which isn&rsquo;t what happens, because the result of <code>sizeof<\/code> must<label for=\"error: cannot access positional params by string name\" class=\"margin-toggle sidenote-number\"><\/label>\n<input type=\"checkbox\" id=\"error: cannot access positional params by string name\" class=\"margin-toggle\"\/>\n<span class=\"sidenote\"><a href=\"http:\/\/www.elide.org\/about\/\">Kate Flavel<\/a> reminds me that this is not true for VLA types, which are so useless, I always forget. Such a type has its expression evaluated.<\/span> \n always be discernable at compile-time. What the language defines instead for this syntax is that <code>sizeof<\/code> yields the size of the pointed-to object; it doesn&rsquo;t turn into some runtime thing. The nice thing about the C idiom is that if I change the type represented by <code>T<\/code>, I only change the type in the declaration or definition. No changes need to be made in the site(s) the pointer object is assigned to the result of the allocation. While the example above is trivial, consider a more complicated case where a structure member points to some type:<\/p>\n\n<pre><code>struct set {\n    size_t cap;\n    size_t nmemb;\n    int members[];\n}\n\nstruct set *\nset_create(size_t sz)\n{\n    struct set *n = malloc(sizeof *n + (sz * sizeof *n-&gt;members));\n    if (n == NULL) {\n        return NULL;\n    }\n\n    n-&gt;cap = sz;\n    n-&gt;nmemb = 0;\n\n    return n;\n}\n<\/code><\/pre>\n\n<p>If, later, we want to change <code>struct set<\/code> to support members with other than <code>int<\/code>, we might change <code>members<\/code> to a union, and add some <code>enum<\/code> specifying the fields we wanted to add. And we can do this without changing <em>any<\/em> of our code in <code>set_create<\/code>.<\/p>\n\n<p>This has bitten me in Go literally every time I&rsquo;ve created some <code>struct<\/code> type that embeds a thing that must be allocated, like a slice or a map. In Go, we are forced to repeat ourselves in expressing the type of thing we&rsquo;d like to allocate, despite that type being well known to the compiler, and type inference is so idiomatic (consider expressions like <code>a := b<\/code>), I sometimes have to dig to figure out what the type of a thing is. Let&rsquo;s look at what&rsquo;s involved in creating a struct with a map embedded:<\/p>\n\n<pre><code>type NamedMap struct {\n    name string\n    m    map[string]string\n}\n\nfunc NewNamedMap(name string) *NamedMap {\n    return &amp;NamedMap{name: name, m: map[string]string{}}\n}\n<\/code><\/pre>\n\n<p>We could alternatively use <code>make<\/code> in <code>NewNamedMap<\/code>, but we&rsquo;re still left with <code>return &amp;NamedMap{name: name, m: make(map[string]string)}<\/code> &ndash; again, repeating the type. With well-thought-out code, there should be only one (additional) place where we need to specify the type to allocate it, but this still requires touching multiple points of code when types change. This bites me when I&rsquo;m prototyping and haven&rsquo;t thought fully about the amount of state I need to keep in a map. I&rsquo;ve found myself needing to change <code>map[string]string<\/code> to <code>map[string]T<\/code> on several occasions, and it bugs me every time I have to change more than one line.<\/p>\n\n<p>One could argue I should think more about what I need before writing code, and that&rsquo;d be fair. I&rsquo;d still counter that it&rsquo;s not uncommon for additional state requirements to develop within a project&rsquo;s lifetime, like in my <code>set<\/code> example above. It&rsquo;s also possible that constraints of a system change over time such that a type that was perfectly fine initially eventually becomes unusable. In Go, the <code>set<\/code> structure might look like this:<\/p>\n\n<pre><code>type Set struct {\n    nmemb   int\n    cap     int\n    members []int\n}\n\nfunc NewSet(sz int) *Set {\n    return &amp;Set{cap: sz, members: []int{}}\n}\n<\/code><\/pre>\n\n<p>You might ask why I&rsquo;m not just using a slice, and the answer is that this is a short example for demonstrating the problem. Anyway, we may later want to support a slice of different types, and we run into the same problem we had previously. With a slice, we might be able to ignore the initialization, assuming we only ever read from the slice after appending to it. With e.g. maps and channels, the situation is more complicated because we really <em>must<\/em> allocate before use. So it&rsquo;s not that uncommon to repeat type information <em>somewhere<\/em>.<\/p>\n\n<p>I&rsquo;m not entirely sure how to go about solving this. For compound literals, it perhaps it would be possible to add a syntax like:<\/p>\n\n<pre><code>return &amp;Set{cap: sz, members: Set.members{}}\n<\/code><\/pre>\n\n<p>If you had a pointer to a slice for whatever reason:<\/p>\n\n<pre><code>return &amp;Set{cap: sz, members: &amp;Set.members{}}\n<\/code><\/pre>\n\n<p>I don&rsquo;t know if I like these compound literal syntaxes. Fixing <code>make<\/code> to support the <code>sizeof<\/code> behavior in C feels more expressive and obvious:<\/p>\n\n<pre><code>return &amp;Set{cap: sz, members: make(Set.members)}\n<\/code><\/pre>\n\n<p>But maybe this is just me programming in C for too long.<\/p>\n\n<p>I don&rsquo;t know that it is useful or valuable to change <code>new<\/code> to have similar behavior; I suspect its use is uncommon enough that it is not. In any case, it does seem clear to me that this would reduce the overhead of refactoring software, and of writing new software.<\/p>\n\n<p><span class=\"newthought\">This particular issue<\/span>\n isn&rsquo;t so bad, but fixing it would definitely make my life better. Go is already much more expressive than C in many cases, and than C++ (which I still can&rsquo;t stand to learn). I think if support for type inference in allocation was added to the language, it&rsquo;d become more appealing to C systems programmers who are still holding out for reasons other than GC. (Personally, I&rsquo;d also like to see a better story around support for system-level concurrency, but that&rsquo;s best left for a separate post.)<\/p>\n\n<p><span class=\"newthought\">I&rsquo;ve edited this article<\/span>\n to fix an error in the C example. When I originally wrote the example, the <code>struct set<\/code> did not make use of flexible array members. Anmol Sethi wrote to ask about this feature, and pointed out that I was erroneously allocating and assigning to the FAM again. I had forgotten to remove that code. Oops.<\/p>\n"},{"title":"Rote Learning in CS","link":"https:\/\/9vx.org\/post\/rote-learning-in-cs\/","pubDate":"Sun, 20 Aug 2017 00:59:00 +0000","guid":"https:\/\/9vx.org\/post\/rote-learning-in-cs\/","description":"<p><span class=\"newthought\">Does rote learning<\/span>\n have a place in computer science education? In <a href=\"https:\/\/www.infoq.com\/presentations\/debugging-mindset\">my talk<\/a> at QCon, I stated:<\/p>\n\n<blockquote>\n<p>[Learning through discovery] is so much better than this typical, learn-by-rote method of teaching that we all sort of think of as teaching&hellip;<\/p>\n<\/blockquote>\n\n<p>I just read a <a href=\"https:\/\/zedshaw.com\/2017\/04\/24\/copying-repetition\/\">blog post<\/a> by Zed Shaw suggesting that rote learning is basically required to become skillful in the field. I came into the article wholly skeptical &ndash; I haven&rsquo;t always been a fan of what Zed writes, and my own research seemed to contradict what I thought the thesis would be. After reading it, I maintain that rote learning is useless as a teaching strategy. So I was surprised to find that I actually agreed with large parts of Zed&rsquo;s post. What gives?<\/p>\n\n<p>Consider for instance this sentence from Zed:<\/p>\n\n<blockquote>\n<p>The problem with [the fear of rote learning] is that nearly every creative thing you do requires rote practice.<\/p>\n<\/blockquote>\n\n<p>Now, let&rsquo;s compare that sentence to what I said:<\/p>\n\n<blockquote>\n<p>To gain skill, we have to practice at the edge of our ability.<\/p>\n<\/blockquote>\n\n<p>Zed and I agree on this idea that practice is required to gain skill &ndash; although if you bring in some context, it might not appear that we do. We coincidentally both contextualize this around learning guitar. Zed writes, &ldquo;The idea that I\u2019m going to learn the major scale on a guitar by just learning the concept of a major scale is laughable.&rdquo; I think it&rsquo;s entirely possible for someone to conclude I disagree based on what I said:<\/p>\n\n<blockquote>\n<p>[F]or example, I\u2019ve played guitar for twenty-five years. And I suck.<\/p>\n\n<p>And fundamentally, this is because I rarely challenge myself. I learn some song or &ndash; the coolest thing I ever learned was some sweep picking pattern, and I can\u2019t even do it very fast. But I rarely challenge myself to do something new. We don\u2019t get better at guitar by playing one chord or plucking one string time after time after time. In fact, I don\u2019t think that would even help us master that chord because you\u2019ve got to do transitions in and out of it, et cetera.<\/p>\n<\/blockquote>\n\n<p>While it sounds like we&rsquo;re saying very different things here, I don&rsquo;t think we are. Undoubtedly, mastery of guitar requires repetition. You&rsquo;re going to play scales a lot. I mean, <em>a lot<\/em>. I&rsquo;ve played them <em>a lot<\/em>, and I&rsquo;m still terrible. I&rsquo;m so bad, I have exactly 0 positions memorized. I could probably noodle out major\/minor\/blues scales given some time, but it wouldn&rsquo;t be pretty. Why? Part of this is that my practice is inconsistent; I&rsquo;ll speak more about what kinds of practice are effective in the closing paragraphs.<\/p>\n\n<p>In my own context, I seem to be saying that practice doesn&rsquo;t help you learn guitar at all. But this isn&rsquo;t at all what I meant &ndash; I really failed to communicate clearly. I meant that playing guitar isn&rsquo;t really a single skill. You aren&rsquo;t good at guitar simply when you can play a single chord or a single song. If you practice one single thing until your death, playing that one thing absolutely perfectly every time, you&rsquo;ve mastered exactly 0% of guitar. The greats in guitar have skills outside the realm of guitar playing as well as a mastery of the instrument. They can improvise. They can play many different styles of music. They&rsquo;re <em>fundamentally diverse<\/em> in their skillset.<\/p>\n\n<p><span class=\"newthought\">Zed goes on<\/span>\n to tie his ideas into CS education. He writes:<\/p>\n\n<blockquote>\n<p>If you\u2019re imagining yourself at 12 trying to learn to code, then I\u2019m betting you had either a book or website with code that you copied and made work. This should just be how we start people in programming, and not the current method of conceptual &ldquo;weed out&rdquo; classes.<\/p>\n<\/blockquote>\n\n<p>I identify closely with this hypothetical. As a kid, I remember copying programs out of books on QBasic and making simple changes to try to understand how things worked. I do think we need to be careful about making an assumption this is the only way people could successfully learn to program &ndash; most of the people I know who learned this way were white, middle-class boys. I would also note that I was a pretty terrible programmer for a really long time until I had people mentoring me, and I thought I was a much better programmer than I actually was until years after I had people who were mentoring me. I&rsquo;m not entirely sure that I agree this is an effective way to start. It took me about 10 years of professional programming to get anywhere close to being something that most folks would call good, and I&rsquo;m positive that a more formal grounding in computer science would have helped.<\/p>\n\n<p>I suggest this caution specifically because I&rsquo;m not a fan of Zed&rsquo;s LCTHW book for <a href=\"https:\/\/kellett.im\/a\/hardway\">many<\/a> <a href=\"http:\/\/hentenaar.com\/dont-learn-c-the-wrong-way\">reasons<\/a>. The book no longer appears to be freely available, but in a previous iteration, it contained a section called &ldquo;Make is your new Python.&rdquo; While factually incorrect, this section title is an example of Meaningful Learning, the idea that we gain new knowledge by building and expanding upon things we already know. I generally think this is a good idea; it&rsquo;s why I <a href=\"https:\/\/9vx.org\/post\/pointers-on-pointers\/\">suggest using mail as a metaphor when discussing indirection in C<\/a>.<\/p>\n\n<p>But Tim also points out, &ldquo;What if the reader has no experience with Python at all, and doesn&rsquo;t even know what it is? The author is making an assumption here that not only is the reader a novice in terms of C, but Python also.&rdquo; I think this is a fair criticism. This assumption of knowledge is why, although I identify with the hypothetical scenario of learning to program at age 12, I think we should be careful in assuming its validity for the purposes of teaching others. If that knowledge is not foundational (and is potentially exclusive), we may continue with the shitty status quo in diversity in our field. This is why I also said we need to operate from curiosity: we have to figure out where it is to start teaching folks.<\/p>\n\n<p>That all said, I agree that the &ldquo;weed out&rdquo; classes are problematic: they challenge people without promoting an incremental self-theory. They&rsquo;re basically designed to be anti-diversity machines. They&rsquo;re designed to be this sort of pedagogical manifestation of laziness by instructors who don&rsquo;t actually want to help people learn.<\/p>\n\n<p><span class=\"newthought\">Anyway,<\/span>\n this bit from Zed&rsquo;s post is what really spurred me to write this post:<\/p>\n\n<blockquote>\n<p>I think the main reason why this is ignored or vilified in CS is the same reason that most programmers simply can\u2019t teach: They are so far removed from their beginner experience that they forget that they actually learned to code via rote learning.<\/p>\n<\/blockquote>\n\n<p>Compare again this statement to what I said about the pedagogy of debugging:<\/p>\n\n<blockquote>\n<p>As we gain experience, we forget what it was like to learn a thing, and we forget even more the more experience we gain.<\/p>\n<\/blockquote>\n\n<p>Zed doesn&rsquo;t appear to be stating that repetitive practice should be the only way that CS education happens, but rather that it should be explored as part of it.<\/p>\n\n<p><span class=\"newthought\">Is repeated practice<\/span>\n entirely absent in CS education? Programs that are heavy on lab projects effectively promote such practice by having students use their new knowledge to solve multiple different problems. I think this effectively merges the repetitive practice aspect with the creative learning aspect. So I don&rsquo;t agree entirely that practice is globally &ldquo;ignored or vilified,&rdquo; we just need to look at how it actually manifests in some curricula.<\/p>\n\n<p>And how does it manifest? I was talking to a high school programming class earlier this year. As part of my presentation, I illustrated how problems that don&rsquo;t at all look similar actually end up being the same problem. The example I gave started with Scrabble: we need some kind of dictionary structure to look up words. It turns out this is exactly the same problem that you face if you want to implement key-based purging in a content delivery network. By understanding how to handle a Scrabble dictionary, I was able to implement the Surrogate Keys feature at Fastly.<\/p>\n\n<p>So we find repetition in solving lots of related problems; this is why programs heavy in lab work can be successful. And we make that less boring (which is the main complaint against this pedagogical practice of rote learning, outside of its total lack of efficacy) by being creative in the tasks that we have folks solve. This seems similar to Zed&rsquo;s suggestion to engage in &ldquo;&hellip;training that involves a mixture of rote (scales, chords, ear training) followed by copying and modifying (learn a song and try to improvise).&rdquo;<\/p>\n\n<p><span class=\"newthought\">Was I off-base<\/span>\n in my talk? Do I agree with Zed? I wasn&rsquo;t off-base, and I think I agree with Zed, but the reason is because of semantics. You may&rsquo;ve noticed that I keep saying &ldquo;repetitive practice&rdquo; and not &ldquo;rote.&rdquo; The word &ldquo;rote&rdquo; means a form of mechanical, habitual, unthinking practice. This isn&rsquo;t what I&rsquo;m talking about. It doesn&rsquo;t seem to be what Zed is talking about. I really don&rsquo;t agree that rote learning works, and I don&rsquo;t think he actually does either. It doesn&rsquo;t jive with his own examples of learning guitar and painting: skills he is motivated to practice deliberately.<\/p>\n\n<p>When I say &ldquo;learn-by-rote,&rdquo; I really do mean &ldquo;rote&rdquo; in this mechanical, mindless context. This is not at all effective; there&rsquo;s <a href=\"https:\/\/medium.com\/@finleyt\/why-rote-learning-doesnt-work-and-what-does-work-4d890d7ca916\">significant, neurological evidence<\/a> to support the idea that it is ineffectual. As a quick aside, <a href=\"https:\/\/ww2.kqed.org\/forum\/2017\/05\/24\/nobel-laureate-carl-wieman-wants-to-to-end-the-college-lecture\/\">a fantastic segment<\/a> earlier this year on KQED&rsquo;s Forum program explored the problems with lecture-based education, in an interview with Nobel laureate Carl Wieman. (Shameless self-promotion: I called into this program at 16:49.) I think it&rsquo;s relevant because lectures are basically unengaging, mechanical, habitual, unthinking ways of throwing entirely too much information at a person and having them remember none of it.<\/p>\n\n<p>Finally, it&rsquo;s not just repetition in practice that is required to gain skill in a craft. Peter Norvig&rsquo;s <a href=\"http:\/\/norvig.com\/21-days.html\">Teach Yourself Programming in Ten Years<\/a> cites numerous papers in explaining that <em>deliberation<\/em> is also required:<\/p>\n\n<blockquote>\n<p>The key is deliberative practice: not just doing it again and again, but challenging yourself with a task that is just beyond your current ability, trying it, analyzing your performance while and after doing it, and correcting any mistakes. Then repeat. And repeat again. There appear to be no real shortcuts&hellip;<\/p>\n<\/blockquote>\n\n<p>Based on his post, I don&rsquo;t think Zed actually believes that repetitive, undirected, automatic practice builds skill. Past experience reading Zed&rsquo;s responses to people who argue with him about semantics haven&rsquo;t been pleasant, so I don&rsquo;t really want to engage in a semantic argument. However, I do wonder whether this incorrect terminology is the reason he thinks that &ldquo;rote learning&rdquo; is such a taboo in education. Ultimately, I think we&rsquo;re both talking about the same thing, and I agree that it works.<\/p>\n\n<p><span class=\"newthought\">Edit:<\/span>\n I somewhat tongue-in-cheek tagged Zed when posting this article on Twitter. He replied and, far from a flame war, we had a <a href=\"https:\/\/twitter.com\/dhobsd\/status\/899116752323264512\">pretty interesting discussion<\/a>. Zed does mean &ldquo;mindless&rdquo; when he says &ldquo;rote&rdquo; &ndash; but it&rsquo;s more of a Buddhist, meditative interpretation of the word. I think I know what he means; I can understand how a sort of culturally different view on &ldquo;mindlessness&rdquo; might be beneficial in learning. Western culture has a romance with the idea of smart, and smart means thinking. As such, learning is often stressful. This makes the sort of meditative version of &ldquo;mindlessness&rdquo; difficult to attain, if at all attainable given the stress on children to perform academically. This was definitely an area of unconscious bias for me, and I&rsquo;m happy to have learned.<\/p>\n"},{"title":"Go Go Gadget Co-Ro","link":"https:\/\/9vx.org\/post\/go-go-gadget-co-ro\/","pubDate":"Mon, 05 Jun 2017 16:00:00 +0000","guid":"https:\/\/9vx.org\/post\/go-go-gadget-co-ro\/","description":"<p><span class=\"newthought\">I like coroutines.<\/span>\n The other day on my team&rsquo;s Slack channel, someone introduced Adam Dunkels&rsquo; <a href=\"http:\/\/dunkels.com\/adam\/pt\/\">protothreads<\/a>. Either I hadn&rsquo;t seen these before or had forgotten them, but it looked cool. A short conversation ensued, and at some point a colleague offered:<\/p>\n\n<blockquote>\n<p>i can&rsquo;t ever decide if i like [coroutines] or not. so ignoring that, i think there&rsquo;s a decent heuristic: something like tatham\u2019s coroutines will provoke a conversation every time you use it. i figure if something produces so much discussion, it&rsquo;s certainly non-obvious to understand<\/p>\n<\/blockquote>\n\n<p>This colleague is an excellent software engineer and computer scientist. She&rsquo;s definitely familiar enough with threads, and so this comment struck me as odd. Programs implemented using threads are, in my view, much more difficult to understand than those implemented atop coroutines. If you can understand how threads work, you can understand coroutines. In this post, I&rsquo;d like to describe what threads and coroutines actually are, the similarities and differences between them, and why I think coroutines provoke so much discussion.<\/p>\n\n<p><span class=\"newthought\">We&rsquo;ll start out<\/span>\n with some definitions. What are threads; what are coroutines? The easiest way I find to introduce concepts is to present definitions based on the properties of the things the word describes. So we&rsquo;ll start at a low level, and work our way up.<\/p>\n\n<p>A computer is some hardware that provides state, and can execute at least one program. A program is simply a specification for how state is changed over time. A program executed by a computer is called a process. The context in which the program is executed is called a &ldquo;thread,&rdquo; and every process contains at least one thread. Processes containing multiple threads may see those threads execute in parallel. The state provided by the computer may or may not be shared between processes, but within a process, this state is shared. Threads are also typically given some &ldquo;protected&rdquo; state: state intended for use by the thread, but not expressly prohibited from sharing between threads.<\/p>\n\n<p>As a program executes, state changes, and time progresses. These tuples of <code>(execution unit, computer state)<\/code> may be arranged over the vector of time to describe what is called an &ldquo;execution history.&rdquo; Each thread is a container of an independent <label for=\"mn-independent\" class=\"margin-toggle\">&#8853;<\/label>\n<input type=\"checkbox\" id=\"mn-independent\" class=\"margin-toggle\"\/>\n<span class=\"marginnote\">There may be dependencies in state between threads, but the execution histories exist indepentedly of eachother.<\/span>\n execution history vector. This independence allows threads to be run either serially or in parallel. Multiplexing threads within a process is indistinguishable from a hardware platform that provides no meaningful abstraction for a process, and multiplexes threads directly.<\/p>\n\n<p>So we have this hierarchy of &ldquo;computer&rdquo; and &ldquo;process&rdquo; and &ldquo;thread&rdquo; &ndash; but all of these things are just containers of independent execution histories. The computer has one or more processors that execute instructions to modify state over time. It executes processes that are comprised of one or more execution histories, which also describes what a thread is. If these are all the same thing, why in the world do we have different words for them?<\/p>\n\n<p>Each word describes the same thing, but at a different level of abstraction. This abstraction is great, because it provides us a means to approximate a new Turing machine, which means we get a little extra state to do something useful each time we abstract. So although we usually say that a computer doesn&rsquo;t contain another computer, we have VMs! Processes don&rsquo;t contain other processes, but they do contain threads &ndash; which are effectively little processes. And threads don&rsquo;t contain threads. But a thread comprising Turing-complete execution units running on an abstraction of a Turing machine can obviously implement another Turing machine. Therefore, a thread may contain another thread, just not at the same abstraction level!<\/p>\n\n<p>Our computer is our base-level abstraction for a Turing machine (we don&rsquo;t actually have one of these because nobody has infinite tape). Our process is another abstraction that allows us to pretend our computer runs multiple Turing machines at once: this is useful because we don&rsquo;t want the computation of one of them to influence the computation of another. And threads exist for effectively the same reason &ndash; they&rsquo;re useful for multiplexing the same kind of computation on discrete bits of state.<\/p>\n\n<p>Coroutines are what we get when we define an abstraction on top of threads. In a novel way, coroutines <em>are<\/em> threads, in the same way that threads are processes and processes are computers &ndash; all abstractions for a Turing machine. Coroutines recognize that the fundamental unit of execution is this tuple of <code>(execution unit, computer state)<\/code>, that multiple vectors of these can execute independently with an almost arbitrary interleaving. Furthermore, since they exist at an abstraction <em>above<\/em> threads, that abstraction should be providing something that makes them easier to understand.<\/p>\n\n<p>To understand why we might like this abstraction, let&rsquo;s take a deeper look at some of the problems with writing purely threaded software.<\/p>\n\n<p><span class=\"newthought\">Threads provide an abstraction<\/span>\n above the machine or process level, representing multiple independent execution histories. This abstraction became popular in recent years as multicore and manycore systems became ubiquitous. Threads existed even before such systems were terribly common, so what is it about the abstraction that makes them useful?<\/p>\n\n<p>Many tasks we solve with software are repetitive. We rarely try to solve tasks that involve taking a single specific input and producing a single specific output. Such a transformation only needs to occur once, and we only write software for something this specific when the input and \/ or output sets are large. In network servers, for example, requests from a client arrive that direct the software to do some work. These requests will all be very similar, and so it makes sense for us to use the same codepath to process each request. Additionally, network requests tend to take time to satisfy, and request arrival often follows a Poisson distribution which (practically) means that requests will usually occur in clusters.<\/p>\n\n<p>Let&rsquo;s say we&rsquo;re serving requests for image thumbnails. This means we&rsquo;re serving a lot of small content across a potentially large library. For simplicitly, let&rsquo;s assume a each request can only result in a single thumbnail, and every thumbnail is equally likely to be requested. A random disk seek on a fast spinning disk takes about 10 milliseconds. This seek time dominates the time we spend on the request, so we can approximate the request time as 10ms + \u03b5. The simplest solution, to serve each request from a single thread of execution as it arrives, has two consequences:<\/p>\n\n<ol>\n<li>The oldest request accumulates the latency of every request in front of it.<\/li>\n<li>We are maximally able to serve 100 requests per second.<\/li>\n<\/ol>\n\n<p>Because request arrival rate tends to follow a Poisson distribution, it&rsquo;s likely that a large number of requests experience the negative of the first point. And if we receive over 100 requests within the same 10 milliseconds, the folks with the latest requests will wait over a second for their thumbnail. In fact, even if we add more disks in a RAID configuration, we can never beat this problem, if we insist on serving requests serially.<\/p>\n\n<p>The abstraction of the thread allows us to take advantage of parallelism in hardware. Let&rsquo;s now assume we have a RAID1+0 configuration (mirrored and striped), and so our objects are stored twice, and distributed across many disks. Adding threads allows us to amortize the cost of disk seeks over multiple disks, reducing the probable latency for any individual request. Because the time for processing the request (compared to the disk seek) is \u03b5, our mental model now says that the probability of any two requests requiring the same disk is proportional to the number of disks. Let&rsquo;s say that we have 10 disks. Now with 100 clustered requests, it&rsquo;s entirely possible we can serve all of them within 100ms instead of 1s.<\/p>\n\n<p>This is fantastic for read-only and read-mostly workloads. However, when requests require us to mutate state, things go downhill quickly. As an abstraction on top of some state (either a machine or a process), threads have access to everything in the parent&rsquo;s state. Traditionally, this has meant that threads are able to share stateful things like memory that tend to be protected between instances of their parent abstraction. This is problematic when the shared state is mutable. Because multiple threads of execution may occur in parallel, some actions may occur at the same time. It&rsquo;s well known that conflicts occur when multiple threads attempt to access some shared mutable state, and one of those accesses is a write. Consider the following code:<\/p>\n\n<pre><code>#include &lt;threads.h&gt;\n#include &lt;stdint.h&gt;\n#include &lt;stdio.h&gt;\n\nstatic volatile uint64_t counter;\n#define NTHRD 2\n\nthrd_start_t\nthread(void *arg)\n{\n        (void) arg;\n        for (int i = 0; i &lt; 1000000; i++) {\n            counter++;\n        }\n        return NULL;\n}\n\nint\nmain(void)\n{\n        thrd_t tds[NTHRD];\n        for (int i = 0; i &lt; NTHRD; i++) {\n                if (thrd_create(&amp;tds[i], thread, NULL) != thrd_success) {\n                        return -1;\n                }\n        }\n        for (int i = 0; i &lt; NTHRD; i++) {\n                if (thrd_join(tds[i], NULL) != thrd_success) {\n                        return -1;\n                }\n        }\n        printf(&quot;counter: %&quot; PRIu64 &quot;\\n&quot;, counter);\n}\n<\/code><\/pre>\n\n<p>In this example, the resulting value of counter is unclear.<label for=\"error: cannot access positional params by string name\" class=\"margin-toggle sidenote-number\"><\/label>\n<input type=\"checkbox\" id=\"error: cannot access positional params by string name\" class=\"margin-toggle\"\/>\n<span class=\"sidenote\">Strictly speaking, in C11, this program invokes undefined behavior; it&rsquo;s fair to say that the program as a whole has no meaning.<\/span> \n It is entirely possible that the value output at the end of the program is any number from 1,000,000 to 2,000,000. The issue here is that code is designed to implement a protocol. In the case above, that protocol is the &ldquo;increment a counter&rdquo; protocol, which is actually broken down into three steps:<\/p>\n\n<ol>\n<li>Read the value of the counter from memory.<\/li>\n<li>Modify the value of the counter by adding one to the value read.<\/li>\n<li>Write the value of the counter back to memory.<\/li>\n<\/ol>\n\n<p>Even though this read-modify-write operation is expressed in C (and possibly at the hardware level) as a single &ldquo;increment&rdquo; instruction, it actually decomposes into these three operations. And each of them could happen interleaved with similar operations executed by other threads. If our first and second threads consistently execute each operation in lock-step with each other (112233), each loop only increments the counter by one despite having run twice.<\/p>\n\n<p>The reason that this can happen has to do partially with how computer hardware handles memory accesses, and partially due to how threads are scheduled (by the machine or an OS scheduler). Because reads from and writes to memory are expensive (when compared to the cost of performing some operations in registers), processors tend to cache these reads and writes. On a system with multiple processors, with each thread simultaneously executing on a different processor, the threads will be operating on cached copies of the memory (despite our use of <code>volatile<\/code>). If a processor did not provide an increment instruction, the assembly for <code>counter++<\/code> might look like:<\/p>\n\n<pre><code>load    %reg, (counter) ; load counter from memory into register\nadd     %reg, 1         ; add 1 to value in register\nstore   (counter), %reg ; store register back into memory\n<\/code><\/pre>\n\n<p>One property of threads is that they&rsquo;re often pre-emptable. This means that whatever is running the threads (whether the machine or an OS scheduler) may stop the process from executing while it is in-between instructions. In the case where our increment is implemented as three separate instructions, the thread could be paused between any of them. This pre-emption further exacerbates the problem of the data race.<\/p>\n\n<p>Typically, we solve this problem by using synchronization constructs. In this case, the hardware might provide us an instruction to do a synchronized increment. In other cases, we might need to use a mutex (or other serializing construct) to ensure the correctness of our implementation of the &ldquo;increment a counter&rdquo; protocol. This has its own issues (many of which are presented well in <a href=\"https:\/\/queue.acm.org\/detail.cfm?id=2492433\">Samy Al Bahra&rsquo;s article<\/a> on non-blocking synchronization) that I might cover in a separate blog post.<\/p>\n\n<p>For now, let&rsquo;s go back to our thumbnail serving example. Implemented in somewhat pseudocode, we might have the following.<\/p>\n\n<pre><code>thrd_start_t\nserve_thumbnails(void *arg)\n{\n        while (true) {\n                struct request_state *state = get_actionable_request();\n\n                if (read_request(state) ||\n                    get_thumbnail(state) ||\n                    send_response(state)) {\n                        handle_error(state);\n                }\n        }\n        cleanup(state);\n}\n<\/code><\/pre>\n\n<p>Ignoring caching, pre-emption is the primary effect that allows a threaded implementation of our thumbnail service to operate efficiently. However, if we take a deeper look, <code>read_request<\/code> and <code>send_response<\/code> are also potentially latency-intensive. Our 10ms + \u03b5 estimate wasn&rsquo;t exactly correct because although <code>get_thumbnail<\/code> is approximately bounded at 10ms, it could take us dozens or hundreds of milliseconds to read the request and send the response. During that time, the thread is consuming resources (every abstraction requires some amount of state to represent), while not providing any computational benefit. Schedulers recognize this and run something else during the time the thread is blocked waiting on input, or waiting for the output side to become ready for more data.<\/p>\n\n<p>Unfortunately, a thread consumes resources (at least memory) even when it is not being executed. Even with threads being lighter-weight than processes, their mere existence isn&rsquo;t necessarily cheap when you have a lot of them. Additionally, the cost to &ldquo;context switch&rdquo; between threads of execution is usually somewhat expensive. Context switches are pretty awful because during that time, the machine isn&rsquo;t doing anything particularly useful. It is just changing the environment around a bit to switch to a different Turing machine abstraction. When we&rsquo;re doing I\/O intensive operations, these operations might occur while we still have time to run on the scheduler (that is, we would not have been pre-empted if we had not done some I\/O task).<\/p>\n\n<p>To address this problem, we can make our I\/O operations asynchronous. In the example above, we&rsquo;ve already created an abstraction for our request state. We can use that to create a finite state machine to know what to execute at any point.<\/p>\n\n<pre><code>thrd_start_t\nserve_thumbnails(void *arg)\n{\n        while (true) {\nnext_state:\n                struct request_state *state = get_actionable_request();\n\n                switch (state-&gt;fsm_state) {\n                case STATE_READ_REQUEST:\n                        switch (read_request(state)) {\n                        case BLOCKED:\n                                queue_for_input(state);\n                                goto next_state;\n                        case ERROR:\n                                state-&gt;fsm_state = STATE_ERROR;\n                                break;\n                        case SUCCESS:\n                                state-&gt;fsm_state = STATE_GET_THUMBNAIL;\n                                break;\n                        }\n                        break;\n\n                case STATE_GET_THUMBNAIL:\n                        switch (get_thumbnail(state)) {\n                        case BLOCKED:\n                                queue_for_input(state);\n                                goto next_state;\n                        case ERROR:\n                                state-&gt;fsm_state = STATE_ERROR;\n                                break;\n                        case SUCCESS:\n                                state-&gt;fsm_state = STATE_SEND_RESPONSE;\n                                break;\n                        }\n                        break;\n\n                case STATE_SEND_RESPONSE:\n                        switch (send_response(state)) {\n                        case BLOCKED:\n                                queue_for_output(state);\n                                goto next_state;\n                        case ERROR:\n                                state-&gt;fsm_state = STATE_ERROR;\n                                break;\n                        case SUCCESS:\n                                state-&gt;fsm_state = STATE_DONE;\n                                break;\n                        }\n                        break;\n\n                case STATE_ERROR:\n                        handle_error(state);\n                        break;\n\n                case STATE_DONE:\n                        cleanup(state);\n                        break;\n                }\n        }\n\n        return NULL;\n}\n<\/code><\/pre>\n\n<p>This works, but it&rsquo;s not particularly easy to read. We no longer have a linear flow of code. There&rsquo;s tons of branching, and we have to be very careful to advance our state correctly to avoid cases where we might cause a loop in our state graph, fail to handle a state, or something else bad. There are a couple of opportunities to avoid repetition in this code, but that won&rsquo;t drastically improve its readability. The property we have lost here is called &ldquo;composability.&rdquo;<\/p>\n\n<p><span class=\"newthought\">Composability is a property<\/span>\n of a system or API that describes how easy it is to implement, consume, and understand that system or interface. Examples of composable interfaces include functions for working with common data structures like queues, hash tables, and graphs of various shapes. These structures tend to have <code>add<\/code>, <code>remove<\/code>, and <code>find<\/code> interfaces. We can &ldquo;compose&rdquo; these interfaces with relative ease to get correct structures that efficiently manage and represent our data sets or program flow.<\/p>\n\n<p>In our example above, we see that C isn&rsquo;t the best language we could possibly use to compose a finite state automaton. When a language, system, or interface becomes too difficult to put together, too difficult to read, or too difficult to understand, we say that it is not composable. We generally solve this by introducing additional layer of abstraction. For example, the wonderful library and tools provided by <a href=\"https:\/\/github.com\/katef\/libfsm\">libfsm<\/a> give us several pleasant means to compose finite state automata through languages, tools, and libraries.<\/p>\n\n<p>Let&rsquo;s look at our original example again:<\/p>\n\n<pre><code>thrd_start_t\nserve_thumbnails(void *arg)\n{\n        while (true) {\n                struct request_state *state = get_actionable_request();\n\n                if (read_request(state) ||\n                    get_thumbnail(state) ||\n                    send_response(state)) {\n                        handle_error(state);\n                }\n        }\n        cleanup(state);\n}\n<\/code><\/pre>\n\n<p>In an ideal world, this would be expressive enough to encode our state transitions. If <code>read_request<\/code> would block, it would be nice if it could simply stop its execution and retry exactly where it left off, instead of us having to design an entire machine to do that. The finite state machine we created does exactly this, but it isn&rsquo;t a very nice way to abstract the problem.<\/p>\n\n<p><span class=\"newthought\">This is precisely where coroutines help.<\/span>\n The point of the abstraction of coroutines is to offload much of this complexity into the scheduler. Unlike threads, coroutines are not pre-empted; they are instead &ldquo;cooperatively&rdquo; scheduled. That is to say, a coroutine (unlike a thread) runs until it explicitly yields its runtime to another coroutine. Like any abstraction, it requires some additional state, but this state is relatively small. Of course, we still have to &ldquo;context switch&rdquo; between coroutines, but this is a much simpler and faster operation than having the OS context switch between threads.<\/p>\n\n<p>A coroutine doesn&rsquo;t typically have a &ldquo;protected&rdquo; state like a thread does. When this is useful, this protected state is usually much smaller than that of a thread. On many machine implementations, coroutines only require a subset of machine registers to be saved and restored. Coroutines exist in a very small number of different states: running, runnable, or blocked on a resource. Because of this, multiple coroutines may execute concurrently on a single thread.<\/p>\n\n<p>Concurrency and parallelism are often confused to mean the same thing. Parallelism describes a system in which multiple independent execution histories may occur at literally the same time. This is the case with threads, where (for example) parallel independent execution histories may be executed simultaneously on multiple processors. Concurrency is weaker: it simply states that multiple independent execution histories may be interleaved such that they appear to be occurring at the same time. This is roughly the same feature operating system schedulers provide to give the illusion of multitasking many processes on the same system.<\/p>\n\n<p>Using coroutines, our original example still works just fine. We might have multiple threads of execution (so that we can utilize our hardware in parallel) with each thread multiplexing several coroutines. In this world, the complexity moves around, but it is tightly contained in a few spots. This lends itself nicely to composability. As an example, our <code>read_request<\/code> function might look like this:<\/p>\n\n<pre><code>int\nread_request(struct request_state *state)\n{\n        ssize_t r;\n\n        do {\n                r = read(state-&gt;fd, state-&gt;buf, state-&gt;bufsiz);\n                if (r == -1) {\n                        if (errno == EAGAIN) {\n                                yield_on_read(state);\n                        } else {\n                                return 1;\n                        }\n                }\n        } while (request_not_complete(state));\n\n        return 0;\n}\n<\/code><\/pre>\n\n<p>The special function <code>yield_on_read<\/code> is provided by the coroutine implementation. It snapshots the current machine state of the coroutine, updates the coroutine&rsquo;s state to become runnable when more data exists on the file descriptor, and returns to the scheduler with a lightweight &ldquo;context switch.&rdquo; Later on, when data arrives (or possibly when a timeout occurs) on that file descriptor, the scheduler returns directly into the coroutine where it left off (again with this lightweight &ldquo;context switch&rdquo;). The coroutine continues by testing <code>request_not_complete<\/code> (which will be true because the request wasn&rsquo;t completed the previous time), and will re-execute the <code>read<\/code> system call, this time guaranteed to get at least some data. In the time between this, some other coroutines may have executed. Similar things could be implemented for <code>get_thumbnail<\/code> and <code>send_response<\/code>. Indeed, we could abstract out all blocking I\/O operations into a single API such that other coroutines don&rsquo;t need to implement their own state handling in a loop like we&rsquo;ve done here.<\/p>\n\n<p>How do coroutines snapshot the machine state? This is usually done using functions like <code>makecontext<\/code> and <code>swapcontext<\/code>, or <code>getcontext<\/code> and <code>setcontext<\/code>. As previously mentioned, some machines do not require all execution context to be saved. In such cases, some assembly code to read or restore the minimal required machine state is written.<\/p>\n\n<p><span class=\"newthought\">Why aren&rsquo;t coroutines more widely used?<\/span>\n Although the resulting code often ends up being more composable, and although highly concurrent workloads like network servers tend to benefit from such an expression, there&rsquo;s a fair amount of overhead involved in implementing the underlying API and scheduling. Threads are already relatively lightweight (at least compared to processes) and already have a scheduler to run them. Much of the time, the overhead of implementing APIs and scheduling is not seen as justification for the resulting composability and scalability win.<\/p>\n\n<p>In my opinion, this is the reason that coroutines provoke a discussion. It looks like a difficult technical discussion about how complicated writing a scheduler is, but really it boils down to a discussion about where and how to hide complexity. It seems clear to me that the bulk of code written using coroutines is less complicated, and the composability benefits are huge.<\/p>\n\n<p>But in some cases, coroutines have significant drawbacks. When part of a cooperatively scheduled task has high computational overhead, do we yield before performing the computation? All other coroutines are blocked while one is executing, so we need to be aware of this tradeoff. If we want to take advantage of parallelism with coroutines, we also face more challenges. If several coroutines are working with some shared state <em>and<\/em> those coroutines can yield in the middle of something that should logically be a critical section, we still need locking. Problematically, if we also have added multiple threads on which to execute coroutines, this means that we need to introduce some kind of locking protocol. Several more corner cases like these exist, and none are particularly trivial to solve. All add complexity to the scheduler and have runtime overhead.<\/p>\n\n<p>It&rsquo;s frequently safer and easier to go with the devil you know. And so we have long conversations (and blog posts) about coroutines instead of actually using them.<\/p>\n\n<p>That all said, the popularity and reasonably good performance of the Go programming language in the systems and network programming space shows that it is possible to get coroutines right.<\/p>\n\n<p>If you&rsquo;re interested in implementations or adopting coroutines into your work, there are several great resources. My two favorites (apart from homegrown implementations) are <a href=\"http:\/\/libmill.org\/\">libmill<\/a>, which is a library that provides Go-style concurrency for C, and <a href=\"https:\/\/swtch.com\/libtask\/\">libtask<\/a> provides coroutines as well as appropriate non-blocking file and network I\/O interfaces as a portable version of Plan 9&rsquo;s concurrent programming interface.<\/p>\n\n<p><span class=\"newthought\">I&rsquo;ve avoided covering<\/span>\n numerous related topics. In particular, message passing in a threaded environment combines the simplicity of existing pre-empted thread scheduling with the composability of data structure operations. In fact, message passing is a fantastic way to implement notification queues for systems built on either threads or coroutines (or both). Like any abstraction, the devil is in the details. Message passing in threaded environments moves the complexity away from the scheduler and the threading code, but into the algorithms and data structures used to pass messages. In environments with extremely high thread counts, this can be a non-starter when using an unmanaged language like C. Anyway &ndash; it&rsquo;s an idea for another blog post!<\/p>\n"},{"title":"Discovering Undefined Behavior","link":"https:\/\/9vx.org\/post\/discovering-undefined-behavior\/","pubDate":"Mon, 17 Apr 2017 17:00:00 +0000","guid":"https:\/\/9vx.org\/post\/discovering-undefined-behavior\/","description":"<p><span class=\"newthought\">The C language is infamous<\/span>\n for undefined behavior. Fantastic, multi-part articles by <a href=\"http:\/\/blog.llvm.org\/2011\/05\/what-every-c-programmer-should-know.html\">Chris Lattner<\/a> and <a href=\"https:\/\/blog.regehr.org\/archives\/213\">John Regehr<\/a> have made their rounds over the years, describing several instances of undefined behavior. In particular, they describe how compilers are allowed to assume it does not occur in correct C programs, and therefore make optimizations that seem to remove correctness from programs. The intent of these posts seems to be to educate people that they should be aware of what consitutes undefined behavior.<\/p>\n\n<p>But this isn&rsquo;t always so easy.<\/p>\n\n<p><span class=\"newthought\">When does<\/span>\n undefined behavior occur? The <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg14\/www\/docs\/n1570.pdf\">C11 draft<\/a> formalizes this in \u00a74p2:<\/p>\n\n<blockquote>\n<p>If a \u2018\u2018shall\u2019\u2019 or \u2018\u2018shall not\u2019\u2019 requirement that appears outside of a constraint or runtime-constraint is violated, the behavior is undefined. Undefined behavior is otherwise indicated in this International Standard by the words \u2018\u2018undefined behavior\u2019\u2019 or by the omission of any explicit definition of behavior. There is no difference in emphasis among these three; they all describe \u2018\u2018behavior that is undefined\u2019\u2019.<\/p>\n<\/blockquote>\n\n<p>Of note should be the text stating that undefined behavior occurs when &ldquo;any explicit definition of behavior&rdquo; is omitted. What happens if you try to run a C compiler on cheese? This is undefined by omission, and therefore this implicit undefined behavior becomes explicit.<\/p>\n\n<p>Maybe that doesn&rsquo;t sound so bad, but it results in tons of confusion amongst programmers and compiler implementors. For example, did you know that <code>printf(&quot;%p&quot;, (void *)NULL);<\/code> yields undefined behavior? This is because \u00a77.1.4p1 states, &ldquo;If an argument to a function has &hellip; a null pointer, &hellip; the behavior is undefined.&rdquo; Later on, in the specification of the <code>printf<\/code> family, no such exception appears.<\/p>\n\n<p>This might seem innocuous, but it&rsquo;s maybe more obviously problematic if you consider a case like <code>printf(&quot;%s\\n&quot;, s);<\/code>, where <code>s<\/code> is <code>NULL<\/code>. <code>Printf<\/code> functions have relatively high overhead: they&rsquo;re variadic functions, which tend to be more expensive to call. They also contain format strings, which must be parsed at runtime. Some compilers therefore optimize calls like <code>printf(&quot;%s\\n&quot;, s);<\/code> to <code>puts(s);<\/code>, which are functionally equivalent. This is allowed because of the statement in \u00a77.1.4p1, but pisses folks off because most <code>printf<\/code> implementations will output something like <code>(nil)<\/code> if <code>s<\/code> is <code>NULL<\/code>. <code>Puts<\/code>, on the other hand, probably crashes trying to dereference a null pointer.<\/p>\n\n<p><span class=\"newthought\">You might think<\/span>\n all cases of undefined behavior have been discovered. I&rsquo;d be surprised if this was true. The &ldquo;undefined by omission&rdquo; case allows for all sorts of things that are so circuitous to understand, it makes you wonder how the language works at all. For example, after a somewhat long discussion in ##C on Freenode, we came to the conclusion that there&rsquo;s undefined behavior hidden in bit-fields. To get here, you have to jump through several hoops:<\/p>\n\n<p>First of all, \u00a76.7.2p5 states that an implementation may choose whether a bit-field of type <code>int<\/code> is represented as <code>signed int<\/code> or <code>unsigned int<\/code>. Given the following code,<\/p>\n\n<pre><code>struct ex {\n    int bf : 1;\n};\n<\/code><\/pre>\n\n<p>we might expect to be able to represent the values <code>-1<\/code> and <code>0<\/code>. Or maybe <code>0<\/code> and <code>1<\/code>. Or maybe <code>-0<\/code> and <code>0<\/code>. But none of these are actually possible, because \u00a76.2.6.2p2 defines signed integers as &ldquo;containing exactly one sign bit,&rdquo; the purpose of which is to modify the value of the variable with either <em>sign and magnitude<\/em>, <em>two&rsquo;s complement<\/em>, or <em>ones&rsquo; complement<\/em> semantics. However, in the above example, <code>ex.bf<\/code> clearly has no value bits to modify if it is represented as a signed integer: there is only enough space to represent the sign bit.<\/p>\n\n<p>The spec could clear this up by specifying in \u00a76.7.2.1 that bit-fields of signed type must include enough space for at least one value bit. Or specifying something similar at or around \u00a76.2.6.2, where the representation of signed integers is discussed. As it stands, this is the kind of thing you end up running into on accident.<\/p>\n\n<p><span class=\"newthought\">I don&rsquo;t think<\/span>\n it&rsquo;s great that programmers should be left to &ldquo;discover&rdquo; undefined behavior. To that end, I think it&rsquo;d be great if things like this were spelled out more completely in the standard, or if Annex J.2 was considered normative. However, it seems the standard committee is fine leaving such behavior undefined by omission.<\/p>\n\n<p>What undefined behavior have you discovered? I&rsquo;d love to hear about it!<\/p>\n\n<p><span class=\"newthought\">This post was edited<\/span>\n to fix a statement about <code>printf<\/code> and <code>puts<\/code> equivalency. In particular, I had stated that <code>printf(&quot;%s&quot;, s);<\/code> was functionally equivalent to <code>puts(s);<\/code>. It is not, but <code>printf(&quot;%s\\n&quot;, s);<\/code> is. Thanks to <code>lemonade\\<\/code>` on Freenode for pointing this out.<\/p>\n"},{"title":"Help Vampires Aren't","link":"https:\/\/9vx.org\/post\/help-vampires-arent\/","pubDate":"Fri, 14 Apr 2017 23:20:00 +0000","guid":"https:\/\/9vx.org\/post\/help-vampires-arent\/","description":"<p><span class=\"newthought\">Help vampires.<\/span>\n It&rsquo;s an idea apparently coined by Amy Hoy in <a href=\"http:\/\/slash7.com\/2006\/12\/22\/vampires\/\">an article she wrote<\/a> over ten years ago. The &ldquo;vampires&rdquo; described are common in open source communities. They&rsquo;re also prevalent in online chat communities like IRC, especially in channels ostensibly for help purposes. The blog post describes a real problem that people experience. Some people in these communities (usually neophytes) engage in maladaptive learning behaviors that drain the energy out of those who might be able to help them (hence the term &ldquo;help vampire&rdquo;).<\/p>\n\n<p>But I have a real problem with this terminology, the presentation of information in the article, and many of its suggestions for solving the problem.<\/p>\n\n<p><span class=\"newthought\">The tone<\/span>\n of the first half of the article is glib, but inflammatory. The article claims to be a resource one might share with a so-called &ldquo;help vampire,&rdquo; but it spends the entire first half basically lambasting these individuals. This could be solved relatively easy by reversing the order of the article, such that the helpful information is placed first and the backstory comes at the end. If this were the case, the article would read to be more genuine about wanting to help.<\/p>\n\n<p>Though the article claims that it can help people change their ways (and help others help those people change their ways), it subverts this goal entirely by first offending the person looking for help, and by training educators to recognize these people as malicious. For example, this appears in the fourth paragraph of the article: &ldquo;[T]hese vampires suck the very life and energy out of people.&rdquo;<\/p>\n\n<p>Read that several times. If you were just called a &ldquo;help vampire&rdquo; &ndash; this is the first suggestion of how to deal with such individuals &ndash; would you feel good about yourself? Would you suddenly be open to changing? Or would you be confused why someone is trying to stop you from learning something? Would you feel offended? Would you become defensive? Would you shut down? Would you feel like this community is for you? Here, at the very beginning of an article that is supposed to be helpful to people of this classification, such individuals are already vilified. This is not an effective strategy for educating people, nor is it an effective strategy for convincing people to change their behaviors. In fact, it engages in the same kinds of &ldquo;life sucking&rdquo; activities it accuses the &ldquo;vampire&rdquo; of possessing!<\/p>\n\n<p>The problem is that the whole idea of a &ldquo;help vampire&rdquo; (as specified) assumes malevolence. It completely disregards the popular aphorism of Hanlon&rsquo;s razor<label for=\"error: cannot access positional params by string name\" class=\"margin-toggle sidenote-number\"><\/label>\n<input type=\"checkbox\" id=\"error: cannot access positional params by string name\" class=\"margin-toggle\"\/>\n<span class=\"sidenote\">Hanlon&rsquo;s razor is usually expressed as &ldquo;never attribute to malice that which is adequately explained by stupidity.&rdquo; I prefer &ldquo;never attribute to malice that which is adequately explained by ignorance.&rdquo; This version is less inflammatory; folks often interpret stupidity as uncorrectable.<\/span> \n &ndash; the idea that people are more likely to be ignorant than malicious. The &ldquo;help vampire&rdquo; likely engages in their maladaptive learning strategy for one or more of the following reasons:<\/p>\n\n<ul>\n<li>It has worked for them in the past.<\/li>\n<li>They possess an entity self-theory as defined by Carol Dweck.<\/li>\n<li>They&rsquo;re under a time crunch and a quick answer is more useful than expanding their knowledge.<\/li>\n<li>They simply don&rsquo;t have enough context to know what to ask, how to ask it, or where to start learning.<\/li>\n<\/ul>\n\n<p>None of these reasons include malice, so interpreting them as malicious is incorrect. It even ends up being destructive when, for example, a person under a time crunch may intend to learn more about the problem after it is mitigated to a sufficient degree. But this person need help mitigating the problem now. They might have a deadline attached to an angry boss. If you don&rsquo;t offer help, you&rsquo;ll never know. The point is really that if the person asking for help is assumed to have a malicious intent, someone equipped to help is less likely to do so. It presents the person as an immediate enemy. This isn&rsquo;t conducive to education.<\/p>\n\n<p>These statements about being &ldquo;out to stop&rdquo; people for sucking &ldquo;life and energy&rdquo; isn&rsquo;t the only example of where malice is trained. Hoy claimed that the &ldquo;vampire&rdquo; has <em>intent<\/em>:<\/p>\n\n<blockquote>\n<p>First, to identify a victim foolish enough to attempt to answer the impossible question. Second, to distract the victim long enough to separate him from his fellows; and [l]astly, to befuddle the victim\u2019s brain while their soul is being removed through the abdominal cavity by way of a standard-issue Bendy Straw.<\/p>\n<\/blockquote>\n\n<p>This is destructive, and a direct example of assuming malice.<\/p>\n\n<p><span class=\"newthought\">An argument is made<\/span>\n that the trait of &ldquo;help vampire&rdquo; is somehow correlated to gender. This is a sensitive subject for many, especially in computer science, where we have such an unfortunate gender disparity. This is a polarizing issue in our field. As such, needlessly labeling any gender as having some undesirable traits is likely to continue polarization of the field.<\/p>\n\n<p>Most problematically, Hoy cited no research to back up this claim. She surmised (probably in jest) that this must somehow be related to some evolutionary trait, but provided no rationale for this. As such, there&rsquo;s no way to address her statement other than to suggest it is purely experiential. As such, it would be more reflective of the gender disparity in computer science than it is some undiscovered evolutionary trait. And the research backs this view up.<\/p>\n\n<p>The maladaptive learning strategies attributed to the &ldquo;vampire&rdquo; are exactly those associated with individuals with an entity self-theory.<label for=\"error: cannot access positional params by string name\" class=\"margin-toggle sidenote-number\"><\/label>\n<input type=\"checkbox\" id=\"error: cannot access positional params by string name\" class=\"margin-toggle\"\/>\n<span class=\"sidenote\">Defined by Stanford&rsquo;s Carol Dweck <a href=\"https:\/\/www.amazon.com\/Self-theories-Motivation-Personality-Development-Psychology\/dp\/1841690244\">in her book<\/a> &ldquo;Self-theories: Their Role in Motivation, Personality, and Development.&rdquo;<\/span> \n Furthermore, Dweck has also conducted research showing that girls are more likely to have an entity self-theory than men, and that this is developed early in life.<label for=\"error: cannot access positional params by string name\" class=\"margin-toggle sidenote-number\"><\/label>\n<input type=\"checkbox\" id=\"error: cannot access positional params by string name\" class=\"margin-toggle\"\/>\n<span class=\"sidenote\">Is Math a Gift? Beliefs That Put Females at Risk. Dweck, Carol S.; Ceci, Stephen J. (Ed); Williams, Wendy M. (Ed). (2007). Why aren&rsquo;t more women in science?: Top researchers debate the evidence, (pp. 47-55). Washington, DC, US: American Psychological Association, xx, 254 pp. <a href=\"http:\/\/dx.doi.org\/10.1037\/11546-004\">http:\/\/dx.doi.org\/10.1037\/11546-004<\/a><\/span> \n Women begin learning and experience the psychological effects of shitty, sexist, pseudo-aphorisms like &ldquo;women should be pretty and not smart&rdquo; at a very young age. The ideas represented by this sexism are culturally reinforced, and such statements actively promote an entity self-theory. Dweck discusses this in some depth in chapter sixteen of her book, where she states directly &ldquo;we find that [bright girls] are the group with the greatest vulnerability to helplessness.&rdquo;<\/p>\n\n<p>Therefore, I don&rsquo;t believe this claim that men are somehow more likely to be &ldquo;help vampires&rdquo; is true. In fact, I believe the evidence shows that women are more likely to engage in &ldquo;help vampirism.&rdquo;<\/p>\n\n<p>And this is what really frustrates me about bringing gender into this at all. It turns out that you can move people from an entity self-theory to an incremental self-theory, and you can do so with the same strategies, regardless of gender. Calling anyone a &ldquo;help vampire&rdquo; is a shitty thing to do, and bringing gender into it is pointless because it has no bearing on the solution.<\/p>\n\n<p><span class=\"newthought\">I don&rsquo;t mean to suggest<\/span>\n that Hoy&rsquo;s article is entirely without merit. Good suggestions are provided as to how individuals can reduce the burden of &ldquo;help vampires&rdquo; on their communities. Many of her suggestions directly reflect suggestions I might provide. Others are variations on things like engaging in Socratic exercises, or engaging in the creation of educational material. (One must be careful here: for example, &ldquo;tutorials&rdquo; that are poorly written end up encouraging copycat behaviors, and may actually prohibit learning.) The majority of the suggestions are good, and in many cases, a well-intentioned effort is better than none at all.<\/p>\n\n<p>Really the only suggestion I take issue with is the suggestion to meet &ldquo;vampires head-on.&rdquo; First of all, the help vampire doesn&rsquo;t know that they are, and probably hasn&rsquo;t heard the term before. So they&rsquo;re sent to Hoy&rsquo;s article, where they immediately learn that they are not respected and not appreciated. They become depressed, defensive, angry, and frustrated. This comes from the same portion of the article that refers to itself as a &ldquo;good resource&rdquo; for someone to become enlightened to their own &ldquo;vampy ways.&rdquo;<\/p>\n\n<p>Techniques for teaching positive learning strategies exist that don&rsquo;t require one convey offense (intentionally or otherwise). In many cases, simply asking the person to reframe their question and directly engaging in a Socratic exercise works wonderfully. <a href=\"https:\/\/9vx.org\/post\/practicing-socratic-exercises-without-patronizing\/\">I&rsquo;ve written previously<\/a> on productive ways to engage in Socratic exercises without being patronizing or putting the student on the defensive. <a href=\"https:\/\/infoq.com\/presentations\/debugging-mindset\">My talk on debugging and mindsets<\/a>, while focused on debugging, contains general-purpose approaches for helping oneself and others learn effectively. (I created <a href=\"https:\/\/9vx.org\/post\/building-a-debugging-mindset\/\">a transcript<\/a> in case watching a video isn&rsquo;t your thing.)<\/p>\n\n<p>Self-awareness is also needed. Are you too tired to help? Do you need to leave in a few minutes, and you just don&rsquo;t have the time to educate the person on all the knowledge that they clearly need to solve their problem? Don&rsquo;t help. Seriously, just don&rsquo;t do it. It&rsquo;s remarkably easy to not engage with people. All you have to do is stop typing. You&rsquo;ll avoid being drained, and the person will either get the help they need or not. And that was never really <em>your<\/em> problem, was it?<\/p>\n\n<p>The article also suggests &ldquo;weeding out the hopeless cases.&rdquo; I disagree that any case in particular is hopeless. I also don&rsquo;t think the set of people who understand the article includes the same set of people who know how to discern between a hopeless case, and a seriously frustrated individual. Certainly it&rsquo;s possible to identify toxic individuals, but not everyone is in the position to eject someone from the community (and as just mentioned, those who are in that position may not be able to discern between hopeless cases). This should really be a last-ditch effort. Removal from the community is also the most obvious solution to dealing with a problem if you&rsquo;re in a position of authority, so I&rsquo;d have preferred it be left out entirely.<\/p>\n\n<p><span class=\"newthought\">Hoy&rsquo;s article finishes<\/span>\n on a high note:<\/p>\n\n<blockquote>\n<p>I felt the need to write this because I think that people are basically good, and basically self-sufficient in the right circumstances.<\/p>\n<\/blockquote>\n\n<p>I agree with this in general.<\/p>\n\n<p><span class=\"newthought\"><em>I<\/em> felt the need<\/span>\n to write <em>this<\/em> because I keep seeing folks being called &ldquo;help vampires&rdquo; by others who have clearly misunderstood what (I think) Hoy intended. I believe that she is basically good, and that the article is well-intentioned. I think most of the suggestions in the article are basically good.<\/p>\n\n<p>As far as I can tell, &ldquo;help vampires&rdquo; are as mythological as the real thing; experiencing someone as such is as much a reflection of the mentor as the mentee.<\/p>\n\n<p>Today, Hoy&rsquo;s article is misinterpreted and misapplied &ndash; at least in my corners of the Internet. Folks are attacked with this inflammatory label after asking a simple question once or twice. They get it applied to them for not understanding how to follow logic from A to B, when they have no knowledge of A or B. It targets neophytes by definition (as they are the most likely to ask the most basic of questions about any given topic). It discourages people who suffer from impostor syndrome by reinforcing the idea that they are somehow wasting the time of those they feel they are imposing upon. A literal interpretation of what (I hope) was intended as hyperbole throughout the article puts individuals trying to learn on the defensive, and teaches would-be mentors to identify and expect malicious intent where clearly none is intended.<\/p>\n\n<p>I read the article shortly after it was written. I remember quite enjoying it when I first read it. I took the content as hyperbole, but I know that I irrationally called numerous people &ldquo;help vampires.&rdquo; And I know it colored my perception of neophytes; I had this implicit platform of knowledge that put me above these vampy creatures. Reflecting on this article after ten years, still being involved in several help and discussion communities on IRC, I feel that it might be time to take another stab at the topic of &ldquo;help vampires.&rdquo; And I&rsquo;m curious what Hoy thinks of her article ten years on.<\/p>\n\n<p>Because &ldquo;help vampires&rdquo; simply aren&rsquo;t. They&rsquo;re just people.<\/p>\n"},{"title":"Pointers on Pointers","link":"https:\/\/9vx.org\/post\/pointers-on-pointers\/","pubDate":"Thu, 13 Apr 2017 16:00:00 +0000","guid":"https:\/\/9vx.org\/post\/pointers-on-pointers\/","description":"<p><span class=\"newthought\">I&rsquo;ve been hanging around<\/span>\n in the ##c channel on Freenode (a channel for discussions about the C programming language) off-and-on for something like ten or fifteen years. One constant over that time (whatever my skill level) has been questions about pointers. They seem a topic that&rsquo;s thoroughly confusing to many, especially neophyte hobbyists and autodidacts. Recently, I witnessed an explanation of pointers that involved people in small rooms, a man with a sign that points, and a magical great uncle named Merlin.<\/p>\n\n<p>(I shit you not. No, I didn&rsquo;t get it either.)<\/p>\n\n<p>Once the person who was trying to learn was thoroughly confused, I said (tongue firmly in cheek), &ldquo;Too much indirection.&rdquo; Speaking indirectly about anything is confusing, especially when the thing you are trying to explain <em>is<\/em> indirection.<\/p>\n\n<p>Much of the material I&rsquo;ve seen on the Internet isn&rsquo;t particularly great at teaching anything about pointers to anybody. People are already asking questions after having read their books. If 45 years of existence hasn&rsquo;t yielded any obviously good methods for learning to use pointers, perhaps it&rsquo;s not surprising that folks find them difficult to teach.<\/p>\n\n<p>I&rsquo;m intending for this to be part one of several posts on education about pointers. Here, I&rsquo;ll discuss some (hopefully) best practices for teaching folks about them. Future posts will attempt to actually teach them.<\/p>\n\n<p><span class=\"newthought\">Programming is based on abstractions.<\/span>\n We use abstractions in an attempt to simplify, to avoid repetition, and to reuse code. This is great because we end up with reusable, generalized code &ndash; as long as we don&rsquo;t overdo it. Comfort with indirection is a key differentiator between novice and expert programmers (indeed, so is overuse of indirection). Indirection is about representing something very complicated in a generic fashion, and this is a skill that is developed with experience.<\/p>\n\n<p>Indirection is arguably the most powerful tool in software engineering. It is the engine that powers reusability, that drives well-designed software, that allows us to find patterns in complicated data, and then to derive a general solution for processing those data. This might be hyperbole, but there are few things more crucial to writing good software than a core understanding and appropriate use of abstraction.<\/p>\n\n<p>The need for generality also isn&rsquo;t immediately apparent to novices; they&rsquo;ve little to no foundational knowledge upon which to build new knowledge. Thinking about abstraction in the way that skilled programmers do is not very obvious (though I believe most people do it in other ways). We need to start teaching people about indirection by first illustrating why generalization, abstraction, and reuse are important concepts when writing software.<\/p>\n\n<p>But this isn&rsquo;t always easy.<\/p>\n\n<p><span class=\"newthought\">Indirection is difficult to talk about.<\/span>\n First of all, it&rsquo;s abstract. It&rsquo;s difficult to discuss abstractions concretely. It&rsquo;s difficult to discuss indirection directly. So we&rsquo;ve invented tons of jargon to help us communicate effectively about it. Learning jargon is hard because definitions often seem circular, or at least as indirect and abstract as the concept they&rsquo;re describing. For example, I&rsquo;ve seen definitions of indirection that describe &ldquo;going through&rdquo; a variable to find a value. It makes sense once you know the thing, but it isn&rsquo;t enlightening when you don&rsquo;t.<\/p>\n\n<p>Secondly, even if we did figure out some really good definitions, it&rsquo;s unclear to me whether that would be helpful. We&rsquo;d need to define numerous terms, and the person learning would have to learn them all individually before they stuck. Rote memorization is an outmoded, unhelpful teaching technique. And I believe it&rsquo;s actually harmful here because it&rsquo;s the critical thinking bit that&rsquo;s important for appropriately using indirection and abstraction in software. Memorization neither reinforces nor encourages critical thinking. Far better would be for us to teach these concepts while also encouraging such problem-solving skills.<\/p>\n\n<p>Even doing that, we&rsquo;re faced with some additional problems.<\/p>\n\n<p><span class=\"newthought\">Indirection in C is a shit show.<\/span>\n There&rsquo;s too much crammed into a tight spot. If you learn variables first from a very correct resource, you know that a variable is a name for storage. But then you get to pointers, and it&rsquo;s not clear why there&rsquo;s any difference between a pointer and a variable (because the reason is hidden behind at least one level of indirection). You have the <code>*<\/code> operator, which has three distinct meanings and has some special (possibly ambiguous-looking) behavior with the <code>sizeof<\/code> operator.<\/p>\n\n<p>If you learn about pointers, you&rsquo;ve already learned about arrays. And the first thing you learn about pointers is that they&rsquo;re actually quite similar to arrays. But then you get lost because you can&rsquo;t spot what that similarity is other than some arithmetic that is somehow not regular. So you try to get some answers in ##c. Bad decision. Now four people are explaining it to you differently (and at length), and it&rsquo;s not long until the explanation devolves into a lengthy and pointed argument about the word &ldquo;decay&rdquo; and whether it implies permanence.<\/p>\n\n<p>It&rsquo;s no wonder folks tear their hair out trying to learn this. How can we as educators and mentors do a better job teaching this subject to students?<\/p>\n\n<p><span class=\"newthought\">Begin at their level.<\/span>\n Knowledge is gained, stored, and retrieved by relating new information to past experience. Be curious about what your students and mentees already know. Don&rsquo;t start off by correcting invalid assumptions, either. Try to ask &ldquo;discovery questions.&rdquo; These are questions intended to help you to discover where the student&rsquo;s knowledge comes from (and where it starts to be incorrect) so you can start teaching from the most correct level.<\/p>\n\n<p>When you start teaching pointers, begin by teaching their motivating uses. Why would we ever want pointers? They&rsquo;re a tool for indirection, and abstraction is one of the most important things in software. But since the student is unlikely to be familiar with this either, we need to provide relevant examples of where generalization is helpful.<\/p>\n\n<p>To do this, familiarize yourself with the individual&rsquo;s current expertise. For example, if your student is comfortable with math, bring up addition. What are we actually doing when we add two numbers? How difficult would it be if the rules for obtaining a result changed for each set of numbers we had to add. If your student is comfortable with languages, bring up verb conjugation. How difficult would language be if <em>every<\/em> verb was irregular? Indirection and abstraction provide us a means to think about different things and create general rules for dealing with them.<\/p>\n\n<p><span class=\"newthought\">Once you&rsquo;ve established<\/span>\n a motivation for pointers, indirection, and abstraction, make sure the student really understands variables. You&rsquo;ll likely need to start at this level. Make sure the student understands what variables are, and all the information they describe. You can present it as a simple hierarchical set of definitions if you like:<\/p>\n\n<ul>\n<li>a variable is a convenient name for an object,<\/li>\n<li>an object is storage which may contain a value and has a lifetime,<\/li>\n<li>a value is information with a type,<\/li>\n<li>and a type is a way to understand how to interpret the information in the value.<\/li>\n<\/ul>\n\n<p>In my experience, much of the confusion around pointers has to do with a poor understanding of how variables, storage, values, and types work. The above list is short enough to fit on a slide, and easy enough to memorize without being tedious.<\/p>\n\n<p><span class=\"newthought\">Avoid using similies.<\/span>\n Similies are indirect comparisons. When explaining abstraction and indirection, being indirect is about the worst way to do it. Use metaphor instead. If, for example, you were relating indirection in C to mail (because you first figured out your student understood mail relatively well), a pointer is not <em>like<\/em> an address, it <em>is<\/em> an address.<\/p>\n\n<p>In the same vein, avoid using allegory unless you&rsquo;ve given your story significant thought and the premise is already one your student understands. A poor story featuring magical men holding signs elucidates nothing.<\/p>\n\n<p>I quite like using mail. Most people understand relatively well how mail works (snail or e), at least at a high level. We already have a shared term between pointers and mail: &ldquo;address.&rdquo; And it&rsquo;s also very much true that writing a value to a pointer is analogous to sending a message to the address. Reading a value is very much analogous to receiving a message. I think this analogy ends up working well for other fields like concurrency, networking, and distributed systems, and the fundamental reason is because it <em>describes a method of communication<\/em> which is (if you squint a little) what we are doing when we write software. It&rsquo;s nice to have a single thing to use to teach multiple concepts, both for you and the student.<\/p>\n\n<p><span class=\"newthought\">One would think<\/span>\n this would go without saying, but don&rsquo;t berate the student. In ##C, we have a contributor who is very technically correct, and offers a lot of good advice. Unfortunately, this person has a habit of doing so in a sometimes cryptic fasion. This person also frequently assumes knowledge. The result is that whenever a newbie is unable to follow this person&rsquo;s train of thought, they&rsquo;re told in colorful ways what kind of idiot they must be.<\/p>\n\n<p>Never do this; it is never helpful. If you don&rsquo;t have the time, energy, or patience to help someone learn &ndash; which must necessarily start with figuring out where you should begin teaching &ndash; don&rsquo;t do it. You will be frustrated, your student will be frustrated, and your student may lose interest in learning. Of course there is the class of person who only wants the solution book&hellip; but even here, the same applies. If you don&rsquo;t want to help someone for any reason, just don&rsquo;t.<\/p>\n\n<p>Similarly, don&rsquo;t assume that just because a student has a book, they should be able to learn from it. I&rsquo;ve seen numerous people ask, &ldquo;Where&rsquo;s your book?&rdquo; If it&rsquo;s not the right book (how were they supposed to know beforehand?), they have to get another one. This isn&rsquo;t great because most books on C aren&rsquo;t particularly cheap, and this assumes an ability or desire to spend a possibly non-trivial amount of money. Secondly, even with the &ldquo;right&rdquo; book, it&rsquo;s not guaranteed that everybody will come to the same understanding from the same material. Operating under the false assumption that they should be able to do this is likely to make your student feel stupid, which will close them to wanting to learn.<\/p>\n\n<p>Learning is the process of forming new understanding. New understanding is, very fundamentally, the relation of new information to past experience. For any given static article, book, or tutorial about pointers, there will be some set of people who can not derive the indended meaning from that work, because it has no contextual relationship to their past experience. This is precisely what makes the real-time experience so exciting! As an educator, you have the ability (and the responsibility) to figure out where to start teaching. <em>You<\/em> get to be the material that is finally successful in helping this person learn something crucial. If this is not an exciting prospect to you, it might be worth reconsidering whether you are actually interested in helping folks learn.<\/p>\n\n<p>If you&rsquo;re not interested in putting in the effort, just don&rsquo;t. You&rsquo;re ruining the experience for the people who want to do it, and you&rsquo;re ruining the experience for people who want to learn. You may even drive people away from learning, which is just an awful thing to do. And you are making it more difficult for people in the future to help your own student.<\/p>\n\n<p><span class=\"newthought\">Through exercises<\/span>\n in patience, curiosity, discovery, and compassion, we can help people learn (and be excited to learn) even the most complicated topics. Pointers are so difficult for so many people to learn that we as educators must engage in these exercises. We need to be patient in our explanation: knowledge can&rsquo;t be rushed. We need to be curious about where our students are coming from, and discover where their gaps in understanding are. In doing this, we provide them personalized context for the new information, which allows them to engage in real learning. And in being compassionate, we recognize that we also don&rsquo;t know everything, we didn&rsquo;t always have our own knowledge, and we show our students that with just a little effort, they can be like us.<\/p>\n"},{"title":"CasTTY: Screencast your Terminal","link":"https:\/\/9vx.org\/post\/castty\/","pubDate":"Fri, 10 Mar 2017 00:30:00 +0000","guid":"https:\/\/9vx.org\/post\/castty\/","description":"<p><span class=\"newthought\">An internet-friend<\/span>\n has been encouraging me\nto put together some material to help teach people stuff about software\nengineering in C and POSIX environments. I&rsquo;ve been wanting to do this sort of\nthing for some time, but the tools are always really obtuse. You have some\napplication that captures a window and makes a video, but then nobody can\nreally interact with the text.<\/p>\n\n<p>But then I remembered that <a href=\"http:\/\/0xcc.net\/ttyrec\/\">ttyrec<\/a> was a thing. I&rsquo;ve\nused it before (mostly to <a href=\"https:\/\/alt.org\/nethack\/\">watch NetHack games<\/a>), but\nhadn&rsquo;t thought much of using it as a tool for education. A few hours of work\npulling together a bunch of other peoples&rsquo; hard work, and I ended up with\n<a href=\"https:\/\/github.com\/dhobsd\/castty\">CasTTY<\/a>.<\/p>\n\n<p>CasTTY is a stripped-down version of ttyrec (in particular, it doesn&rsquo;t care so\nmuch about portability) that aims to make creating educational &ldquo;videos&rdquo; easy.\nIt comes with code to &ldquo;record&rdquo; a TTY and simultaneously records audio. Where\nttyrec just spits out some 32 bit integers with time offsets and terminal\nsequences, CasTTY spits out some Javascript containing basically the same\ninformation. Audio is recorded using <a href=\"http:\/\/portaudio.com\">PortAudio<\/a>.<\/p>\n\n<p><span class=\"newthought\">The player combines<\/span>\n\n<a href=\"https:\/\/github.com\/sourcelair\/xterm.js\/\">xterm.js<\/a>,\n<a href=\"https:\/\/github.com\/andreruffert\/rangeslider.js\/\">rangeslider.js<\/a>,\n<a href=\"https:\/\/jquery.com\/\">jQuery<\/a>, and all the new-fangled HTML5 audio stuff to\nmake an immutable (but copy-buffer-friendly) player. I&rsquo;m hoping to use this in\nthe near term to make some slightly-interactive tutorials on programming and\ndebugging.<\/p>\n\n<p>Putting this together was a snap, but I&rsquo;d like to give another hat-tip to the\ndevelopers of all the software I cobbled together for this project. Without\ntheir software, I probably wouldn&rsquo;t have done this at all.<\/p>\n\n<p>There are still bugs and numerous features I&rsquo;d like to implement, but I want to\nget some experience using it first. I was thinking of doing a dive into the\nCasTTY code as a first example of how it works &ndash; but other suggestions are\nwelcomed!<\/p>\n\n<p>Finally, if you&rsquo;d like to contribute, there are\n<a href=\"https:\/\/github.com\/dhobsd\/castty\/issues\">plenty of ideas<\/a> for ways to take the\ncode. I&rsquo;d especially appreciate help with the UI-side of things. I&rsquo;m not very\ngood at making things look very good.<\/p>\n\n<p>Enjoy!<\/p>\n"},{"title":"Some Updates","link":"https:\/\/9vx.org\/post\/qcon-talk\/","pubDate":"Tue, 07 Mar 2017 01:22:00 +0000","guid":"https:\/\/9vx.org\/post\/qcon-talk\/","description":"<p>I&rsquo;ve been avoiding updating this site for some time because I really wanted\nto use <a href=\"https:\/\/edwardtufte.github.io\/tufte-css\/\">Tufte CSS<\/a> for this site. I\nswitched my theme to the <a href=\"https:\/\/github.com\/shawnohare\/hugo-tufte\">Hugo-Tufte<\/a>\ntheme for numerous reasons. For one, I really like they layout and readability.\nBut I&rsquo;m also finding that I really want to use things like sidenotes. I&rsquo;m not\nsuper happy with how those are exposed in Hugo-Tufte, but I will grin and bear\nit until I spend some more time formalizing a Markdown-derivative that supports\nsuch things.<\/p>\n\n<p>After several long months, <a href=\"https:\/\/www.infoq.com\/presentations\/debugging-mindset\">my presentation at QConSF 2016<\/a>\nis available to watch! I&rsquo;ve also written an article for the\n<a href=\"https:\/\/queue.acm.org\">ACM Queue<\/a> on the topic in the Jan\/Feb 2017 issue.\nIt isn&rsquo;t currently available without a subscription, but will be available\n<a href=\"https:\/\/queue.acm.org\/issuedetail.cfm?issue=3055301\">at the issue page<\/a> once\nit is released for public viewership.<\/p>\n\n<p>Some new blog material is in the works. In particular, I&rsquo;ve been doing a fair\namount of work on searching data using finite state automata using my wonderful\ncolleague <a href=\"http:\/\/www.elide.org\/about\/\">Kate&rsquo;s<\/a> library,\n<a href=\"https:\/\/github.com\/katef\/libfsm\">libfsm<\/a>. As usual, my inputs are relatively\nlarge, so I&rsquo;ve put a fair amount of work into improving the performance of the\ntools, and I&rsquo;m pretty excited by what we&rsquo;re going to use them for. A quick\nexample of a FSM for IPv4 network space:<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/ip4fsm.png\" alt=\"IPv4\" \/><\/p>\n\n<p>I&rsquo;ll also be wrapping up a blog post shortly regarding pointers in C. It remains\na topic confusing to many, and I figured I should try my hand at it. I&rsquo;m looking\nforward to wrapping that up soon and hopefully writing more in the near future.<\/p>\n"},{"title":"Practicing Socratic Exercises Without Patronizing","link":"https:\/\/9vx.org\/post\/practicing-socratic-exercises-without-patronizing\/","pubDate":"Wed, 16 Nov 2016 00:23:00 +0000","guid":"https:\/\/9vx.org\/post\/practicing-socratic-exercises-without-patronizing\/","description":"\n\n<p>At my <a href=\"https:\/\/9vx.org\/post\/building-a-debugging-mindset\/\">QConSF<\/a> talk, I got a really great\nquestion:<\/p>\n\n<blockquote>\n<p>So when you were talking about the Socratic method, how do you engage in the\nSocratic method without sliding into a patronizing tone, if you know what I\nmean?<\/p>\n<\/blockquote>\n\n<p>Off the top of my head, the answer I gave focused on fostering trust. But I\nthink this is a really important question, and I ended up ruminating on it for\nsome time afterwards. I don&rsquo;t think this was the best answer, and I think it&rsquo;s\nworth talking about some more.<\/p>\n\n<p>Regardless of what Socrates actually believed about knowledge, the methodology\nhe used to help educate others stays with us today because it&rsquo;s useful and it\nworks. Briefly, it is a methodology by which one or more people intending to\nlearn are asked leading questions about a premise. The idea is that when the\n&ldquo;right questions&rdquo; are asked, the student is able to solve the problem without\nactually having been spoon-fed a solution. This is fantastic for the\nindividual(s) learning, because they get a feeling of ownership of knowledge,\nand the new knowledge they own is solidly based on things they already\nunderstand.<\/p>\n\n<p>The problem encoded in this question is that it is very easy to come across as\npatronizing in exercises like this. And the trick here is really in asking the\nright questions. Start from something that is plainly obvious, and your mentee\nfeels patronized. Start from something that isn&rsquo;t at all obvious, and you risk\nfrustrating your mentee. Figuring out what questions to ask and how to ask\nthem is <em>hard<\/em>. To engage in a successful Socratic exercise, we really have to\nget this right.<\/p>\n\n<h2 id=\"difficulties-of-socratic-dialogue\">Difficulties of Socratic Dialogue<\/h2>\n\n<p>Consider an online forum, or any other medium where folks are temporally\npresent, but may not have much context into who you are, your motivations, or\nyour expertise. If someone raises a question and is immediately met with a\nquestion in response, this discussion is unlikely to be successful. Why should\nanybody entertain your question?<\/p>\n\n<p>There are multiple reasons folks may be hostile, skeptical, or otherwise\nadverse in engaging in such a discussion. In a sort of context-free\nenvironment, you&rsquo;re operating without credentials and it&rsquo;s likely folks simply\ndon&rsquo;t know who you are. If you&rsquo;re an authority on the subject, this is not\nnecessarily obvious. The people in most need of help are going to be new to\nthe subject. This by definition means they are likely unfamiliar with who the\nexperts of a particular field actually are, and wouldn&rsquo;t be able to recognize\nyou as one anyway.<\/p>\n\n<p>This can be exacerbated by the fact that we implicitly categorize people\nasking questions as fellow learners, rather than mentors. Socratic dialogue, as\nold as it is, isn&rsquo;t a hugely popular form of teaching. So the technique remains\nunfamiliar to many. If I&rsquo;ve asked a question, and I&rsquo;m met with a question in\nresponse, it may feel natural for me to doubt whether this person is trying to\nhelp, or whether they&rsquo;re in the same boat as I am. My primary motivator is\ngaining knowledge to solve my problem, not to help someone else who is stuck\nwith possibly less information than I have. Even experienced folks are likely\nto be similarly conditioned.<\/p>\n\n<p>Finally, in the age of the Internet, there&rsquo;s plenty of misinformation and most\nfolks are clued in to this. Especially in the area of programming languages\nand technology, moderately experienced folks tend to look out for blatantly\nincorrect information. People who are experienced may just be looking for a\nquick refresher as opposed to a long-winded discussion about what they already\nknow. These folks may be more hostile towards engaging in a Socratic dialogue\nfor this reason as well.<\/p>\n\n<h2 id=\"building-trust\">Building Trust<\/h2>\n\n<p>I think building and fostering trust is the first step we need to take when\ncommunicating with anybody in any form, but it especially holds here. When\nengaging in Socratic exercises, we can&rsquo;t just start with asking questions. We\nhave to have some lead-in by which we engage in the discussion. One frequent\npitfall I&rsquo;ve observed in practicing Socratic dialogues has been the lack of\nlead-in.<\/p>\n\n<p>We can start with something like, &ldquo;Hey, I think I can help you with your\nproblem, would you mind me asking you a few questions about what you&rsquo;ve found\nout first?&rdquo; This solves nearly all of the difficulties mentioned in the\nprevious section:<\/p>\n\n<ul>\n<li>You&rsquo;ve established yourself as an authority by stating you think you can\nhelp with the problem.<\/li>\n<li>You&rsquo;ve established your willingness to help with the problem, so it&rsquo;s\nunlikely you will be misidentified as someone with a similar problem.<\/li>\n<li>You&rsquo;re setting up the individual to expect questions. This allows them to\nsee your questions as a tool for their learning as opposed to a tool for\nyou.<\/li>\n<li>You&rsquo;ve provided an opportunity for someone unwilling to engage in a\nSocratic exercise to reply with something like, &ldquo;Hey, I appreciate that,\nI know what I&rsquo;m doing, but I&rsquo;m having a brain fart right now and I really\njust need the name of this function so I can look at the manpage.&rdquo;<\/li>\n<\/ul>\n\n<p>If the person responds positively, it&rsquo;s time to engage in a Socratic dialogue.\n(Although, surprise surprise, this first question already started that\nprocess!) If they&rsquo;re cautious, they&rsquo;ve also a chance to ask you why you think\nyou can help.<\/p>\n\n<p>Some people only recognize &ldquo;help&rdquo; as being spoon-fed answers. It&rsquo;s up to you\nwhether you want to do this or not, but either engage or disengage. There&rsquo;s\nlittle point in complaining about whether a person is learning in a &ldquo;proper&rdquo;\nfashion. In fact, doing so will discredit your authority and will engender\ndistrust from the person aiming to learn. That&rsquo;s antithetical to the goal\nhere: to build trust.<\/p>\n\n<p>If someone is on the fence, or really wants to be spoon-fed information,\ncontinue to build trust. &ldquo;Ok, well it would help me to have all of the\ninformation about where you are so that I can provide you an accurate answer.\nYou said the problem is X. How did you reach that conclusion?&rdquo;<\/p>\n\n<p>Stay calm and collected. Apologize if offense is taken and reassure none was\nintended. Your goal is to educate, and being a trusted person is going to\nreduce the likelihood people see you as an elitist or authority figure (and\ntherefore more likely to take offense at your help), while increasing the\nlikelihood you are a helpful peer. Which is really what you are if you cared\nenough to read this article.<\/p>\n\n<h2 id=\"avoiding-patronizing\">Avoiding Patronizing<\/h2>\n\n<p>Once we start the meat of the process, we need to be very careful to ask\nquestions that are not just leading, they&rsquo;re guiding. Unfortunately, this is\nalso very descriptive of patronizing questions. Let&rsquo;s be clear about what\nconstitutes patronizing.<\/p>\n\n<p><a href=\"https:\/\/en.oxforddictionaries.com\/definition\/patronize\">OED defines patronizing<\/a>\nas treating another &ldquo;with an apparent kindness which betrays a feeling of\nsuperiority.&rdquo; (Kind of like their definition of that word.)\n<a href=\"http:\/\/www.merriam-webster.com\/dictionary\/patronize\">Merriam-Webster says<\/a> it\nis &ldquo;talk[ing] to (someone) in a way that shows that you believe you are more\nintelligent or better than other people.&rdquo;<\/p>\n\n<p>There are two difficulties:<\/p>\n\n<ol>\n<li><p>This is a subjective experience. With no malintent on your behalf, someone\nmay read superiority into your tone, expression, cadence, delivery,\nvocabulary, or anything else you&rsquo;re using to communicate.<\/p><\/li>\n\n<li><p>We&rsquo;re conditioned to see teachers as authority figures. This means that\nthere&rsquo;s an increased likelihood that folks will interpret superiority\nwhere none was intended, and it also means that we&rsquo;re conditioned to act\nwith superiority when attempting to mentor others.<\/p><\/li>\n<\/ol>\n\n<p>As it is a purely subjective experience, we might be tempted to gauge another&rsquo;s\nreactions to our questions to detect whether we&rsquo;re being experienced as\npatronizing. But I&rsquo;d recommend against that. First of all, it might require\nreading between the lines, which is counterproductive because it tends to add\nnon-present information; it&rsquo;s the behavior that was likely to have made you\nseem patronizing in the first place. Additionally, in text-only communication\nplatforms, there are no non-verbal cues to interpret. This makes it highly\nlikely you will misinterpret something like &ldquo;hold on a sec&rdquo; as an indicator of\nexasperation instead of as &ldquo;I really need to pee&rdquo; (for example). Finally, even\nwith trust, we are likely not qualified to determine whether they\n<a href=\"https:\/\/www.nimh.nih.gov\/health\/topics\/autism-spectrum-disorders-asd\/index.shtml\">can even understand the social cues and behaviors<\/a>\nthat might result in this assessment.<\/p>\n\n<p>We&rsquo;re most likely to be misunderstood for patronizing if we ask questions that\nhave obvious answers. If we think we might be asking such a question, the onus\nis on us to preface it with, &ldquo;I&rsquo;m sorry if this is obvious; &hellip;&rdquo; But this\ndoesn&rsquo;t really help if we genuinely don&rsquo;t know. If a response comes back that\nseems exasperated, we can apologize: &ldquo;I&rsquo;m sorry, I&rsquo;m trying to understand the\nproblem from your perspective. I didn&rsquo;t intend any condescention.&rdquo; The goal\nhere again is to reinforce trust.<\/p>\n\n<h2 id=\"avoiding-frustration\">Avoiding Frustration<\/h2>\n\n<p>Sometimes these exercises can go on for a long time. Learning can be an\narduous and time-consuming process. This is likely to result in one or both of\nyou becoming tired and frustrated.<\/p>\n\n<p>If you notice your mentee repeating previous mistakes or becoming frustrated\n(and especially if you notice yourself becoming frustrated), take a time-boxed\nbreak. Neurologically, we are <em>not<\/em> wired to learn by spending more than a few\nhours on a task (which is why methods like spaced practice \/ segmented study\nwork really well). Negotiate a break and a time when you can both get back to\nsolving the problem.<\/p>\n\n<h2 id=\"commonalities\">Commonalities<\/h2>\n\n<p>In both cases, curiosity is crucial. We ostensibly have knowledge our mentee\nlacks, which is why we are in a position to help. Yet we cannot offer this\nknowledge in a Socratic dialogue, where we are primarily asking questions.\nSomehow, we need to use this knowledge to ask the right questions, and this is\nwhy we need curiosity.<\/p>\n\n<p>In my QConSF talk, I said that curiosity &ldquo;is really about understanding where\nyour mentee or your colleagues are coming from.&rdquo; Only when we are able to\ntruly empathize with folks can we successfully employ the Socratic Method. The\nbasis of employing Socratic methodologies should always be to understand the\nmentee. We likely already know the problem and the solution, but the goal of\na Socratic exercise is to allow an individual to independently discover the\nsolution (a far more rewarding experience than the &ldquo;learn-by-rote&rdquo; method of\nteaching to which we are all accustomed).<\/p>\n\n<p>To this end, really try to put yourself in your mentee&rsquo;s shoes. Don&rsquo;t ask\nquestions that are clearly within the mentee&rsquo;s knowledge: this is blatantly\npatronizing. Don&rsquo;t ask questions that are clearly outside the mentee&rsquo;s\nknowledge: this is both patronizing and frustrating. The goal with each\nquestion should be to ask from the edge of the mentee&rsquo;s ability such that new\nknowledge is formed with the answer to each question. Understanding your\nmentee&rsquo;s existing knowledge, perspectives, and favorite learning methods is\ncrucial for effectively employing Socratic exercises.<\/p>\n\n<h2 id=\"in-closing\">In Closing<\/h2>\n\n<p>We may be avoiding employing Socratic exercises as a learning tool in our work\nand education environments because we don&rsquo;t wish to be condescending. Yet it\nis only by practicing such exercises that we can really identify how to best\nuse this method without patronizing or frustrating others. Like nearly\neverything in life, Socratic learning methods are a skill, and we only get\nbetter with practice. I hope that this post can serve to help others engage\nmore effectively in this amazing teaching tool we have to help others in their\nown lifelong journies of learning.<\/p>\n"},{"title":"Building a Debugging Mindset","link":"https:\/\/9vx.org\/post\/building-a-debugging-mindset\/","pubDate":"Sun, 13 Nov 2016 05:39:00 +0000","guid":"https:\/\/9vx.org\/post\/building-a-debugging-mindset\/","description":"<p>I <a href=\"https:\/\/qconsf.com\/sf2016\/presentation\/building-debugging-mindset\">spoke at QConSF<\/a>\nlast week about debugging. The content is largely similar to my previous blog\nposts on the topic, but I think more refined. I&rsquo;m unsure as to when the video\nof the talk will be made available on InfoQ&rsquo;s website. If you attended QConSF,\nyou <a href=\"https:\/\/www.infoq.com\/presentations\/debugging-mindset\">have early access<\/a>\nto watch the video. However, since that may be some time yet, I figured I&rsquo;d\nput the slides here with a rough transcript.<\/p>\n\n<p>I love speaking and sharing knowledge. Every time I do it, I get a bit\nnervous in the minutes before I present. I opened this one up with a favorite\njoke of mine to break the ice:<\/p>\n\n<p>An ion walks into a bar. It goes up to the bartender and says, &ldquo;Hey, excuse\nme, I think I left an electron here last night.&rdquo; The bartender asks, &ldquo;Are you\nsure?&rdquo; And the ion replies, &ldquo;I&rsquo;m positive!&rdquo;<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.001.png\" alt=\"Introduction\" \/><\/p>\n\n<p>Today, I&rsquo;m really excited to share with you a learning journey I undertook.\nI wanted to know why some people seem better than others at debugging, which\nsort of lead me to the fundamental question of why debugging is hard. What\nmakes it hard? And the underlying motivation here was that I wanted to see how\nthis thing that we spend so much of our time doing, that seems so difficult,\nhow we could make that process easier.<\/p>\n\n<p>I&rsquo;m actually going to front-roll some credits for this talk, so I want to thank\nIn\u00e9s Sombra, Elaine Greenberg, Nathan Taylor, Erran Berger, and my wife Crystal\nO&rsquo;Dell for sitting through this over multiple iterations. And also thank you\nfor coming. There are some really awesome presentations that I know are going on\nduring this time, so thanks for being here.<\/p>\n\n<p>A bit about me. I&rsquo;m Devon O&rsquo;Dell, it&rsquo;s really great to meet all of you!<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.002.png\" alt=\"Fastly\" \/><\/p>\n\n<p>I work at Fastly; I&rsquo;ve been there for about 4 years. We&rsquo;re a content delivery\nnetwork focused on near-real-time features like instant cache invalidation,\nstreaming logs, live metrics, and instant configuration changes. We also\nprovide web security and performance-related solutions for our customers.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.003.png\" alt=\"Fastly POP Map\" \/><\/p>\n\n<p>Professionally, I started doing web development in 2000. I started mixing in\nsystems programming (down at the kernel \/ hardware level) in about 2004. At\nFastly, I work on our edge caching layer. I lead a team that works on\nperformance and features. I also collaborate with other teams that build on\ntop of the infrastructure I work on.<\/p>\n\n<p>Now, performance and features are always really hard to get right the first\ntime. So, inevitably, I spend a large amount of my time on debugging-related\ntasks.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.004.png\" alt=\"Confused dho\" \/><\/p>\n\n<p>And since I spend a lot of time debugging, I also spend a lot of time being\nconfused. Debugging is hard because it&rsquo;s confusing. And that&rsquo;s really the\nthesis of this talk: debugging <em>is<\/em> hard.<\/p>\n\n<p>This talk is roughly split into two acts. In the first act, I&rsquo;m going to speak\nabout why debugging is hard, and sort of finish off with what are some of the\nqualities that make some people inherently better at the task than others. And\nin the second, I&rsquo;m going to discuss what things we can do to make everybody\nsuccessful at debugging.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.005.png\" alt=\"Act 1: Debugging is hard\" \/><\/p>\n\n<p>So: Act 1: Debugging is hard.<\/p>\n\n<p>Over the years, I&rsquo;ve asked folks what they think, why they think debugging is\ndifficult, and there&rsquo;s a real wide range of answers, right? Confusing interfaces,\nincomplete knowledge of systems, state space explosion, experience, just to name\na few.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.006.png\" alt=\"Range of experience\" \/><\/p>\n\n<p>So let&rsquo;s look at some of these, let&rsquo;s take experience. Debugging is hard because\nit&rsquo;s a skill, and experience ranges widely. But I know ridiculously senior\nengineers who seem basically &ldquo;okay&rdquo; at debugging. They&rsquo;re not great, but they&rsquo;re\nnot bad. But I&rsquo;ve also worked with novices who just seem to have a knack for it\nthat&rsquo;s sort of beyond their skill level, or what you would expect from their\nexperience. And research shows, by the way, that there is a wide range of skill,\neven amongst experts, so this isn&rsquo;t just me telling you an anecdote.<\/p>\n\n<p>Experience really can&rsquo;t be the only factor, so let&rsquo;s look at another one.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.007.png\" alt=\"Confusing interfaces\" \/><\/p>\n\n<p>Let&rsquo;s look at confusing interfaces. I work primarily on systems stuff in C and\nPOSIX environments these days. And for me, the mmap syscall is a really, really\ngreat example of a confusing interface. It has 6 arguments; I can never remember\nthe order &ndash; because it&rsquo;s not really logical, none of them sort of follow from\nthe other. I don&rsquo;t actually know any syscalls that have more arguments that I\ncan think of off the top of my head.<\/p>\n\n<p>One of the arguments is a bitmap of flags. Now POSIX specifies three or four, but\non Linux, you have like twelve. Other systems have more or fewer.<\/p>\n\n<p>Mmap can return at least seven different kinds of errors, and one those errors can\nhappen for four completely separate reasons.<\/p>\n\n<p>So mmap is hard because I can really never remember how to use it, how it fails,\nand this really means that I&rsquo;m constantly referring to the manpage every time I\nneed to use this interface. And when I have to debug it, you got it: back at the\nmanpage.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.008.png\" alt=\"Not the whole pictrue\" \/><\/p>\n\n<p>So these aren&rsquo;t really satisfying answers: experience or confusing interfaces.\nThey don&rsquo;t really give us the whole picture. And it seems like there&rsquo;s something\nmore fundamental going on about the difficulty. So to figure out what to do about\nthe difficulty, I thought I needed to understand <em>why<\/em> it was difficult,\nfundamentally. So I kept looking, and I thought that some of the answer would lie\nin the history of software engineering.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.009.png\" alt=\"Maurice Wilkes, 1949\" \/><\/p>\n\n<p>So I went way, way, way, way back. In &ldquo;Memoirs of a Computer Pioneer,&rdquo; Maurice\nWilkes talks about his experience programming the EDSAC (at Cambridge, I believe)\nin June of 1949. And now, at this time, nearly nothing about software development\nwas obvious. And at one point, Wilkes relayed this powerful realization he had that\nhe was going to spend a large portion of the remainder of his life finding errors\nin his own programs.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.010.png\" alt=\"Debugging is not obvious\" \/><\/p>\n\n<p>Now, debugging is hard because it&rsquo;s not obvious. If we think back to when we first\nstarted programming, it wasn&rsquo;t ever obvious that it was going to be hard to figure\nout what went wrong, or even that things would go wrong in the first place.\nPrograms were just these sort of magical things that mostly worked. And when they\ndidn&rsquo;t, we thought it was our fault.<\/p>\n\n<p>Bugs also have a connotation of failure. And Wilkes, in the full context of his\nquote, sort of gives a hat tip to this. I think this connotation has sort of led us\nto a culture of half-joking about how we make terrible decisions about the design\nand implementation of systems and interfaces.<\/p>\n\n<p>Debugging is hard because we&rsquo;ve created a self-deprecating culture around it. So\nnobody actually wants to do it. And I think that&rsquo;s tragic, because as other people\nin this track before me have said, we learn from failure. So every failure is at\nleast a partial success.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.011.png\" alt=\"Brian Kernighan, 1974\" \/><\/p>\n\n<p>Ok, so let&rsquo;s fast-forward twenty-five years. In 1974, Brian Kernighan and P.J.\nPlauger publish the book &ldquo;The Elements of Programming Style.&rdquo;<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.012.png\" alt=\"Kernighan's quote\" \/><\/p>\n\n<p>And in this book, Kernighan introduces this quote that I think many of you may be\nfamiliar with:<\/p>\n\n<blockquote>\n<p>Everyone knows that debugging is twice as hard as writing a program in the\nfirst place. So if you&rsquo;re as clever as you can be when you write it, how will\nyou ever debug it?<\/p>\n<\/blockquote>\n\n<p>Now taken at face value, this is pure rhetoric. All it says is that debugging is\nhard and we&rsquo;re not clever enough to debug our cleverest solutions. And this is\nactually how I see this quote used most often in context: as a joke, in jest.\nAgain with the self-deprecation.<\/p>\n\n<p>But I think it says something more. What if we looked at this question as a\nchallenge?<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.013.png\" alt=\"Challenge accepted\" \/><\/p>\n\n<p>This leads me to much more fundamental questions about the conjecture and the\npremise. Is debugging really twice as hard as programming? If so, <em>why<\/em>? And\nhow <em>will<\/em> we ever debug these programs?<\/p>\n\n<p>Let&rsquo;s just take as a given that debugging is twice as hard as programming. Where\nmight we learn more information about how to apply that practically?<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.014.png\" alt=\"Flow\" \/><\/p>\n\n<p>One place I found answers is an area of positive psychology pioneered by Mihaly\nCsikszentmihalyi; the concept of Flow. We all intuitively know what Flow is, it&rsquo;s\nthis feeling of being &ldquo;in the zone.&rdquo; Everything else is shut out and you just get\nstuff done. You look up at the clock, hours have passed. You just feel good. We\nfeel our most accomplished, our most productive, and our most fulfilled in our jobs\nor the activities we&rsquo;re engaging in when we&rsquo;re operating in this Flow state.<\/p>\n\n<p>Now, &ldquo;Flow&rdquo; is actually the technical term for this, even though it sounds a\nlittle &ldquo;watery.&rdquo;<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.015.png\" alt=\"Plotting skill\" \/><\/p>\n\n<p>I do want to say that tying Flow to debugging isn&rsquo;t an original idea of mine. I\ngot the idea from Linus \u00c5kesson, who wrote a blog post specifically tying it to\nKernighan&rsquo;s quote.<\/p>\n\n<p>But basically, let&rsquo;s say we&rsquo;re debugging a problem. We can plot that task on\nthis graph based on the bug&rsquo;s difficulty versus our debugging skill. There&rsquo;s this\narea in the middle that represents our Flow state, and this is where we&rsquo;re going\nto be our most effective and most engaged in solving the problem.<\/p>\n\n<p>However, if the bug falls below that Flow state, what happens is we feel bored or\napathetic. Maybe in some cases relaxed, but we&rsquo;re never really engaged in solving\nthe problem. Now, if some task is too hard, it&rsquo;s above our Flow state, and we\nmight get frustrated or worried or anxious solving the problem. You might not be\nable to hit a deadline or something.<\/p>\n\n<p>Practically, this actually gives us a choice. We can a priori optimize our\nexperience either for debugging or for implementation.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.016.png\" alt=\"Debugging in Flow\" \/><\/p>\n\n<p>So if we implement below our Flow state and debugging is twice as hard, we&rsquo;re\noptimizing to be able to debug in Flow. However, this means we&rsquo;re going to be\nbored writing our code, because we&rsquo;re not that engaged.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.017.png\" alt=\"Programming in Flow\" \/><\/p>\n\n<p>If, on the other hand, we implement in Flow state, we get frustrated and\nanxious debugging outside of our skill.<\/p>\n\n<p>So what choice do we make here? Most studies I&rsquo;ve read seem to find 50% of the\ntime spent in the software development lifecycle is on debugging-related tasks.\nI&rsquo;ve actually seen studies that state as high as 80%. So if we&rsquo;re spending an\nabsolute minimum of half of our time debugging, you might think we should\noptimize for debugging in Flow. Implementing below Flow.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.018.png\" alt=\"Gaining skill\" \/><\/p>\n\n<p>But I want to challenge that here; I want to encourage implementation in Flow.\nAnd one simple, but absolutely crucial reason for this is that to gain skill\nin an activity, we have to practice at the edge of our ability.<\/p>\n\n<p>So for example, I&rsquo;ve played guitar for twenty-five years. And I suck.<\/p>\n\n<p>And fundamentally, this is because I rarely challenge myself. I learn some song\nor &ndash; the coolest thing I ever learned was some sweep picking pattern, and I\ncan&rsquo;t even do it very fast. But I rarely challenge myself to do something new.\nWe don&rsquo;t get better at guitar by playing one chord or plucking one string time\nafter time after time. In fact, I don&rsquo;t think that would even help us master\nthat chord because you&rsquo;ve got to do transitions in and out of it, et cetera.<\/p>\n\n<p>So to gain skill, we really have to practice at the <em>edge<\/em> of our ability.<\/p>\n\n<p>For debugging, what this means is that we have to learn something new. And\nsince learning involves teaching to some extent, I thought I might find some\nanswers to my questions in the area of the pedagogical aspects of debugging.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.019.png\" alt=\"We don't teach it\" \/><\/p>\n\n<p>Debugging actually turns out to be hard because we don&rsquo;t teach it. There are\nlectures embedded into some courseware that I&rsquo;ve seen, but as far as I&rsquo;m aware,\nno universities are actually teaching full courses on the subject. I think\nthat needs to change. I think industry is actually in a place to ask for that\nchange from academia.<\/p>\n\n<p>Even so, there&rsquo;s been plenty of research into teaching it. My hands-down\nfavorite paper in this area is a 2008 literature review from Ren\u00e9e McCauley\nand like six other folks. And what it does is survey fifty-two papers from\n1967 to 2008 and its trying to gather &ndash; sort of collate &ndash; all the information\non how debugging is taught, what&rsquo;s working, and what&rsquo;s not.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.020.png\" alt=\"Who\/what\/where...\" \/><\/p>\n\n<p>Most of these papers end up being about the classification. So the who, the\nwhat, the when, the where, the why of bugs. And what I was really interested in,\nthe how. Right? How do we solve it?<\/p>\n\n<p>Unfortunately, after reading tens of papers, nothing I read actually had\nconclusive, positive results that we can help people get better at debugging\nthrough teaching it. So. Back to asking questions.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.021.png\" alt=\"The root cause\" \/><\/p>\n\n<p>And why not go to the root cause? Why do bugs happen? We know specific reasons:\noff-by-one errors, race conditions, buffer overflows, whatever. But these are\nactually just classifications of related types of failure. They&rsquo;re not\nexplanatory. And the key reason that I love this paper is that it goes through\nthe literature and gives us a single, generalized, and I really think correct\nanswer about why bugs occur.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.022.png\" alt=\"Chains of cognitive breakdowns\" \/><\/p>\n\n<p>It says that bugs occur due to &ldquo;chains of cognitive breakdowns that are formed\nover the course of programming activity.&rdquo; That sounds ominous, right? But what\ndoes it actually mean?<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.023.png\" alt=\"Faulty assumptions\" \/><\/p>\n\n<p>So it means that debugging is hard because we don&rsquo;t know, and we can&rsquo;t possibly\nknow everything. For any moderately complex system, we can&rsquo;t possibly know all\nof it. So we create mental models about how the system behaves. And these are\neffectively assumptions and the goal is to provide a foundation to us to\naccurately reason about the behavior of these systems as we execute them, as we\nmake changes, and as we debug them.<\/p>\n\n<p>We actually need to do this even for the simplest software. We take it for\ngranted because of experience, I think, but really think. Just take a second to\nreally think about everything that has to happen under the hood for &ldquo;Hello,\nWorld!&rdquo; to work in any environment.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.024.png\" alt=\"Gaps in knowledge\" \/><\/p>\n\n<p>Our gaps in knowledge can lie anywhere from our editor, our compiler, our\nruntime environment. Our filesystem. Kernel. Hardware. Physics, if we&rsquo;re dealing\nwith latency-sensitive applications. Debugging is hard because we have so many\nopportunities to lack knowledge, even for very fundamentally simple problems.<\/p>\n\n<p>And I think this answer, that we necessarily lack knowledge really gets to the\ncore of why debugging is hard. I think it actually shows that Kernighan is right,\nthat it is actually harder than programming.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.025.png\" alt=\"New software based on existing knowledge\" \/><\/p>\n\n<p>If you don&rsquo;t agree, think of it this way: writing software requires us to use\nexisting knowledge to solve a problem. If we&rsquo;re writing new software, we may\nhave to learn something new, but we typically front-load this into our process\nwith technical design, reviewing API documentation, et cetera. That happens\nbefore we really engage in writing full systems in code.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.026.png\" alt=\"Debugging requires new knowledge\" \/><\/p>\n\n<p>Debugging on the other hand requires us to gain new knowledge to solve a problem\nthat we already thought was solved correctly. And this is harder because we by\ndefinition don&rsquo;t know where we went wrong. And so, I think this roughly proves\nKernighan&rsquo;s conjecture.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.027.png\" alt=\"We don't know how to teach\" \/><\/p>\n\n<p>Now, I mentioned none of the research had conclusively positive results in how\nwe teach debugging in order to help people get better at it. And maybe that&rsquo;s\nwhy we don&rsquo;t teach it: we don&rsquo;t know how. But in that 2008 paper, it was\nrecommended that future studies into teaching debugging look into some work from\nCarol Dweck. She published a book called &ldquo;Self-Theories&rdquo; in 1999, there&rsquo;s also a\nsort of pop-psych version called &ldquo;Mindsets&rdquo; that some people may be more familiar\nwith.<\/p>\n\n<p>Anyway, this 2008 paper suggests we might find some promising results there. And\nI do want to note that a later paper &ndash; or some later papers &ndash; have actually seen\nsuccesses in teaching debugging, incorporating this stuff, which is why I got\ninterested in it.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.028.png\" alt=\"Carol Dweck\" \/><\/p>\n\n<p>So Dweck&rsquo;s work includes over 40 years of what I think is just absolutely\nfascinating scientific research that unfortunately I don&rsquo;t have time to cover in\ndetail. But if this sort of hooks you a little bit, I will talk about it for\nyears. If you read one reference from this talk, and at the end I have reference\nslides in case you&rsquo;re interested, I do recommend reading this book. It&rsquo;s not\nthat long.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.029.png\" alt=\"Spectrum of intelligence\" \/><\/p>\n\n<p>In a nutshell, her work says that an individual&rsquo;s view of intelligence falls on\na spectrum &ndash; somewhere on a spectrum &ndash; of intelligence being an inherent trait\nversus a developed skill. And actually where a person&rsquo;s view falls on this\nspectrum says quite a lot about how they solve problems and their motivations\nfor solving problems.<\/p>\n\n<p>So what does intelligence have to do with debugging?<\/p>\n\n<p>Debugging at its core is all about solving problems. Since this research\naddresses how and why folks are actually doing that, I think it turns out to be\na really great proxy for talking about debugging.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.030.png\" alt=\"Mindsets on spectrum\" \/><\/p>\n\n<p>People who have an idea that intelligence is an inherent trait, you can&rsquo;t do\nmuch to change it, you&rsquo;re sort of born with it have a fixed mindset. And people\nwho think it&rsquo;s a skill, it can be developed, it&rsquo;s malleable, you can grow in\nyour intelligence, are said to have a growth mindset.<\/p>\n\n<p>And it&rsquo;s specifically the traits and the motivations and the behaviors of people\noperating on one or the other side of this spectrum that makes this relevant to\ndebugging.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.031.png\" alt=\"Fixed mindsets\" \/><\/p>\n\n<p>Debugging is hard in a fixed mindset. If you can&rsquo;t change much about your skill\nat debugging, why try? So fixed-mindset individuals don&rsquo;t tend to take on\nchallenging problems. And this also means that fixed-mindset individuals stagnate\nin their skill. If you remember from earlier when we were learning about Flow and\ndebugging, to get better at a skill, we have to practice at the edge of our\nability, and fixed mindset individuals avoid this, because they don&rsquo;t take on\nthe challenging problems.<\/p>\n\n<p>People operating in a fixed mindset are also more likely to cheat. Now, that\nsounds very academic, how do you cheat writing software? Well, think for example\nof folks who cargo-cult some code off of StackOverflow and put it in because it\nanswered some question they were asking about how to do a thing in their software.\nThey paste it in without understanding the code, the edge conditions, how it&rsquo;s\ndesigned to work within a system, and maybe they don&rsquo;t even give attribution. So\nnow when there&rsquo;s a gap in their understanding (which is I would assert a\ncomplete gap because otherwise they would have just written the code themselves),\nthey can&rsquo;t even figure out where that solution came from.<\/p>\n\n<p>So why would anybody do any of this? The fixed-mindset individual is motivated\nprimarily by appearing smart. This means they tend to compete with others in\ncollaborative situations, like pair programming. This makes debugging more\ndifficult because they don&rsquo;t do this collaboration, they don&rsquo;t ask questions,\nthey&rsquo;re trying to outperform. Because appearing smart is a motivator for them,\nthey see failure as a failure. They <em>don&rsquo;t<\/em> see it as an opportunity to learn.\nAnd to make matters even worse, failure will actually negatively their future\nperformance when they&rsquo;re working on problems that fall within their concrete,\nfoundational skill level.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.032.png\" alt=\"Growth mindsets\" \/><\/p>\n\n<p>The growth mindset is basically the opposite of all of this. Growth-oriented\nindividuals are fundamentally motivated by hard work and tackling challenging\nproblems. And this is because they see it as a way to gain skill.<\/p>\n\n<p>They work well with others in collaborative situations; they&rsquo;re likely to ask\nquestions therefore. They&rsquo;re usually enthusiastic to impart their knowledge\nand help mentor others because they also learn through teaching.<\/p>\n\n<p>Debugging is hard, but all of these traits make the task easier for the\ngrowth-oriented individual. And this gives us some insight as to why some\npeople seem to be better at it than others. It&rsquo;s as simple and fundamental as\ntheir motivations for solving problems.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.033.png\" alt=\"Lots of information\" \/><\/p>\n\n<p>So that&rsquo;s a lot of information.<\/p>\n\n<p>When I got to this point in my research I had a ton of questions. I still hadn&rsquo;t\nlearned anything about how we get folks interested in practicing at the edge of\ntheir ability. Can we move people from fixed to growth mindsets? How can we\neffectively develop our teams to solve the problems that they&rsquo;re always facing?<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.034.png\" alt=\"Act 2: mentoring growth\" \/><\/p>\n\n<p>So now we&rsquo;re in act 2, and we&rsquo;re gonna explore some techniques we can use as\nmentors and as individuals to improve our skill and to make the technique of\ndebugging &ndash; the experience of debugging &ndash; less painful for everybody. I want\nto note that while I&rsquo;m going to tie these techniques specifically to debugging,\nI think they all generalize very nicely; we can use them in other areas of our\nlives as well.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.035.png\" alt=\"Creating growth-oriented teams\" \/><\/p>\n\n<p>Creating growth-oriented teams starts at hiring.<\/p>\n\n<p>When we&rsquo;re looking at resumes, provide some additional screening for people\nwho are clearly touting credentials. Who focus on individual achievements\nwithout trying to tie this to the global benefit of the solutions they\nprovided. These folks are more likely to operate in a fixed mindset.<\/p>\n\n<p>People who like to explain in detail how they worked hard to solve a problem,\nwhat&rsquo;s challenging them right now, what they&rsquo;re learning: these are folks who\nare more likely to have growth-oriented thoughts towards work and debugging.<\/p>\n\n<p>And so in interviews now, I ask, &ldquo;What are you working on or learning right\nnow that&rsquo;s really challinging you, that you&rsquo;re finding difficult?&rdquo;\nFixed-mindset people don&rsquo;t tend to have a compelling answer! They don&rsquo;t want\nto admit there&rsquo;s something that they don&rsquo;t know. Now, growth folks will answer\nthis question forever, so you need to time-box it.<\/p>\n\n<p>I do really, really, really, <em>really<\/em> specifically want to note that we\nshouldn&rsquo;t exclude fixed-mindset individuals from hire. There are a number of\nsuper-compelling reasons around diversity and what types of folks are more\nlikely to develop a fixed mindset (specifically women and minorities). This is\nsort of culturally enforced, but I don&rsquo;t want to get too much into that so I&rsquo;ll\ntalk about that more afterwards. But just as good a reason is that we can\nactually promote folks into a growth mindset.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.036.png\" alt=\"Promoting growth\" \/><\/p>\n\n<p>This can be as simple as changing the words we use when communicating with\npeople. Dweck&rsquo;s research actually repeatedly shows that praising people for\nbeing smart results &ndash; reinforces a fixed mindset and all the negative that\ncomes with it. Praising hard work, on the other hand, reinforces the motivation\nto work hard. It recognizes the effort put into work, and it promotes a shift\nto a growth mindset for someone who&rsquo;s not there already.<\/p>\n\n<p>When we praise folks for being smart, we&rsquo;re actually robbing them of the\nrecognition of the time and effort they spent solving problems. So the next time\nyour team member solves a particularly hairy bug, praise them for their hard\nwork. For their effort. And if someone calls you a genius for doing the same,\ndeflect that assessment. Because they way you respond can help move that person\naway from their fixed view of your ability into a growth-oriented view. So you\ncan reply with something like, &ldquo;Thanks for the compliment! I&rsquo;ve actually just\ndone this kind of thing a lot.&rdquo; Or, &ldquo;I have a lot of experience in this area.&rdquo;<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.037.png\" alt=\"Be curious\" \/><\/p>\n\n<p>Once we&rsquo;ve created and promoted growth-oriented teams, we need to nurture and\nsupport their success. And one way to do this is by encouraging curiosity.\nEncouraging learning. When mentoring folks, we also need to be curious. What\ndoes a mentor have to be curious about?<\/p>\n\n<p>So for example, I was explaining issues of debugging concurrent systems to a\nteam member recently and used some metaphor (it doesn&rsquo;t really matter what) to\ndescribe how concurrent processes interact with shared mutable state. My\ncolleague reflected a completely different metaphor back at me. My first hunch\nwas to toss it aside, because it didn&rsquo;t immediately make sense to me. It wasn&rsquo;t\nhow I was internalizing the view of this system. So I wanted to correct this\nperson.<\/p>\n\n<p>But after some further thought, I realized that the new metaphor was perfectly\nfine, it just wasn&rsquo;t what I was using. And this is what I mean by being curious:\nbe open to new ideas, to new solutions, to new methods of actually thinking\nabout things.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.038.png\" alt=\"Many paths to knowledge\" \/><\/p>\n\n<p>Because there&rsquo;s many paths to get to knowledge. Our own intrinsic views aren&rsquo;t\nthe best for everybody. People want to base new knowledge on existing\nknowledge. It provides them an already-understood foundation on which to build.\nSo being curious is really about understanding where your mentee or your\ncolleagues are coming from.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.039.png\" alt=\"Ask questions\" \/><\/p>\n\n<p>If we&rsquo;re encouraging curiosity, we should expect to answer questions; we should\nask questions. Asking questions about someone&rsquo;s past experiences and solutions\nto problems is a really effective way to learn. And when I was first learning to\ndebug, I didn&rsquo;t even know what to ask. So I started with, &ldquo;How do you debug?&rdquo;<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.040.png\" alt=\"Art or science?\" \/><\/p>\n\n<p>And one frequent answer I&rsquo;d get was, &ldquo;Oh, well, debugging&rsquo;s more of an art than\na science.&rdquo; Is that true?<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.041.png\" alt=\"B.S.\" \/><\/p>\n\n<p>Uh&hellip; debugging is part of computer science, which is science, and computer art\nis something very different. So I think this is a B.S. answer.<\/p>\n\n<p>I think the reason that we think debugging is an art is actually a privilege of\nexperience. As we gain experience, we forget what it was like to learn a thing,\nand we forget even more the more experience we gain. Giving this answer tacitly\nallows us to avoid remembering. It acts as a scapegoat for not actually having\nto teach anything to anybody. Finally, this answer also reinforces the idea that\ndebugging is not actually a science.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.042.png\" alt=\"The scientific method\" \/><\/p>\n\n<p>And that&rsquo;s not great because the scientific method is a collection of techniques\nspecifically designed for the acquisition of new knowledge. Which is exactly\nwhat we need for a task that requires us to gain new knowledge.<\/p>\n\n<p>As a quick review: we observe a problem, we develop a general theory about the\nproblem. We ask some questions, gather some data about that theory. We form a\nhypothesis, we test the hypothesis. Now science is in this sort of regard a\nBloom filter. Because if our tests succeed, we might be right, and if they fail,\nwe&rsquo;re definitely wrong about something.<\/p>\n\n<p>But in practice, I notice that we skip steps.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.043.png\" alt=\"Obvious memory corruption\" \/><\/p>\n\n<p>So for example, let&rsquo;s say our software crashed, and we look at memory, and it&rsquo;s\ngarbage. And we say, &ldquo;Ah-ha-ha-ha! This obvious memory corruption.&rdquo; And we start\nlooking for and testing that.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.044.png\" alt=\"Not a hypothesis\" \/><\/p>\n\n<p>But that&rsquo;s not a hypothesis, it&rsquo;s a general theory. And because it&rsquo;s a general\ntheory, it&rsquo;s also wrong sometimes. And so we end up calling it a heisenbug because\nwe&rsquo;re uncertain when it happens, even though the actual problem is that we don&rsquo;t\nunderstand <em>why<\/em> it happens.<\/p>\n\n<p>The root cause here is that we skipped some of the steps. We skipped the steps of\ncreating and testing a hypothesis.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.045.png\" alt=\"Heisenbugs\" \/><\/p>\n\n<p>Furthermore, memory corruption is usually a symptom of something else, at least\nin my area. Something like wrong buffer sizes, off-by-one bugs, wild pointers,\nuse-after-free, race conditions, any number of other things could actually be a\ncause for corrupted memory.<\/p>\n\n<p>So we end up debugging symptoms instead of causes. And if we were looking for\nsomething specific, like uninialized variable or an off-by-one instead of\n&ldquo;obvious memory corruption,&rdquo; the heisenbug effectively ceases to be so uncertain.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.046.png\" alt=\"A real hypothesis\" \/><\/p>\n\n<p>We can avoid all of this simply by creating and testing against a real hypothesis.\nNow, a hypothesis is a testable and falsifiable statement like, &ldquo;An uninitialized\nvariable causes us to write past the end of some object, corrupting adjacent memory\nunder some very specific conditions.&rdquo;<\/p>\n\n<p>Reproducing the problem with tests is a crucial part of the scientific method as\napplied to debugging. This <em>is<\/em> how we test our hypothesis; we need to reproduce\nissues. Following this process not only makes debugging less frustrating, it\nactually makes it easier the next time around because all of a sudden, you can\nintuit causes from your new experience.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.047.png\" alt=\"What questions do we ask?\" \/><\/p>\n\n<p>That&rsquo;s all well and good. But sometimes we don&rsquo;t actually have enough information\nto ask intelligent questions about the data that we see to help us form a\nhypothesis. So what do we do then? We might not even know the causes for memory\ncorruption.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.048.png\" alt=\"Socratic circle\" \/><\/p>\n\n<p>The Socratic method is a fantastic way to build understanding in a collaborative\nfashion. It works hand-in-hand with the scientific method because its goal is to\nidentify and eliminate hypotheses that lead to contradictions. And it&rsquo;s based on\nthe idea that people build new knowledge from existing knowledge, so it explicitly\npromotes curiosity.<\/p>\n\n<p>The circular flowchart I&rsquo;ve drawn here is an illustration of how a group process\ncalled &ldquo;the Socratic circle&rdquo; is designed to work. Starting from some existing\nknowledge, a premise is introduced by the person who is aiming to learn. A mentor\nthen asks questions about this premise, and a discussion ensues. The mentor is\nguiding the discussion through questions, not through providing answers. The mentee\nis the one providing answers and making statements about the questions.<\/p>\n\n<p>This can lead to new questions, at which point we repeat the process, but it&rsquo;s\nrepeated until some new knowledge is formed&hellip; at which point that becomes existing\nknowledge, and we can repeat with new premises.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.049.png\" alt=\"Owning knowledge\" \/><\/p>\n\n<p>A really key benefit here is it allows a mentee to discover knowledge, to really\n<em>own<\/em> it. Isn&rsquo;t it really great to feel like you&rsquo;ve discovered something new?<\/p>\n\n<p>It&rsquo;s so much better than this typical, learn-by-rote method of teaching that we\nall sort of think of as teaching, where all glory belongs to the teacher, to the\nmentor. Owning new knowledge feels really great for the person who is learning,\nand it absolutely helps to build and solidify strong teams.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.050.png\" alt=\"Active recall\" \/><\/p>\n\n<p>The next two techniques I&rsquo;d like to share with you, I learned from Allison Kaptur.\nActive recall &ndash; I think she calls it something different, I don&rsquo;t remember what\nshe calls it; I call it active recall &ndash; it&rsquo;s the process of actively trying to\nremember the answer to a thing that you don&rsquo;t think you know. Flash cards. We all\nthink they suck, but maybe we&rsquo;re just not using them properly.<\/p>\n\n<p>For example, as applied to something real, remember at the very beginning of this\ntalk, I was ranting about how mmap is confusing and I&rsquo;m always looking at the\nmanpage? Well, instead of looking at the manpage, I can use active recall. And what\nthis means is that I try my hardest to write the line of code to consume the\ninterface and its errors. (Or lines, if I&rsquo;m consuming the errors as well.) I just\ndo that to the best of my ability. As I think it works.<\/p>\n\n<p>And then regardless of whether I&rsquo;m right or wrong, I can check my answer, because I\nhave a manpage that tells me. So I can actively work on trying to remember what the\nanswer to a thing is before I do the checking. It&rsquo;s completely harmless. I wouldn&rsquo;t\nnecessarily recommend doing this in production, but it&rsquo;s harmless like 99% of the\ntime or more.<\/p>\n\n<p>In addition to being a really excellent personal exercise, we can also practice this\nwith others on our teams. It fits in really nicely with the Socratic method. So for\nexample, the next time somebody comes up with a bug and they say, &ldquo;Hey, how did this\nhappen,&rdquo; you can say, &ldquo;Well, what&rsquo;s your understanding of the bug right now?&rdquo; This\nengages in a Socratic dialogue and forces them to recall their knowledge of the system\nand the conditions of this bug and what they think.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.051.png\" alt=\"Spaced practice\" \/><\/p>\n\n<p>Another technique I learned from Allison is spaced practice. The idea here is that\nwhen we learn new information, our brains need time to process it, or sort it, or\nwhatever makes sense for you to visualize how we retain information. If we spend too\nmuch time adding new, related information (like in a 40 minute talk), our brains\ndon&rsquo;t have time to do the background processing they need to really let that\ninformation sink in. I&rsquo;m not a neuroscientist, so apologies, that hand-wavy thing\nis about as good as I&rsquo;m going to get here.<\/p>\n\n<p>But spaced practice is the idea of switching between different tasks to allow this\nprocessing to occur. Different unrelated tasks.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.052.png\" alt=\"Diminishing returns\" \/><\/p>\n\n<p>So for example, think of the last time you spent and entire afternoon staring at a\ndebugger. It&rsquo;s really easy for us to get engaged in these sort of sunk-cost\nfallacies about our work, where we spend additional time &ndash; more and more and more\ntime &ndash; debugging despite diminishing returns for our effort.<\/p>\n\n<p>If we put the problem away for a few hours or overnight, frequently we have a\nworkable solution the next time we come back to it; I think we all identify\nwith this.<\/p>\n\n<p>What we can do about this is to have one or two additional items to switch to\nthrough the course of the day. <em>Do<\/em> go eat lunch. <em>Do<\/em> take breaks. Go home.\nGo to sleep. Give yourself the personal resources that you need to finish the\nchallenge, and really encourage your team members to do the same thing.<\/p>\n\n<p>Especially if you&rsquo;re in a managerial role: it&rsquo;s extremely important because most\npeople won&rsquo;t ask for something from you. It&rsquo;s extremely important for you to\nexplicitly allow people to just let something go for a little while. Especially\nwhen they&rsquo;ve worked really hard for a really long time and they have basically\nnothing to show for it.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.053.png\" alt=\"Persevere.\" \/><\/p>\n\n<p>What spaced practice doesn&rsquo;t mean is putting problems to the side and giving up\nwhen you encounter a challenging problem. Persevere through difficulties. Challenges\nare opportunities to learn something new, to gain skill. If you need help, ask for\nit and be available to others who need your help. Help them persevere by giving them\neffort-based encouragement. By engaging in Socratic exercises to solve their issue.\nAnd when appropriate, by givin them space to let something go for a while, give them\nsomething else to work on.<\/p>\n\n<p>And maybe even something easier. That&rsquo;s not really promoting a fixed mindset, because\nwhat it does is it gives them a win. And after a really big failure, even a small\nwin makes a huge difference psychologically to people.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.054.png\" alt=\"Action!\" \/><\/p>\n\n<p>So I think we&rsquo;ve seen that debugging is fundamentally more difficult than\nprogramming. There are things we can do to make it less awful. And I think if we\ngo down to it, the change needs to start in our culture.<\/p>\n\n<p>Engineering culture has this sort of romance with the idea of &ldquo;smart.&rdquo; It starts\nwith our language, we praise people for their &ldquo;genius&rdquo; or their &ldquo;intelligence&rdquo;\ninstead of their effort, or the outcome. But our jobs are all fundamentally\neffort-driven.<\/p>\n\n<p>We engage in fixed-mindset assessments of our peers. We engage in activities like\nhero worship: &ldquo;Linus Torvalds is a god&rdquo; (regardless of how you feel about him).\nDespite that we do this, our heroes are actually pretty similar to us. And if they\nhave a skill that we don&rsquo;t, we can get to be like them with just a little bit of\neffort and education.<\/p>\n\n<p>Our culture doesn&rsquo;t sometimes promote the tools we need to stay in our Flow\nstate. Or to develop a growth mindset. We convince ourselves and others that\ndeadlines mean that we need to discard processes like the scientific and Socratic\nmethods, despite the fact that the time you spend using them actually saves you\ntime in the long run.<\/p>\n\n<p>We interrupt people with pop-up notifications in Slack, with trivial things that\ncould have been solved better in email. Our culture doesn&rsquo;t do a lot to promote\nthe soft skills that are required to really be effective at debugging, and as I\nwould also argue, anything to do with actually programming.<\/p>\n\n<p>So let&rsquo;s change this.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.055.png\" alt=\"Action items\" \/><\/p>\n\n<p>Regardless of whether you&rsquo;re a manager, a mentor, a lead, an architect, an\nindividual contributor, recognize and praise people for their effort. Congratulate\nhard work, not genius. Recognize and promote the idea that failure is not\nnegative, it&rsquo;s part of the learning process.<\/p>\n\n<p>Be curious, be scientific in your work. Encourage learning and skill development,\nwhether that&rsquo;s through the Socratic method or curiosity. Through active recall. Or\nthrough sending folks, sending your employees, to conferences like this one where\nthey can learn something new.<\/p>\n\n<p>Give yourself time to digest information by taking breaks. Don&rsquo;t interrupt people,\ndon&rsquo;t interrupt yourself with trivialities. Teach folks from what they know, not\nfrom what you know.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.056.png\" alt=\"Re-analysis\" \/><\/p>\n\n<p>In closing, I want to re-assess Kernighan&rsquo;s conjecture and his question.<\/p>\n\n<blockquote>\n<p>Everyone knows that debugging is twice as hard as writing a program in the\nfirst place. So if you&rsquo;re as clever as you can be when you write it, how will\nyou ever debug it?<\/p>\n<\/blockquote>\n\n<p>We&rsquo;ve learned today that there&rsquo;s no absolute, objective difficulty to debugging.\nWe also now know that Kernighan is correct: debugging the most difficult problems &ndash;\nthe hardest problems &ndash; is always going to be harder than writing our cleverest\ncode. But by adopting these processes, by adopting and promoting a growth mindset &ndash;\nthe debugging mindset &ndash; we can now definitively answer his question of how we\nwill do it.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.057.png\" alt=\"Thank you\" \/><\/p>\n\n<p>Thank you.<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/dm.058.png\" alt=\"Reference slide\" \/><\/p>\n\n<p>Here are my references for those of you who care. I have 12 minutes for\nquestions if anyone has any.<\/p>\n\n<blockquote>\n<p>Hey. Great talk, you mentioned this earlier in your talk and I was curious\nto know why you think minorities, in particular females, would fit more into\na fixed mindset, and what is encouraging that behavior.<\/p>\n<\/blockquote>\n\n<p>Yep! So this actually comes from another &ndash; the first paper I read trying to\ntie Dweck&rsquo;s stuff to computer science. And at least in the United States this is\ntrue, but I think our culture is similar enough to other Western European\ncultures &ndash; and just sort of patriarchal, right?<\/p>\n\n<p>And so what&rsquo;s actually true is that high IQ girls in elementary school &ndash; so up\nthrough grade five &ndash; outperform every other group. But because of sort of socially\nand culturally imposed ideas like &ldquo;women should be pretty, not smart&rdquo; or &ldquo;best\nto be seen and not heard,&rdquo; these things are reinforced. And these things actively\npromote a fixed mindset.<\/p>\n\n<p>And so, because of these cultural pressures, women are more likely to develop a\nfixed mindset. What you see is these high performing girls in fifth grade, all of\na sudden they get to middle school and their grades go down a cliff.<\/p>\n\n<p>The idea behind what they were saying in this paper was that this even goes as far\nas to explain why in computer science (and maybe STEM in general) you even see\nthe discrepancy in gender balance in the enrollment level. Women don&rsquo;t enroll in\nC.S. as much as men. And they tie that back to this because everything about\ncomputer science, and especially debugging, requires a growth oriented view.<\/p>\n\n<p>So if you don&rsquo;t have that, and you think you can&rsquo;t solve these problems, and you&rsquo;re\nengaging in a field that&rsquo;s constantly asking you to solve something hard and outside\nof your skill level, you just don&rsquo;t. You switch to something else.<\/p>\n\n<p>So that&rsquo;s where that comes from. I don&rsquo;t think there&rsquo;s anything inherently true\nthat says it has to be that way. And this is where I&rsquo;m coming from with starting\nthe change in our culture.<\/p>\n\n<blockquote>\n<p>So when you were talking about the Socratic method, how do you engage in the\nSocratic method without sliding into a patronizing tone, if you know what I\nmean?<\/p>\n<\/blockquote>\n\n<p>Yeah, that&rsquo;s really hard. So, to be honest, most of my experience with this is on\nIRC and so people are just going to take things how they take them. So in like\nhelp channels like ##c on Freenode, or #freebsd, or other channels that I&rsquo;m\ninvolved in or have been.<\/p>\n\n<p>I think it gets back to what some other folks in this track have said which is\nthat it requires trust. If you have a good relationship with the person you&rsquo;re\ninvolved with in this methond &ndash; or the people you&rsquo;re involved with in this\nmethod, you end up having this trust. Trust that your questions are genuinely\ncuriosity-driven.<\/p>\n\n<p>Also one thing I didn&rsquo;t really touch on is that it can feel patronizing, but it\nisn&rsquo;t necessarily experienced that way. And one of the reasons we might be\nconcerned about that is that these aren&rsquo;t really behaviors we&rsquo;re engaging in\nfrequently with our colleagues or even with ourselves. But, getting again back\nto neuroscience, there&rsquo;s neuroplasticity. The more frequently you engage in\nsome behavior, the less weird it becomes.<\/p>\n\n<p>And so I would suggest if you&rsquo;re engaging in this exercise and you&rsquo;re concerned\nabout that, be up-front about what you&rsquo;re doing, at the head. Say, &ldquo;I&rsquo;d like to\ntry this activity with you&hellip; it might come across&hellip;&rdquo; &ndash; and we can all sort of\ntell if we&rsquo;re sounding like an ass, so just don&rsquo;t sound like an ass, but, &ldquo;I&rsquo;m\nnot trying to be condescending and I&rsquo;m not trying to be patronizing, but I&rsquo;d\nlike to try this method with you, would you engage in it with me?&rdquo;<\/p>\n\n<p>And from there, I think you&rsquo;ve kind of gotten rid of that sort of stigma, maybe.<\/p>\n\n<blockquote>\n<p>Hi, so you talked about a couple of methods like taking breaks, working on\na couple of different things to kind of give people that space to grow. What\nI&rsquo;m curious about is in a very time-critical scenario &ndash; servers are going\ndown, you&rsquo;re hemmoraging value &ndash; what other approaches would you take, or\nhow can we apply that in a way that sort of tightens that loop and keeps\npeople focused.<\/p>\n<\/blockquote>\n\n<p>That&rsquo;s a really good question that I would have suggested you went to my\ncolleague Lisa Phillips&rsquo; talk this morning on how we run our incident command.\nAnd actually, fundamentally, it follows a lot of these same principles. So\nshe&rsquo;s actually VP of our ops department and she&rsquo;d be able to explain &ndash; I&rsquo;m\njust a lowly engineer slash tech lead &ndash; whatever I am.<\/p>\n\n<p>Provide support for people outside the context of what they&rsquo;re doing. So we\nhave incident commanders who delegate tasks, specific tasks, who consume\ninformation from folks, who ask people if they need bathroom breaks or rest\nbreaks or food breaks &ndash; who will order food for them. It really gets back\nagain to some of the bits of spaced practice where you need to really give\nyourself the personal space to honor your body and your mind. I mean, without\nsounding too granola, I hope.<\/p>\n\n<p>But these are things that you physiologically need, and when you don&rsquo;t get them,\nyour performance diminishes. So it is always better, in fact I would argue you\nshould be doing all of these things <em>especially<\/em> in those activities, because\nif you&rsquo;re not, you&rsquo;re more likely to F-up if you don&rsquo;t have your needs met.<\/p>\n\n<blockquote>\n<p>I have seen, and you can see, some developers are really good at attacking a\nproblem in some way, and others may take a while. Is that aligned with because\nthey are fixed mindset versus growth, or is it something just because of\nexperience and an individual&rsquo;s expertise?<\/p>\n<\/blockquote>\n\n<p>The mindset research doesn&rsquo;t say anything about actual skill at any point in\ntime. It just talks about motivations and approaches. So I don&rsquo;t think you could\nderive mindset from that. But there are questions you could ask about a person\nand about their view.<\/p>\n\n<p>So fixed-mindset people will say things like, &ldquo;I just&hellip;&rdquo; and, &ldquo;I can&rsquo;t&rdquo; and stuff\nlike that, and so you can observe that through the way they communicate with\nyou. I don&rsquo;t think you can observe it through how they do their work, or how long\nit takes them.<\/p>\n\n<p>You might be able to observe it in how they approach problems. Do they try the\nsame thing frequently expecting a different result? That maybe gives a better idea.<\/p>\n"},{"title":"VarnishCon Video and QConSF","link":"https:\/\/9vx.org\/post\/debugging-talk\/","pubDate":"Sun, 21 Aug 2016 05:39:00 +0000","guid":"https:\/\/9vx.org\/post\/debugging-talk\/","description":"<p>I <a href=\"https:\/\/9vx.org\/post\/debugging-psychology-theory-and-application\/\">wrote earlier<\/a> about my\ntalk at VarnishCon 2016. <a href=\"https:\/\/www.infoq.com\/fr\/presentations\/varnishcon-devon-odell-debugging\">A video<\/a>\nis now available of the talk.<\/p>\n\n<p>Unfortunately, I think I did a particularly poor job of conveying irony\n(especially at the end: &ldquo;I did you all a huge solid&rdquo;), which I regret. Also,\ntime constraints meant that I had to skip some of the points in applying the\ninformation, which I think made some of the talk less than useful for folks\nwho didn&rsquo;t chat with me afterwards.<\/p>\n\n<p>I hope folks can look past that, and I&rsquo;d be happy to answer any lingering\nquestions about my talk via email or whatever medium.<\/p>\n\n<p>In the same vein, <a href=\"https:\/\/qconsf.com\/sf2016\/presentation\/building-debugging-mindset\">I&rsquo;ll be speaking at QConSF<\/a>\nthis year on a similar topic! I&rsquo;m hoping to make this talk a little more\nactionable, while focusing more on Dweck&rsquo;s research, the scientific method,\nteams, mentorship, learning, and even neuroplasticity.<\/p>\n\n<p>If folks have thoughts \/ opinions (as if anybody actually reads this) on what\nI could do better from my previous talk, I&rsquo;d love to hear it!<\/p>\n"},{"title":"Debugging: Psychology, Theory, and Application","link":"https:\/\/9vx.org\/post\/debugging-psychology-theory-and-application\/","pubDate":"Thu, 23 Jun 2016 00:23:00 +0000","guid":"https:\/\/9vx.org\/post\/debugging-psychology-theory-and-application\/","description":"\n\n<p>I spoke at <a href=\"https:\/\/varnishcon2016.sched.org\/\">VarnishCon 2016<\/a> on\ndebugging. A small portion of the content was related to Varnish, but about\nhalf of the talk focuses on research into debugging. Though I have\n<a href=\"https:\/\/speakerdeck.com\/dho\/debugging-varnish\">made my slides available<\/a>,\nthey&rsquo;re not very useful without speaker notes (and I ended up ad-libbing a\nfair bit more than intended). This post is an attempt to provide a reference\nmaterial for the ideas in the talk as well as to expand on some points I\ndidn&rsquo;t have time to cover.<\/p>\n\n<h1 id=\"theory\">Theory<\/h1>\n\n<p>Debugging, if we&rsquo;re honest about it, is simply another word for problem\nsolving. We&rsquo;ve all been doing this since seeing word problems in our basic\nschool math education. &ldquo;If Levar has 3 apples and Melissa has 2 apples, how\nmany apples do both Levar and Melissa have?&rdquo; Debugging isn&rsquo;t that dissimilar:\nthe only thing missing here is that formulating the question we have to solve\nisn&rsquo;t actually part of our education. Given the word problem above, debugging\nwould be like showing kids a picture of Levar and Melissa and their apples.\nThey&rsquo;d be asked to create a word problem that could be solved to determine\nthe total number of apples, and then to solve that problem. It&rsquo;s easier said\nthan done, but there are things we can do to make debugging easier for\nourselves and for others.<\/p>\n\n<h2 id=\"impetus\">Impetus<\/h2>\n\n<p>Why does any of this matter? Certainly we spend time debugging our software,\nbut eventually this just becomes part of the process.<\/p>\n\n<p>Personally, information in this area is currently extra interesting. I have a\nnew role as &ldquo;Tech Lead&rdquo; at Fastly, where my primary objective is to help other\nmembers of my team succeed. Without a good idea of what success looks like or\nhow to get people there, I can&rsquo;t succeed in this. It would follow here that\neither they succeed and my role is obviated, or they simply don&rsquo;t succeed. I&rsquo;d\nprefer for success all around.<\/p>\n\n<p>But there are reasons that everyone should care regardless of their role in\nan organization.<\/p>\n\n<p><a href=\"http:\/\/citeseerx.ist.psu.edu\/viewdoc\/download?doi=10.1.1.370.9611&amp;rep=rep1&amp;type=pdf\">A 2013 study (PDF)<\/a>\nconducted by the University of Cambridge Judge School of Business concluded\nthat the estimated total cost of debugging globally reaches $312 billion.\nTheir study also finds that 49.9% of programming time was spent on debugging\nactivities (&ldquo;fixing bugs&rdquo; and &ldquo;making code work&rdquo;). Other studies find that\nthe proportion of debugging to coding time is as high as 80%.<\/p>\n\n<p>This implies that if we can reduce the time it takes to &ldquo;fix bugs&rdquo; and &ldquo;make\ncode work&rdquo;, we can save our employers money. If that doesn&rsquo;t sound rewarding\nfor some reason, we can also spend more of our time working on new code,\nwhich is quite often a more rewarding exercise than staring at that damn bug\nfor 8 hours a day.<\/p>\n\n<h2 id=\"the-root-of-the-problem\">The Root of the Problem<\/h2>\n\n<p><img src=\"https:\/\/9vx.org\/images\/root-problem.png\" alt=\"The Root of the Problem\" \/><\/p>\n\n<blockquote>\n<p>As soon as we started programming, we found to our surprise that it wasn&rsquo;t\nas easy to get programs right as we had thought. We had to discover\ndebugging. I can remember the exact instant when I realized that a large\npart of my life from then on was going to be spent in finding mistakes in\nmy own programs. &mdash;Maurice Wilkes, 1949<\/p>\n<\/blockquote>\n\n<p>I think we&rsquo;re all sympathetic to Wilkes&rsquo; discovery. Indeed, we all\nindividually &ldquo;find out&rdquo; that it&rsquo;s not as easy to write software as it first\nappears to be. &ldquo;Just tell the computer to copy this thing there,&rdquo; we na&iuml;vely\nspeculate. &ldquo;How hard can that possibly be?&rdquo;<\/p>\n\n<p>25 years later, we have an indicator for how hard it is. In <em>The Elements of\nProgramming Style<\/em>, Brian Kernighan famously states:<\/p>\n\n<blockquote>\n<p>Everyone knows that debugging is twice as hard as writing a program in the\nfirst place. So if you&rsquo;re as clever as you can be when you write it, how will\nyou ever debug it?<\/p>\n<\/blockquote>\n\n<p>First, we should understand that in context, this quote is illustrating that\nwe really shouldn&rsquo;t be trying to outsmart compilers in the name of\noptimization. Expressing our problem as simply as possible is beneficial to\nothers reading the code. And as it turns out, it is usually beneficial to\ncompilers.<\/p>\n\n<p>That said, there&rsquo;s still a strong, unsupported conjecture here: that debugging\nis twice as hard as programming. Is this really true? Should it be true?<\/p>\n\n<p>I don&rsquo;t have a good feeling for the order of magnitude of difference, but I\nfeel that debugging is necessarily more difficult than programming. The\nrationale here is that a bug is generally indicative of a piece of software\nbehaving in an unexpected way. This lack of expectation translates directly to\nsome unhandled case in the software. When software is sufficiently well\ndesigned, we can deduce that such unhandled cases were unhandled because they\nwere not understood (as opposed to being understood but forgotten).<\/p>\n\n<p>Certainly, some bugs are indicative of forgetfulness. But these cases of\nmisunderstandings and ignorance are fundamentally more insidious when dealing\nwith software bugs. If we work on well-designed software, and we encounter a\nbug in it, we are operating without the tool of knowledge, which is a thing\nwe <em>did<\/em> have when we were writing the software.<\/p>\n\n<p>Whether this makes debugging exactly twice as hard as writing software remains\nambiguous, and would be worth a quantitative study. But we can probably safely\nsay that it <em>is<\/em> harder. From this, it is clear that if we are operating at\nthe limits of our knowledge and ability, to debug code, we must extend the\nlimits of our knowledge and ability.<\/p>\n\n<p>There are other interesting things we can read into this quote. One is that\nwhen we&rsquo;re debugging software, it&rsquo;s usually not software we wrote ourselves.\nThe software industry is overwhelmingly collaborative, and very few popular\nprojects or professional endeavors receive contributions from only a single\nsource. This means that to debug most code, we actually have to extend our\nabilities and knowledge past those of our colleagues and peers.<\/p>\n\n<p>Intuitively, we know that this is a solveable problem. Whether you&rsquo;ve been in\na computer science course for a semester, gone through a code bootcamp for a\nfew months, or practiced professionally for more than twenty years, you&rsquo;re\nbetter at writing code than you were when you started. You&rsquo;re more clever. And\nyou&rsquo;re better at debugging than when you started.<\/p>\n\n<p>So how <em>will<\/em> we ever debug this clever code? How will we extend the limits of\nour knowledge and ability? Simple: we have to learn more.<\/p>\n\n<h2 id=\"flowing-along\">Flowing Along<\/h2>\n\n<p>Clearly there&rsquo;s another way to interpret this quote. Instead of seeing it as a\nconjecture preventing us from debugging complicated systems, we can look at it\nas a platform we can use to raise our ability.<\/p>\n\n<p><a href=\"http:\/\/www.linusakesson.net\/programming\/kernighans-lever\/index.php\">Linus \u00c5kesson calls this platform &ldquo;Kernighan&rsquo;s lever&rdquo;<\/a>.\nLinus notes:<\/p>\n\n<blockquote>\n<p>By putting in a small amount of motivation towards the short-term goal of\nimplementing some functionality, you suddenly end up with a much larger\namount of motivation towards a long term investment in your own personal\ngrowth as a programmer.<\/p>\n<\/blockquote>\n\n<p>Linus then builds on top of <a href=\"https:\/\/www.amazon.com\/Flow-Psychology-Experience-Perennial-Classics\/dp\/0061339202\">Mihaly Csikszentmihalyi&rsquo;s work<\/a>\non the psychological concept called Flow. The whole book summarized is that\n&ldquo;flow&rdquo; is the name of the state you get into when you are fully immersed in\na task such that you find it difficult to be distracted, lose track of time,\nand gather great enjoyment from the process. Furthermore, this state only\noccurs when you&rsquo;re taking on a task that matches the limits of your abilities.\nThe book goes on to talk about practicing achieving Flow state, mostly by\nrepeating the thesis. <a href=\"https:\/\/www.ted.com\/talks\/mihaly_csikszentmihalyi_on_flow\">Csikszentmihalyi&rsquo;s TED talk<\/a>\non Flow is more digestable.<\/p>\n\n<p>Anyway, Linus observes that when this concept is applied to Kernighan&rsquo;s quote,\nthere are two consequences. If we want to optimize our experience for\ndebugging, we have to implement code below our ability &mdash; but this is\ntedious and boring. If we want to optimize our experience for writing code, we\nhave to debug above our ability, but then debugging is frustrating. Making a\ndecision between tedium and frustration seems counter-productive as well.<\/p>\n\n<p>So in this model of learning more by implementing outside of our ability, we\nare necessarily frustrated at least part of the time. If we make the tradeoff\nto never do this, we don&rsquo;t learn more. I wanted to see if there was any\nacademic research on debugging, how it&rsquo;s taught, and how we learn to get\nbetter at it.<\/p>\n\n<h2 id=\"reading-papers\">Reading Papers<\/h2>\n\n<p>It turns out that there&rsquo;s been a fair amount of research into many areas of\ndebugging not directly related to tools and automation.\n<a href=\"https:\/\/www.semanticscholar.org\/paper\/Debugging-a-review-of-the-literature-from-an-McCauley-Fitzgerald\/b4b5fb50dc18c9278ca48d47a0ba79264dd9201d\/pdf\">Debugging: a review of the literature from an educational perspective (PDF)<\/a>\nfrom Ren&eacute;e McCauley, Sue Fitzgerald, Gary Lewandowski, Laurie Murphy,\nBeth Simon, Lynda Thomas, and Carol Zander provides an excellent overview of\nthe research on the pedagogical aspects of debugging, reviewing materials from\nas early as 1967 through to 2008. I&rsquo;d like to stress to researchers that this\nsort of literature review is extremely useful to practitioners and to other\nresearchers who aren&rsquo;t quite sure where to start.<\/p>\n\n<p>In the literature, much of the research from 1980 to 2008 focuses on\nclassification of programmers based on skill level. Nearly all of this\nclassification research relies on the\n<a href=\"http:\/\/www.dtic.mil\/cgi-bin\/GetTRDoc?AD=ADA084551&amp;Location=U2&amp;doc=GetTRDoc.pdf\">Dreyfus model of skill acquisition (PDF)<\/a>\nor a simplified form of it. In particular, this research tends to compare\nprogrammers of &ldquo;novice&rdquo; and &ldquo;expert&rdquo; skill levels and figure out what\nabilities the expert has that the novice lacks.<\/p>\n\n<p>The pedagogical research here then looks at whether it is possible to teach\nthese skills to novices. Though several papers claim success (notably\n<a href=\"https:\/\/dl.acm.org\/citation.cfm?id=971310\">Chmiel &amp; Loui in 2004<\/a>),\nI find the success criteria dubious. For example, Chmiel &amp; Loui write:<\/p>\n\n<blockquote>\n<p>From these observations, it appears that the improvement in debugging time\nshown by the treatment group over the control group is directly related to\ntheir completion of the debugging exercises rather than differences in\naptitude or in program design skills. Students who completed the debugging\nexercises spent significantly less time debugging their programs than those\nwho did not complete the exercises.<\/p>\n<\/blockquote>\n\n<p>However, some of the previous observations include:<\/p>\n\n<blockquote>\n<p>Although the treatment group spent a smaller percentage of time on debugging\nthan the control group, this statistic does not show whether the treatment\ngroup also spent less actual time on debugging the programming assignments.<\/p>\n<\/blockquote>\n\n<p>And<\/p>\n\n<blockquote>\n<p>Students in each group were not consistently spending the same amount of time\non each assignment.<\/p>\n<\/blockquote>\n\n<p>And<\/p>\n\n<blockquote>\n<p>We analyzed the exam scores of students in each group to determine whether\nthere was a difference in aptitude between the groups. &hellip;[T]here was no\nnoticeable difference in the aptitude of the two groups.<\/p>\n<\/blockquote>\n\n<p>This sort of thing seemed common in the papers I read (I had to choose one of\nthem to pick on). Fundamentally, it remains unclear whether this actually\nreduced the amount of time it took for students to debug issues, and it <em>is<\/em>\nclear that none of these studies were able to increase student test scores by\nany statistically significant amount. This would suggest that specific\nstrategies were taught and tested with problems that looked similar.<\/p>\n\n<p>Applying this research to the idea of &ldquo;Kernighan&rsquo;s lever&rdquo; almost invalidates\nthe idea of the lever. If folks were able to increase the edge of their\nknowledge and debug in flow, we&rsquo;d expect a more complete understanding, which\nshould translate to better test scores. To that end, students who were <em>not<\/em>\ngiven specific debugging instruction should have been operating more outside\ntheir skill level and should have therefore increased test scores with some\nsignificance, and that&rsquo;s just not what we see:<\/p>\n\n<blockquote>\n<p>On the first midterm, the treatment group averaged 70.7% while the control\ngroup averaged 72.0%. On the final exam, the treatment group averaged 78.6%\nwhile the control group averaged 74.0%. T-tests showed neither of these\ndifferences to be statistically significant.<\/p>\n<\/blockquote>\n\n<p>Furthermore, the Dreyfus model seems particularly useless for classifying\nprogrammer capability if you want to perform research to figure out how to move\npeople from one end to the other. In Chmiel &amp; Loui, they note that\n&ldquo;[n]ovices make observations &hellip; with no attention to the overall situation.&rdquo;\nThis is a behavior that is almost entirely contradictory to the approach of\nthe expert, who is &ldquo;able to act on intuition&rdquo; and is &ldquo;not limited by the\nrules.&rdquo; The focus then should be on changing behaviors instead of whether\ndebugging time was reduced.<\/p>\n\n<p>All of this together makes it seem like education has a limit, and there is\nsome sort of self-limiting factor on the part of students.<\/p>\n\n<p>McCauley et. al. seem to be aware of this, and in their section on future\nresearch directions, they note:<\/p>\n\n<blockquote>\n<p>The relationships between debugging success and individual factors such as\ngoal orientation, motivation, and other self theories (Dweck, 1999) have\nnot been researched. &hellip; Given that debugging presents challenges for many\nnovices, exploring the influence of self theories on student approaches to\ndebugging appears worthwhile.<\/p>\n<\/blockquote>\n\n<p>Later in 2008, they got their wish.<\/p>\n\n<h2 id=\"dweck-and-self-theory\">Dweck and Self-Theory<\/h2>\n\n<p>In 1999, Carol Dweck published <a href=\"https:\/\/www.amazon.com\/Self-theories-Motivation-Personality-Development-Psychology\/dp\/1841690244\">a book<\/a>\ncalled <em>Self-theories: Their role in motivation, personality and development<\/em>.\nThis work covers over two decades of research conducted by Dweck and others,\nand presents an interesting thesis: an individual&rsquo;s own perception of\nintelligence and how it is obtained directly affects the way they solve\nproblems and interact with others. This perception is called a <em>self-theory<\/em>.<\/p>\n\n<p>An individual&rsquo;s self-theory falls somewhere within a range of mindsets. At\none extreme, we have the <em>fixed mindset<\/em>; at the other end, we find the\n<em>growth mindset<\/em>.<\/p>\n\n<p>The fixed mindset person has a view that intelligence is a fixed resource.\nInternally, this means that when a fixed-mindset person approaches a difficult\nproblem, they give up quickly under the assumption that the solution lies\noutside of their ability. This view is projected onto others: someone else who\n<em>can<\/em> solve the problem has an inherently larger capacity for intelligence.\nWhen required to solve difficult problems, the fixed mindset person may find\na way to evade the issue. They may even become visibly agitated.<\/p>\n\n<p>On the other end of the spectrum, we have growth mindset people, who believe\nthat intelligence is malleable. They recognize problems as opportunities for\nlearning and growth, and solving problems as the only way to capitalize on\nthat opportunity.<\/p>\n\n<p>The practices of both programming and debugging can be compartmentalized into\na single field, a field of problem solving. Some problems are inherently\nchallenging. When viewed from this perspective, an individual with a growth\nmindset is necessarily more capable and productive than an individual with a\nfixed mindset. The reasoning here is that the person with a fixed mindset\nis more concerned with keeping up appearances of a particular level of\nintelligence: they want to appear smart. The easiest way to do this is to\nonly solve problems at or below their skill level. The growth-minded person\nis also concerned with keeping up appearances, but the appearance they want\nrecognition for is hard work. And it turns out the only way to really give\nthe appearance of working hard is to actually work hard.<\/p>\n\n<p>I admit that this sounds a little wishy-washy, but the research and\nexperimentation holds up. I&rsquo;m still digesting it, but I definitely recommend\na talk Allison Kaptur gave as a <a href=\"https:\/\/www.youtube.com\/watch?v=Mcc6JEhDSpo\">keynote speech at Kiwi PyCon 2015<\/a>.\n(She also provides a <a href=\"http:\/\/akaptur.com\/blog\/2015\/10\/10\/effective-learning-strategies-for-programmers\/\">blog post<\/a>\nserving as a rough transcript of the talk.)<\/p>\n\n<p>The talk is titled <em>Effective Learning Strategies for Programmers<\/em>, and I\nhighly recommend reading \/ watching for more information on Dweck&rsquo;s research\nand how it can be practically applied.<\/p>\n\n<h2 id=\"self-theory-and-debugging\">Self-Theory and Debugging<\/h2>\n\n<p>In 2008, Laurie Murphy and Lynda Thomas released a paper\n<a href=\"http:\/\/learningcomputing.org\/murphy.pdf\"><em>Dangers of a Fixed Mindset: Implications of Self-theories Research for Computer Science Education<\/em> (PDF)<\/a>.\nThis work is (as far as I can find) the first to apply Dweck&rsquo;s research to the\npractice of problem solving in computer science. It considers numerous factors\nof how self-theory affects the field of computer science, including:<\/p>\n\n<ul>\n<li>Learning to write programs<\/li>\n<\/ul>\n\n<blockquote>\n<p>[M]ost CS students &hellip; face many challenges and a barrage of negative\nfeedback &hellip; While students with a growth mindset view errors and\nobstacles as opportunities for learning, those with a fixed mindset are\nlikely to interpret excessive negative feedback a as a challenge to their\nintelligence and to avoid similar situations in the future.<\/p>\n<\/blockquote>\n\n<ul>\n<li>The extremely unfortunate situation of gender disparity in CS<\/li>\n<\/ul>\n\n<blockquote>\n<p>Research has also shown that, although high-IQ girls tend to out perform all\nother groups in elementary school, they are also more likely to have a fixed\nmindset. As a consequence, they are less inclined to seek out challenges.<\/p>\n<\/blockquote>\n\n<ul>\n<li>Why collaborative programming practices don&rsquo;t always work<\/li>\n<\/ul>\n\n<blockquote>\n<p>Pair-programming has been shown to boost confidence, improve retention,\nincrease program quality and heighten students\u2019 enjoyment. Students with a\ngrowth mindset &ldquo;feel good about their abilities when they help their peers\nlearn.&rdquo; However, because those with a fixed mindset feel smartest when they\nout perform others, they are less likely to value collaborative learning.<\/p>\n<\/blockquote>\n\n<ul>\n<li>Defensive classroom climates<\/li>\n<\/ul>\n\n<blockquote>\n<p>Those who believe intelligence is fixed seek validation and judge and label\nothers. Such practices are exhibited in CS by students who ask\n&ldquo;pseudo-questions&rdquo; so they can demonstrate their programming knowledge, and\nby instructors who accord special status to students with previous experience.<\/p>\n<\/blockquote>\n\n<ul>\n<li>Psychological success factors like self-efficacy and self-handicapping<\/li>\n<\/ul>\n\n<blockquote>\n<p>A study by Bergin and Reilly looked at both self-efficacy and motivation and\nobserved that intrinsic motivation had a strong correlation with programming\nperformance, as did self-efficacy. They did not, however, establish the\nnature of this link. Self-theories research, which has linked theories of\nintelligence to motivation and self-esteem, may shed light on results from\nCS education.<\/p>\n<\/blockquote>\n\n<p>The paper then goes on to provide suggestions for how teachers may better\nutilize this information to help student retention, handle classrooms where\nstudents have diverse backgrounds, and more. It sets recommendations for future\nresearch about what teachers can do to influence growth mindsets in students.<\/p>\n\n<p>In 2010, <a href=\"http:\/\/ims.mii.lt\/ims\/konferenciju_medziaga\/SIGCSE'10\/docs\/p431.pdf\">Manipulating Mindset to Positively Influence Introductory Programming Performance (PDF)<\/a>\nwas published by Quintin Cutts, Emily Cutts, Stephen Draper, Patrick O&rsquo;Donnel,\nand Peter Saffrey. Though this study admits some faults, it does show that with\nsome particular &ldquo;interventions&rdquo;, students can both improve mindset <em>and<\/em>\ngrades. These interventions included first teaching all students about self-theory\nresearch and its implications in learning; this was called &ldquo;mindset training\nintervention&rdquo;.<\/p>\n\n<p>Some students received a crib-sheet that explained various ways in which they\ncould solve various types of problems generally. When tutors were assisting\nstudents, they were required to assist from the crib-sheet <em>only<\/em>. The idea was\nto remind students at every struggle that general solutions to problems exist\nand to take them through the process of figuring out how to apply a general\nsolution to a specific problem, every time there was a problem. This was called\nthe &ldquo;crib-sheet intervention&rdquo;.<\/p>\n\n<p>Finally, when feedback was received on worksheets \/ problem sets, some students\nreceived &ldquo;rubric intervention&rdquo;. For these students, feedback sheets all\nincluded at the top:<\/p>\n\n<blockquote>\n<p>Remember, learning to program can take a surprising amount of time &amp; effort \u2013\nstudents may get there at different rates, but almost all students who put in\nthe time &amp; effort get there eventually. Making good use of the feedback on\nthis sheet is an essential part of this process.<\/p>\n<\/blockquote>\n\n<p>This intervention was designed to remind students to enter the growth mindset\nwhen they received feedback, which is ostensibly when they would be in a\nposition to learn the most.<\/p>\n\n<p>The study was done in a 2x2x2 matrix form. While the crib-sheet intervention\nwas ineffective alone, only students who received all three interventions\nwere shown to improve test scores. The ineffectiveness of the crib-sheet\nintervention applied alone backs up <a href=\"https:\/\/dl.acm.org\/citation.cfm?id=1404537\">other research<\/a>\nstudying the &ldquo;saying is believing&rdquo; theory and finding that it isn&rsquo;t necessarily\ntrue.<\/p>\n\n<h2 id=\"so-what\">So&hellip; what?<\/h2>\n\n<p>This research fundamentally shows that it is possible to both improve mindset\nin students and improve their scores. By being aware of self-theory and its\nimpleications, approaching problems methodologically and with an open mind, we\ncan improve our abilities in problem solving. And debugging is just a fancy\nword for problem solving.<\/p>\n\n<p>Finally we have a path forward both for educating students and continuing to\neducate ourselves. By approaching problems with a growth mindset, we are\nmuch more likely to learn and grow our skills. If we approach our problems\nwith a fixed mindset, we stunt our growth.<\/p>\n\n<h1 id=\"practice\">Practice<\/h1>\n\n<p>Once we reject the fixed-mindset view that programming is an innate talent\n(and Jacob Kaplan-Moss <a href=\"https:\/\/www.youtube.com\/watch?v=hIJdFxYlEKE\">discusses several reasons<\/a>\nwe might want to do that which have nothing to do with personal growth), we\nare ready to continue our journey to becoming an expert. How do we do this?\nWhat general steps do we take to debug software?<\/p>\n\n<h2 id=\"mental-models\">Mental Models<\/h2>\n\n<p>When we think about or discuss software, we are usually simplifying its\nbehavior in order to better comprehend it. In a\n<a href=\"http:\/\/applicative.acm.org\/speakers\/akleen.html\">talk at ACM&rsquo;s Applicative conference<\/a>\nin 2016, Andi Kleen discussed mental models as they relate to performance\ntuning. One of the foundational points of this talk was that it is actually\nimpossible to fully understand any sufficiently complex software. Why should\nthis be?<\/p>\n\n<p>Software and hardware becomes more complex by handling additional states. The\nnumber of states and state transitions handled scales with the number of\nlogical components of a piece of software, including subsystems, classes,\nthreads, consumed libraries, and even the number of source lines. As all these\nvariables increase, so does the amount of state carried by the system as it\nruns, and we necessarily observe the <a href=\"http:\/\/cswww.essex.ac.uk\/CSP\/ComputationalFinanceTeaching\/CombinatorialExplosion.html\">combinatorial explosion problem<\/a>.\nTrying to reason exactly about such systems is akin to the problem of trying\nto <a href=\"http:\/\/www.emis.de\/proceedings\/PME30\/4\/345.pdf\">understand infinity (PDF)<\/a>.<\/p>\n\n<p>So we must necessarily create mental models of how software should work to\nsimplify the process of understanding it. In software engineering, we even do\nthis as an up-front exercise when designing new software: from product\nrequirements documents to &ldquo;architectural&rdquo; design to technical design\ndocuments, the process begins and completes by following predefined models of\nwhat the software should do.<\/p>\n\n<p>A bug is effectively defined as a disconnect between the expectation of a\npiece of software to behave in some particular way versus how the software\nactually behaved. It follows that one critical component of why bugs occur\nis an incorrect mental model. And the <a href=\"https:\/\/www.cs.cmu.edu\/~NatProg\/papers\/Ko2004SoftwareErrorsFramework.pdf\">research (PDF)<\/a>\nsupports this.<\/p>\n\n<p>Importantly, this means that mental models <em>do not help us<\/em> when debugging\nsoftware. The first step of approaching a software bug is to be critical of\nour own understanding of the buggy code and the code it interacts with. In\nother words, we must accept our knowledge of the system is flawed and discard\nit as faulty. (This is, as you might imagine, more difficult for a\nfixed-mindset individual.)<\/p>\n\n<h2 id=\"the-scientific-method\">The Scientific Method<\/h2>\n\n<p>Software engineering is synonymous with applied computer science. When\nattempting to diagnose and troubleshoot bugs in a system, it is proper to\nfollow the scientific method. As a refresher:<\/p>\n\n<ol>\n<li>Form a hypothesis.<\/li>\n<li>Rigorously test the hypothesis and gather data.<\/li>\n<li>If the data do not support the hypothesis, goto 1.<\/li>\n<\/ol>\n\n<p>Experience leads me to believe that many engineers (myself sometimes\nincluded) follow this process backwards. We are so caught up in trying to fix\nthe underlying problem that we forget about the process of first figuring out\nwhat the underlying problem actually is.<\/p>\n\n<p>To that end, the mistake we often make is treating our investigation as if the\nhypothesis was &ldquo;I think there is a bug in the software.&rdquo; As the presence of a\nbug is usually self-evident, such a hypothesis is already proven. As engineers,\nwe need to come up with a hypothesis of why the bug happens, not that the bug\nexists.<\/p>\n\n<p>To this end, a better initial hypothesis might be something to the effect of,\n&ldquo;I think the bug is on source line 42.&rdquo; This is somewhat better than, &ldquo;I think\nthere is a bug in the software&rdquo; as it provides some specificity to the\nsituation. Sometimes we can test whether line 42 is buggy simply by commenting\nit out! A better hypothesis describes the situation in detail, which means we\nusually need to gather data to form a hypothesis, not just to test it.<\/p>\n\n<p>&ldquo;An off-by-one condition on line 42 causes us to fail to log the bytes received\nfrom the last read call&rdquo; is a fantastic hypothesis. We can test that the issue\nis on line 42. We can test that the bug is solved by looking at our logs.<\/p>\n\n<h2 id=\"investigation\">Investigation<\/h2>\n\n<p>Sometimes we have a good intuition of what the issue is with our code. This\ncould be because some compiler or tool gave us a hint as to the issue; it could\nbe because the bug misbehaved in a very particular way, giving us a clue to its\nnature. This intuition gives us an initial hypothesis (and sometimes coming up\nwith the hypothesis is the hardest part). In this case, we quickly identify a\npossibly buggy section of code and begin to analyze it for a bug.<\/p>\n\n<h3 id=\"intent-versus-syntax\">Intent versus Syntax<\/h3>\n\n<p>When forming a hypothesis, we should begin by reading code for semantics\n&mdash; what it means &mdash; versus syntax &mdash; what it says.<\/p>\n\n<p>Let&rsquo;s take a simple, buggy VCL snippet as an example:<\/p>\n\n<pre><code class=\"language-c\">    sub vcl_fetch {\n        if (beresp.http.authenticated) {\n            set beresp.http.cacheable = &quot;false&quot;;\n            return (pass);\n        }\n\n        return (deliver);\n    }\n<\/code><\/pre>\n\n<p>First, notice there is nothing obviously wrong with this code. We may have an\nintuition that something is wrong here specifically because we found some\nobject cached that we expected this code to avoid caching. So we focus our\nefforts here.<\/p>\n\n<p>If we read this code for what it says, we are literally pronouncing the syntax\nof the code out loud, substituting some syntactic elements with words while\nstripping others. &ldquo;If the backend response header &lsquo;authenticated&rsquo; is set, set\nthe backend response header &lsquo;cacheable&rsquo; to &lsquo;false&rsquo; and return &lsquo;pass&rsquo;.\nOtherwise, return deliver.&rdquo; This tells us nothing new about the nature of the\nbug.<\/p>\n\n<p>If we instead read the code for its intent, we get &ldquo;responses for resources\nthat required authentication must not be cached.&rdquo; In addition to being shorter\nto say (and therefore easier to communicate about with others, should you need\nexternal input), it tells us something else: what the code is <em>supposed<\/em> to do.\nWhen we do not yet have a hypothesis, this can be crucial for figuring out\nwhich areas of the code may be responsible for a bug.<\/p>\n\n<p>Once intent is understood, we can begin to gather data. We might look at the\nresponses for authenticated resources and realize that such resources are\nactually identified by a header called <code>authed<\/code> instead of one called\n<code>authenticated<\/code>. It is only at this point that we have data about the state of\nthe system that we should actually begin to do any syntactic analysis of the\ncode.<\/p>\n\n<h3 id=\"discard-comments\">Discard Comments<\/h3>\n\n<p>When a bug occurs in heavily commented code, ignore the comments. While they\nmay be useful for forming a mental model of what some code is supposed to do,\nreading comments while debugging can subtly reinforce the correctness of\nincorrect code. Consider:<\/p>\n\n<pre><code class=\"language-c\">    \/* Print all the elements *\/\n    for (int i = 0; i &lt;= n; i++) {\n        printf(&quot;%s\\n&quot;, elems[i]);\n    }\n<\/code><\/pre>\n\n<p>You may already see that this is intended to illustrate an off-by-one error.\nThe point here is really that the comment can tend to make us paper over this\ncode. We may think, &ldquo;Yep, this prints all the elements.&rdquo; It does. It also\n&ldquo;prints&rdquo; more than that. Just because a comment is correct in describing the\ncode it annotates does not mean that the code is correct in solving the\nproblem.<\/p>\n\n<h2 id=\"understand-which-bugs-occur\">Understand Which Bugs Occur<\/h2>\n\n<p>The McCauley et. al. literature review finds many different (and some\nconflicting) papers on bug classification. I&rsquo;m not of the opinion that any\nparticular set of classifications is better or worse than any other (assuming\nboth are relatively complete). That said, agreeing on a standard terminology\nwithin a team is important so that we can efficiently and effectively\ncommunicate with others about problems.<\/p>\n\n<p>I&rsquo;ll cover a few classifications I find useful.<\/p>\n\n<h3 id=\"syntax-semantic-bugs\">Syntax \/ Semantic Bugs<\/h3>\n\n<p>Some of the most obvious bugs have to do with not following the syntax or\nsemantics defined by the programming language we&rsquo;re using.  Because our tools\ncan already assist us a great deal here, we tend to think of these problems as\ntrivial. This is a mistake.<\/p>\n\n<p>Although syntax errors are nearly always caught at compile time, languages\n(like PHP) which do not necessarily distinguish between compile time and run\ntime may confuse this a bit. (This is one reason a robust testing environment\nis important, regardless of your preferred testing strategy.) Weakly typed\nlanguages may be unable to perform type conversions at run time. In some cases,\nstatic and dynamic analysis tools for our platform can help.<\/p>\n\n<p>A good awareness of our environment always includes an understanding of the\ncapabilities of our language and tools. When these issues hit at run time, they\ncan be very hard to spot because we heavily rely on our tools to figure these\nsorts of issues out for us. Be prepared to learn more about your environment\nwhen you encounter new issues like this.<\/p>\n\n<h3 id=\"logic-bugs\">Logic Bugs<\/h3>\n\n<p>This is a broad set of bugs with many sub-classifications. Off-by-one errors,\noverflows and underflows, and control flow errors (like early returns,\nincorrect conditionals, using logical or when you meant logical and &mdash; or\nvice versa) are all examples.<\/p>\n\n<p>It may be unfortunate to group all of these different bugs into a single\ncategory, but I prefer to do this because they are all related to a bug in our\nexpression of logic in a program form. Furthermore, they all tend to behave\ndifferently enough from each other that they&rsquo;re unlikely to be mistaken for\neach other.<\/p>\n\n<h3 id=\"race-conditions\">Race Conditions<\/h3>\n\n<p>Though these are arguably logic bugs, I call out race conditions separately for\na few reasons:<\/p>\n\n<ol>\n<li>They&rsquo;re frequently not reproducible with synthetic workloads.<\/li>\n<li>A race condition is not necessarily indicative of a bug.<\/li>\n<li>The absense of race conditions is not necessarily indicative of\ncorrect software.<\/li>\n<li>Race conditions can appear as a result of logic bugs elsewhere.<\/li>\n<li>Race conditions can mask other bugs.<\/li>\n<\/ol>\n\n<p>Especially because of the facts that race conditions can be more symptomatic\nthan original in nature, and because this has the effect of masking other more\nfundamental bugs, I find it useful to think about them as a separate category.<\/p>\n\n<h3 id=\"performance-bugs\">Performance Bugs<\/h3>\n\n<p>Premature optimization may be bad, but not performing to SLA or capacity is\nworse. Performance problems tend to be particularly insidious because they&rsquo;re\ntypically very hard to work around. This is likely because such issues tend to\nbe deeply seeded in software design and highly dependent on the problem space.\nIn addition to this, performance gains in existing systems are frequently found\nby changing assumptions of the complexity of the problem being solved.<\/p>\n\n<p>To avoid being bitten by unsolvable performance problems, try to make sure your\ninterfaces are composable. Design systems that can be independently tested and\nvalidated such that you can capacity plan. Avoid sharing state through memory.\nWhen this is not possible, use mutexes only when they are unlikely to have\nhigh contention. In highly contended workloads, attempt to make use of wait-\nand lock-free solutions where possible.<\/p>\n\n<h3 id=\"and-many-more\">&hellip;and many more<\/h3>\n\n<p>When we accept that &ldquo;debugging&rdquo; is a domain-specific term for problem solving,\nit follows that a &ldquo;bug&rdquo; is a domain-specific term for a problem. Unexpected\nbehaviors in software may also be caused by (or at least attributed to) a\nnumber of areas outside of the code itself:<\/p>\n\n<ul>\n<li>Protocol bugs: the software you are implementing is specified incorrectly\nsuch that an implementation to the specification cannot possibly fulfill\nthe stated goals of the specification.<\/li>\n<li>Process bugs: a failure to incorporate accepted best-practices into an\nindividual or organizational project. This includes lack of version\ncontrol, not performing code reviews, absence and aversion to testing, etc.<\/li>\n<li>Environmental bugs: problems with the tools we use when designing, building,\nand running our software may end up affecting how our software runs.\nFrom compiler bugs to kernel issues, library bugs to hardware bugs, any\nnumber of issues in our environment may affect the run-time behavior of our\nsoftware. These issues tend to be insidious; we usually assume our operating\nplatform to be infallible. It&rsquo;s not, and this is why actual full-stack\nunderstanding is necessary.<\/li>\n<\/ul>\n\n<h2 id=\"behavioral-patterns\">Behavioral Patterns<\/h2>\n\n<p>It turns out that these classifications are more than just a taxonomy. Though\nthe above classifications seem to be solely based on how a bug was introduced\ninto the system, it turns out that nearly all of these classifications have\nrun-time signatures as well. In other words, we can observe patterns of\nbehavior from a single sort of bug.<\/p>\n\n<p>For example, an off-by-one bug is always going to do something one fewer or one\nmore time than expected (whether that &ldquo;something&rdquo; is &ldquo;scan a string&rdquo; or &ldquo;count\nreferences&rdquo;). Once we understand the reason for this bug, we can generalize:\nmaybe there are special cases where we could have off-by-N. And indeed, in C\nthis is usually a thing that happens iterating over an array of structs.<\/p>\n\n<p>But first we have to be able to observe these patterns.<\/p>\n\n<h2 id=\"tooling\">Tooling<\/h2>\n\n<p>Tools and introspection are crucial to the debugging process. As both the\nprocesses of forming and testing a hypothesis require analyzing data, we need\ntools to collect, analyze, and understand the data. Relevant tools differ for\nnearly every project, but include things like:<\/p>\n\n<ul>\n<li>Software to read statistics counters from the buggy application (like\n<code>varnishstat<\/code>).<\/li>\n<li>Graphing \/ plotting statistics over time (using something like Ganglia or\nGraphite).<\/li>\n<li>Debuggers like gdb, lldb, or any other language \/ platform-specific\ndebugger.<\/li>\n<li>Profiling utilities like <code>perf<\/code> or <code>hwpmc<\/code>.<\/li>\n<li>Tracing facilities like <code>dtrace<\/code> or <code>lttng<\/code>.<\/li>\n<\/ul>\n\n<p>I can&rsquo;t stress enough the importance of recording metrics over time. At\n<a href=\"https:\/\/www.fastly.com\">Fastly<\/a> we use <a href=\"http:\/\/ganglia.info\/\">Ganglia<\/a>,\n<a href=\"https:\/\/www.datadoghq.com\">DataDog<\/a>, and other tools to gather data over time\nand track changes. As far as recognizing patterns go, visualizations really\ncan&rsquo;t be beat.<\/p>\n\n<p>The useful tools vary by project, so it&rsquo;s not super useful to discuss them in\nany real detail. What is important is to understand that you will need to\ngather data for both forming and testing your hypothesis, and understanding\nthe tools available in your domain gets you halfway there.<\/p>\n\n<p>Importantly, not every tool fits the task. Be prepared to extend your software\nto provide additional information, as well as to write your own tools to gather\nand analyze this information.<\/p>\n\n<h1 id=\"application\">Application<\/h1>\n\n<p>What does this look like in the real world? In practice, we encounter cases\nwhere we&rsquo;re looking at problems extending far past our experience. Bugs, as\nKernighan might say, we are not yet clever enough to debug. In some cases,\nwe may be extremely time-constrained in how long we have to solve an issue.\nIn yet other cases, the tools we have may not even be useful.<\/p>\n\n<p>Our workload at Fastly is unique. Our Varnish runs at a thread load between\n40000-60000 threads; it consumes in some of our busier POPs over 300GB RAM.\nThis comes with all sorts of problems:<\/p>\n\n<h2 id=\"the-maps-debacle\">The Maps Debacle<\/h2>\n\n<p>Many debugging tools on Linux rely on the <code>\/proc\/[pid]\/maps<\/code> file to gather\ninformation about mapped memory regions in the executable. In releases of the\nLinux kernel from 3.2 to 4.5, some code to annotate which maps belonged to\nthread stacks actually resulted in O(n^2) complexity, iterating over maps. A\ngood analysis of this problem is available on the\n<a href=\"http:\/\/backtrace.io\/blog\/blog\/2014\/11\/12\/large-thread-counts-and-slow-process-maps\/\">backtrace.io blog<\/a>.\nReading this file on our systems took <em>minutes<\/em>.<\/p>\n\n<p>Fundamentally, this meant rolling releases of libraries like <code>libunwind<\/code> and\n<code>perf<\/code> to use <code>\/proc\/&lt;pid&gt;\/task\/&lt;pid&gt;\/maps<\/code> (which does not suffer from this\nissue). It also resulted in <a href=\"https:\/\/github.com\/jemalloc\/jemalloc\/pull\/227\">sending patches<\/a>\nupstream to <code>jemalloc<\/code>.<\/p>\n\n<h2 id=\"the-gdb-fiasco\">The GDB Fiasco<\/h2>\n\n<p>Both GDB and LLDB have a similar polynomial-time issue when attaching to\nprocesses with large numbers of threads. This is largely due to how both\nutilities have decided to structure contexts about threads they are attached\nto. The last time I tried to attach GDB to one of our Varnish instances, it\ntook over 4 hours and still hadn&rsquo;t finished attaching. I tried to cancel this\noperation and panicked the kernel.<\/p>\n\n<p>To this end, we&rsquo;re using other tools for runtime introspection that I will\ntalk about later. And you might ask why not just grab a core of the process\nand analyze it offline&hellip;<\/p>\n\n<h2 id=\"the-core-dump-disappointment\">The Core Dump Disappointment<\/h2>\n\n<p>Our cache nodes have 768GB RAM. Varnish uses several hundred gigabytes of this\nmemory, and most of it is actually not at all related to object storage, but\ninstead to thread stacks, various memory pools, and metadata. Our root\nfilesystems generally have about 150GB free space. This means we do not have\nenough disk space to store core dumps if Varnish crashes. We could, of course,\noffline one of our SSDs, but it is not economically sensible for us to waste\n500GB-2TB of space to leave room for core dumps.<\/p>\n\n<p>Besides, it actually takes a ridiculously long time for the kernel to dump a\ncouple hundred gigs of memory into a file. And even then, it would take over\nan hour to copy the dump off of the machine at 10Gbit speed, compressed.<\/p>\n\n<p>Here, we are using some software from <a href=\"https:\/\/www.backtrace.io\">backtrace.io<\/a>\nto solve the problem. I highly recommend using their utilities if you&rsquo;re\nworking on systems software, regardless of your software architecture and\nmemory usage.<\/p>\n\n<h2 id=\"carrying-on\">Carrying On&hellip;<\/h2>\n\n<p>Modifying existing utilities to work around system bottlenecks is a little\nfrustrating, but being fundamentally unable to do any runtime debugging is\nmaddening. To that end, we&rsquo;ve come up with creative ways to gather information\nfrom our system at low operational overhead. Our strategy here is frequently to\nhave these features be things we can leave &ldquo;always on&rdquo; as turning them on\nduring an outage situation may lose valuable originating context. To this end,\nthe solutions must be low-overhead.<\/p>\n\n<p>I have <a href=\"https:\/\/9vx.org\/post\/ripping-rings\/\">blogged about librip<\/a> this year,\ndetailing the software and the problems it solves. To summarize, librip is a\n&ldquo;minimal-overhead API for instruction-level tracing in highly concurrent\nsoftware&rdquo; and it enables us to get execution histories of threads at runtime.\nThis granularity isn&rsquo;t quite to the level of providing a backtrace, but it gets\nclose. And it has helped us solve deadlocks, livelocks, stalls, and performance\nissues.<\/p>\n\n<p>Tracing is another point where custom tooling can be extremely useful. Several\ngreat existing technologies exist on this front, including <a href=\"https:\/\/en.wikipedia.org\/wiki\/DTrace\">DTrace<\/a>\nand <a href=\"https:\/\/lttng.org\/\">LTTng<\/a>. Tools like DTrace are fantastic as they\noperate without any overhead when disabled. However, the dynamic nature of\nDTrace makes its runtime overhead considerable when generating\nhigh-frequency traces, which is where instrumentation-oriented tools like\nLTTng become useful. (DTrace can do this too, but then you&rsquo;re responsible for\nwriting the instrumentation hooks as well as dealing with the overhead of\ninstrumenting.)<\/p>\n\n<p>In any case, tracing information can be a firehose: it spits out a ton of\npotentially useful information, but figuring out which bits of the information\nare actually useful can be difficult. Usually we&rsquo;re looking for a couple\ndifferent things:<\/p>\n\n<ul>\n<li>Long tail trace information. 99th percentile trace events are common in\nsystems handling tens of thousands of events per second. Figuring out the\n99.999th percentile is more difficult, as <a href=\"http:\/\/danluu.com\/perf-tracing\/\">described by Dan Luu<\/a>.<\/li>\n<li>Correlating events. When we notice some long tail traces, what other traces\nare responsible? For example, if we spent a long time waiting to acquire a\nmutex, we may be interested to correlate this time with the original holder\nof the mutex.<\/li>\n<\/ul>\n\n<p>I&rsquo;m working on some tracing tools that use local trace buffers to scribble\ninformation into, and logging entire trace buffers at a time. The idea is that\nit is easier to determine whether any individual trace chain is interesting\nthan it is to determine whether any individual trace is interesting in terms of\ndata retention. It&rsquo;s my hope that this will be successful, and I will surely\nblog about it if so.<\/p>\n\n<h1 id=\"wrapping-it-up\">Wrapping it Up<\/h1>\n\n<p>This has been a ridiculously long post. To summarize:<\/p>\n\n<ul>\n<li>Recent research provides us evidence that the way we think about problems\ndirectly effects our ability to learn and how we interact with others about\nthese problems.<\/li>\n<li>There are ways that we can improve our mindset (and influence the mindsets\nof those around us) such that we optimize our experience for learning,\nproblem solving, and success.<\/li>\n<li>Debugging is a special case of problem solving, and benefits hugely from\nshifts in mindset.<\/li>\n<li>Through classifying bugs, recognizing patterns, and using tools, we are able\nto come up with a general strategy for solving problems in our code, based\non the scientific method.<\/li>\n<li>Understanding this methodology prepares us to ask better questions about\nwhat data we need to solve problems. This allows us to extend existing and\nauthor new tools to help us in our journey.<\/li>\n<\/ul>\n\n<p>I&rsquo;m extremely interested in this subject in general. Look for more posts\nrelated to debugging and pedagogical topics in the (hopefully near) future!<\/p>\n"},{"title":"Ripping Rings","link":"https:\/\/9vx.org\/post\/ripping-rings\/","pubDate":"Wed, 16 Mar 2016 15:07:35 +0000","guid":"https:\/\/9vx.org\/post\/ripping-rings\/","description":"\n\n<p>TL;DR, I&rsquo;ve released a &ldquo;library&rdquo; called <a href=\"https:\/\/github.com\/fastly\/librip\" title=\"librip\">librip<\/a>: a &ldquo;minimal-overhead API for\ninstruction-level tracing in highly concurrent software&rdquo;. The rest of this post\nis about the &ldquo;why&rdquo; of the software, followed by a slightly a little bit of the\n&ldquo;what&rdquo; and only a smidge of the &ldquo;how&rdquo;.<\/p>\n\n<blockquote>\n<p>Everyone knows that debugging is twice as hard as writing a program in the\nfirst place. So if you&rsquo;re as clever as you can be when you write it, how will\nyou ever debug it?<\/p>\n<\/blockquote>\n\n<p>I&rsquo;ve previously included this Brian Kernighan quote from\n<a href=\"https:\/\/www.amazon.com\/The-Elements-Programming-Style-Edition\/dp\/0070342075\" title=\"The Elements of Programming Style\">The Elements of Programming Style<\/a> before in my writing (I likely will\nagain) &ndash; it&rsquo;s as true today as it was over forty years ago when it was\npublished. However, it is not all despair. The quote ends with a question, and\nquestions beg for answers. And fundamentally, that&rsquo;s what I do in my capacity\nas a software engineer. The answer to this particular question is to always be\nlearning &ndash; you have to level-up your clever.<\/p>\n\n<p>But I&rsquo;m also not always debugging my own code. I suppose this means that I have\nto be twice as clever as the original author was when writing the code to\ndebug the code. In my experience, this usually results in code deletion and\nsimplification &ndash; which is the underlying point of the context surrounding the\nquoted passage above.<\/p>\n\n<p>In my work at <a href=\"https:\/\/www.fastly.com\/\" title=\"Fastly\">Fastly<\/a>, there&rsquo;s rarely a shortage of things to debug.\nWhether the Internet-at-large has managed to trigger some pathological edge\ncase or I&rsquo;m working on new features, debugging is always in the picture. And\nI can&rsquo;t always be twice as clever. In such cases, I&rsquo;m usually aided by the\ntrusty debugging toolset: gdb, perf, strace, lsof, etc. But here&rsquo;s the catch:\nsometimes these tools are entirely unusable.<\/p>\n\n<h2 id=\"tens-of-thousands-of-threads\">Tens of Thousands of Threads<\/h2>\n\n<p>It&rsquo;s no secret that we at Fastly use a modified <a href=\"https:\/\/www.varnish-cache.org\/\" title=\"Varnish\">Varnish Cache<\/a> to\nfacilitate our content delivery. What&rsquo;s less well known is how it works.\nWhen Varnish was originally implemented, the idea was that threads were cheap,\nand that operating systems were going to do a better job scheduling them than\nyou were. This is (to a large degree) true today, but it comes at a cost.<\/p>\n\n<p>On our caches, our Varnish processes run with tens of thousands of threads in\nthe normal case. And it turns out that the kernel does a reasonably good job\nwith this workload. When most of your time is spent writing data over a\nnetwork, the kernel does a pretty reasonable job of keeping everyone running\nwithin acceptable latency bounds. But as with Biggie&rsquo;s money, with more\nthreads, we do get some more problems.<\/p>\n\n<h3 id=\"composability\">Composability<\/h3>\n\n<p>Lock-based synchronization is nearly the canonical solution to sharing memory\nin concurrent software today. But there are significant drawbacks, especially\nin heavily threaded environments.<\/p>\n\n<p>In a <a href=\"https:\/\/queue.acm.org\/detail.cfm?id=1454462\" title=\"Real-world Concurrency\">2008 ACM Queue article, Real-world Concurrency<\/a>, Bryan Cantrill points\nout that it is possible to write composable software built on top of mutexes.\n(I do find it funny that one of the suggested ways of doing this is to not use\nmutexes.)<\/p>\n\n<p>Anyway, composability is a desirable property because such systems are easier\nto build, consume, and verify (and there is plenty of literature about this for\nnearly any language). While it may be possible to write composable software\nthat consumes locks, this isn&rsquo;t actually the problem. The problem is that locks\nthemselves do not compose. Why not?<\/p>\n\n<p>Locks are not self-contained. Although lock and unlock functions work on\ndiscrete structures, they are relatively tightly coupled both to logic\ndescribed in code as well as to the data they are meant to protect. This means\nthat programmers are required to understand implementation details of APIs\nsurrounding code which may or may not need protection, and may or may not\nresult in locks being held on return.<\/p>\n\n<p>Secondly, lock operations can&rsquo;t be recombined in any order to produce valid\nexecution histories. Lock operations must precede unlocks. Locks cannot\nrecurse. (If they can, are they unlocked once or many times?) Locks cannot be\nacquired and released in arbitrary orders. Nothing about their API or\nimplementation provides guidance on what order and where locks must be\nacquired.<\/p>\n\n<p>So while it is possible to hide locks behind a composable API, the code in the\nAPI itself isn&rsquo;t guaranteed to be composable. Indeed, at the lowest level, it&rsquo;s\n<a href=\"http:\/\/download.oracle.com\/sunalerts\/1001013.1.html\" title=\"Security Vulnerabilities in Solaris Kernel Statistics Retrieval Process May Allow a Denial of Service (DoS)\">clear that implementing correct, composable APIs which consume locks<\/a>\n(sorry Bryan, I had to) is at best non-trivial.<\/p>\n\n<h3 id=\"contention\">Contention<\/h3>\n\n<p>Let&rsquo;s assume we have some API that isn&rsquo;t at all OpenSSL. (Just kidding, it is.)\nBut let&rsquo;s assume it isn&rsquo;t, because I need a strawman API that has a correct\nimplementation that uses locks and is composable.<\/p>\n\n<p>Even when implemented correctly, this API may cause the software (and in some\ncases the whole system) to enter a state known as livelock.<\/p>\n\n<p>In livelock, your software is spending so much time negotiating lock transfer\nthat it doesn&rsquo;t actually get any work done. Your system continues to run, but\nappears to do nothing. I have recently encountered a livelock where the average\nlock hold time was something like 10 minutes because the processes owning the\nlock couldn&rsquo;t get any time on the scheduler. (And once they did, other\nprocesses suddenly needed those locks as well.)<\/p>\n\n<h3 id=\"concurrency\">Concurrency<\/h3>\n\n<p>Given these problems, we might care to use a different approach to sharing\nmemory. We may want to explore lock-free and wait-free algorithms as an\nalternative to lock-based approaches. So we dust off <a href=\"https:\/\/concurrencykit.org\/\" title=\"Concurrency Kit\">Concurrency Kit<\/a> and\ngo hacking.<\/p>\n\n<p>Problematically, nearly all of our problems are multiple producer, multiple\nconsumer (MPMC) problems. And in unmanaged languages like C, many (all?) MP and\nMC problems require some form of safe memory reclamation (SMR). Many SMR\nschemes have an approach that goes something like this:<\/p>\n\n<ul>\n<li>Stash pointers in a thread local area.<\/li>\n<li>Mark unused stashed pointers as such.<\/li>\n<li>Wait for the system to enter a quiescent state.<\/li>\n<li>Free any unused pointers.<\/li>\n<\/ul>\n\n<p>The actual approach for stashing, marking, and detecting quiescent states\nvaries, but this is the gist. When you have tens of thousands of threads,\ndetecting a quiescent state is very difficult. Even in schemes that make it\neasier, the <code>O(mn)<\/code> complexity of scanning pointers in thread local areas\nmakes many of these nearly unusable in environments with such high thread\ncounts.<\/p>\n\n<p>&ldquo;But dho!&rdquo;, you say. &ldquo;Just use reference counts!&rdquo; Ignoring entirely the cache\nunfriendliness of reference counting schemes (and therefore poor throughput\nwith high numbers of objects), when we require non-global, per-object\nreferences (such that the reference is embedded into the memory of the\nprotected item), we&rsquo;re back to locking.<\/p>\n\n<p>So we put Concurrency Kit back on the shelf and hope it doesn&rsquo;t collect too\nmuch dust before the next time we can use it.<\/p>\n\n<h3 id=\"debugging\">Debugging<\/h3>\n\n<p>Have you ever tried to attach gdb to a process with more than 40,000 threads\nand 500GB resident memory usage? I&rsquo;ll wait. Actually, I won&rsquo;t, because it&rsquo;s\ngoing to take you somewhere between 45 minutes and 3 hours. (Don&rsquo;t bother with\nlldb, it&rsquo;s the same problem.) I&rsquo;ve theorized that this is due to a combination\nof <a href=\"http:\/\/backtrace.io\/blog\/blog\/2014\/11\/12\/large-thread-counts-and-slow-process-maps\/\" title=\"High Thread Counts and Slow Process Maps\">\/proc\/&lt;pid&gt;\/maps suckage<\/a> and something between linear and polynomial\ntime complexity of attaching to threads in the debugger itself. (It walks a\nlinked list of threads multiple times for each thread it attaches to.)<\/p>\n\n<p><a href=\"http:\/\/backtrace.io\/\" title=\"backtrace.io\">Backtrace.io crash dumps<\/a> help for some cases, but the overhead of tracing\nthis many threads is fundamentally non-trivial, so we can&rsquo;t (yet) use their\ntools for tracing live processes. That said, when our software does crash,\ntheirs is the only tool that works for us.<\/p>\n\n<hr \/>\n\n<p>So now we have lock-based software with tens of thousands of threads that we\ncan&rsquo;t debug. Twice as hard as writing the code, indeed. In the absence of\ncrash dumps, determining cause of bugs can be excruciatingly difficult. And in\nsuch highly threaded software, the likelihood that a bug is related to\ncomposability, contention, or concurrency is actually rather high.<\/p>\n\n<p>Outside of contention, many concurrency bugs are the result of a bad execution\nhistory. Deadlocks occur when locks are acquired and \/ or released out of\norder. Race conditions occur when execution histories happen out of order with\nrespect to a protocol defining a required ordering. If we could somehow observe\nexecution histories, that would be pretty close to ideal for debugging numerous\nissues.<\/p>\n\n<p>But how do we get this without a debugger? We sort of write our own.<\/p>\n\n<h2 id=\"ripping-rings\">Ripping Rings<\/h2>\n\n<p>I&rsquo;ve released a new &ldquo;library&rdquo;, <a href=\"https:\/\/github.com\/fastly\/librip\" title=\"librip\">librip<\/a>: a &ldquo;minimal-overhead API for\ninstruction-level tracing in highly concurrent software&rdquo;. This code was (when I\nwrote it) originally baked into our Varnish implementation, where it was known\nas &ldquo;rip_ring&rdquo;. <em>Rip<\/em> because <code>%rip<\/code> is the name of the 64-bit program counter\n(PC) on x86-64 machines, <em>ring<\/em> because snapshots of this value are stored into\na shared memory ring buffer exposed through Linux <code>tmpfs<\/code>. (Although it is\ncurrently extremely platform-specific, ports to other architectures and systems\nare welcomed.)<\/p>\n\n<p>The software is comprised of two components. One is an API for actually\nsampling the PC into this shared memory ring buffer. The other is a script that\ncan process the file and resolve addresses to symbols in the binary.<\/p>\n\n<p>And how does it work?<\/p>\n\n<p>Pretty goddamn well, to be perfectly honest. The overhead is pretty low: there\nare a few bitwise operations performed on some thread-local variables, and then\nstuff is written to memory.<\/p>\n\n<p>Since the shared memory is backed by a <code>tmpfs<\/code>, if it is mapped with\n<code>MAP_SHARED<\/code>, it survives crashes. This means we get the recent execution\nhistory of every thread active in the system (plus exited threads) on a crash.\nIt&rsquo;s not quite a backtrace, but it&rsquo;s close.<\/p>\n\n<p>PC samples are littered strategically through the code. For example, our\nlocking function interface is modified:<\/p>\n\n<pre><code>#define Lck_Lock(l)                                            \\\n        do {                                                   \\\n                rip_snap();                                    \\\n                Lck__Lock((l), __func__, __FILE__, __LINE__);  \\\n                rip_snap();                                    \\\n        } while (0)\n\n#define Lck_Unlock(l)                                          \\\n        do {                                                   \\\n                Lck__Unlock((l), __func__, __FILE__, __LINE__);\\\n                rip_snap();                                    \\\n        } while (0)\n<\/code><\/pre>\n\n<p>At any particular time, this gives us all threads contending on a mutex, tells\nus which thread is the mutex owner, and gives us all threads that have recently\nreleased the mutex.<\/p>\n\n<p>What does this look like? Depends what&rsquo;s wrong. A livelock is exposed in the\ndefault condensed output of the <code>read_rip<\/code> utility (written by\n<a href=\"https:\/\/github.com\/jhatala\" title=\"Jozef Hatala\">a colleague of mine<\/a>):<\/p>\n\n<pre><code>locking_function : 19126 threads, 2 locations: ssl.c:220 (10); ssl.c:210 (19116)\n<\/code><\/pre>\n\n<p>Also remember, this is hanging out in shared memory. So you can use this tool\nto immediately rule out livelock as a runtime performance problem without\npausing your software to attach a debugger or using tools that add system-wide\ntrace-time overhead.<\/p>\n\n<p>Lock order reversals and deadlocks are trickier. The nice thing is that the\ndebugging information puts both <code>rip_snap()<\/code> calls on the same source line, but\nthey (of course) get different addresses. So we can get per-thread output like:<\/p>\n\n<pre><code>[35283]: {82}source_a.c:124&lt;0x442bae&gt;, {83}source_a.c:129, {84}source_b.c:2670,\n    {85}source_b.c:217, {86}source_b.c:662, {87}source_a.c:124&lt;0x442af7&gt;,\n    {88}source_a.c:124&lt;0x442bae&gt;, {89}source_a.c:129, {90}source_b.c:767,\n    {91}source_c.c:132, {92}source_c.c:134&lt;0x477f12&gt;, {93}source_c.c:144&lt;0x477fa3&gt;,\n    {94}source_b.c:110, {95}source_b.c:3873, {96}source_b.c:3405,\n    {97}source_d.c:339&lt;0x45f617&gt;, {98}source_d.c:339&lt;0x45f6a3&gt;, {99}source_d.c:341,\n    {100}source_c.c:238&lt;0x479f52&gt;, {101}source_c.c:238&lt;0x479fe0&gt;,\n    {102}source_c.c:240, {103}source_b.c:3405, {104}source_b.c:2773,\n    {105}source_a.c:124&lt;0x442af7&gt;, {106}source_a.c:124&lt;0x442bae&gt;, {107}source_a.c:129,\n    {108}source_a.c:124&lt;0x442af7&gt;, {109}source_a.c:124&lt;0x442bae&gt;, {110}source_a.c:129,\n    {111}source_e.c:573&lt;0x43be8f&gt;, {112}source_e.c:573&lt;0x43bf1b&gt;,\n    {113}source_a.c:124&lt;0x442af7&gt;\n<\/code><\/pre>\n\n<p>And this works quite well. Given our lock and unlock interfaces are the only\nones with multiple <code>rip_snap()<\/code> calls mapping to the same source line, we can\ncreate neat little scripts, like this one that <a href=\"https:\/\/github.com\/hachi\" title=\"Jonathan Steinert\">a colleague of mine<\/a>\nwhipped up in a couple of minutes. It scrapes output to help find deadlocks by\nproviding a list of threads that have acquired locks, but not released them.<\/p>\n\n<pre><code>#!\/usr\/bin\/perl\n\nuse strict;\nuse warnings;\n\nmy $sf = shift; # source file with lock\n\nwhile (my $l = &lt;&gt;) {\n  next unless $l =~ \/\\Q$sf\\E\/;\n  next unless $l =~ \/\\{\\d+\\}\\Q$sf\\E:(\\d+)&lt;0x[0-9a-f]+&gt;, \\{\\d+\\}\\Q$sf\\E:\\1&lt;0x[0-9a-f]+&gt;\/;\n  next if $l =~\n      \/\\{\\d+\\}\\Q$sf\\E:(\\d+)&lt;0x[0-9a-f]+&gt;, \\{\\d+\\}\\Q$sf\\E:\\1&lt;0x[0-9a-f]+&gt;, .*\\{\\d+\\}\\Q$sf\\E\/;\n  print $l;\n}\n<\/code><\/pre>\n\n<p>Super cool.<\/p>\n\n<hr \/>\n\n<p>The implementation details aren&rsquo;t that interesting. We do some pointer packing\nsuch that event order can be reconstructed linearly. Ring buffers wrap around,\nbut they are also FIFO, so with counters, we can reconstruct the correct event\nordering. I expect this framework to be useful in any highly-concurrent\nscenario. Whether using native threads or coroutines, livelocks and deadlocks\nare real problems as long as you employ lock-based synchronization plans to\nsolve the problem of sharing memory.<\/p>\n\n<p>Other than that, the utility of this thing is all in how you use it. I&rsquo;d\nappreciate any feedback, and especially code to make this useful on other\narchitectures (especially ones with weaker memory models than x86-TSO).<\/p>\n"},{"title":"On Window TinyLFU","link":"https:\/\/9vx.org\/post\/on-window-tinylfu\/","pubDate":"Wed, 09 Mar 2016 18:06:34 +0000","guid":"https:\/\/9vx.org\/post\/on-window-tinylfu\/","description":"\n\n<p>I was first introduced to the Window TinyLFU algorithm through a\n<a href=\"https:\/\/github.com\/ben-manes\/caffeine\/wiki\/Efficiency\" title=\"Efficiency\">wiki entry for the caffeine project<\/a>.<\/p>\n\n<p>The graphs looked neat, but it took actually reading the Einziger and Friedman\npaper, <a href=\"http:\/\/www.cs.technion.ac.il\/~gilga\/TinyLFU_PDP2014.pdf\" title=\"TinyLFU: A Highly Efficient Cache Admission Policy\">TinyLFU: A Highly Efficient Cache Admission Policy<\/a> to understand\njust how interesting and simple the algorithm actually is.  Unlike many papers\nin computer science, this one is extremely consumable for those (like myself)\nlacking a strong math background.<\/p>\n\n<p>I&rsquo;ve been studying this algorithm with interest, since it is quite relevant to\nmy work at <a href=\"https:\/\/www.fastly.com\" title=\"Fastly\">Fastly<\/a>. I&rsquo;ve a few ideas for possible extensions, as well as\nsome for efficient lock-free implementations. But first, let&rsquo;s talk about\ncache eviction and how TinyLFU works.<\/p>\n\n<h2 id=\"lru\">LRU?<\/h2>\n\n<p>Why not just use LRU and be done with it? There are a few practical problems\nwith LRU:<\/p>\n\n<ul>\n<li>LRU is not scan resistant. What this means is that, given a cache of N\nitems, an attacker can send N+1 requests in sequence. Once an initial\nscan is completed, all subsequent scans will result in cache misses.<\/li>\n<li>Every hit requires moving objects in the queue. When queues are dynamically\nallocated, this results in high cache miss overhead, TLB thrashing, and\nmemory writes.<\/li>\n<li>With high request loads comes high lock contention.<\/li>\n<\/ul>\n\n<p>Because of these problems, LRU implementations frequently employ clever tricks\nto reduce lock contention. This can be achieved with a few strategies:<\/p>\n\n<ul>\n<li>Don&rsquo;t promote items that were recently promoted. (They&rsquo;re probably still\nnear the front of the LRU.)<\/li>\n<li>Don&rsquo;t promote items in the face of lock contention.<\/li>\n<\/ul>\n\n<p>Under high load, these strategies (coupled with the lack of scan resistance)\ncombine in a way that results in dropping items that have some regular access\nfrequency. These are items that we always want to have cached &ndash; so shouldn&rsquo;t\nwe consider usage frequency as part of our caching strategy?<\/p>\n\n<h2 id=\"lfu-background\">LFU Background<\/h2>\n\n<p>Access frequency seems like a good thing to use as a cache eviction strategy.\nIt even seems like a pretty close approximation to the clairvoyant algorithm:\ncache every item that is frequently used.<\/p>\n\n<p>Easier said than done.<\/p>\n\n<h3 id=\"aging\">Aging<\/h3>\n\n<p>The first problem is one of aging. Frequency isn&rsquo;t a useful absolute metric\nin terms of knowing which objects are worth keeping. This is especially true\nfor sites like Imgur, where front-page images will see many impressions on\none day, and then effectively never be accessed again. To address this, we\nneed some method of aging hit counts. This means that over time, the weight\nof a hit decreases by some factor.<\/p>\n\n<p>How do we determine how often to age? How do we determine a good weight to\nuse?<\/p>\n\n<p>The <a href=\"http:\/\/www.squid-cache.org\" title=\"Squid Cache\">Squid proxy<\/a> provides links to a couple papers at their\n<a href=\"http:\/\/www.squid-cache.org\/Doc\/config\/cache_replacement_policy\/\" title=\"cache_replacement_policy Squid documentation\">cache_replacement_policy<\/a> documentation that describe two approaches used\nfor aging when using frequency as a cache metric.<\/p>\n\n<h3 id=\"memory-overhead\">Memory Overhead<\/h3>\n\n<p>Keeping an access frequency on cached objects is common. To effectively use a\nfrequency-based caching strategy, we also have to remember the access frequency\nof objects we <em>no longer have in cache<\/em>. In a system that sees and forgets about\nbillions of distinct objects per day, the memory overhead of maintaining\ncounters is not tenable.<\/p>\n\n<p>Some approaches to solving this include the use of saturating counters. (A\nsaturating counter is a counter that increments up to, but not past, some\nmaximum value.) If we assume that any object with a frequency larger than X\nis an object that we want to cache, we might be able to reduce our counter\nspace. But by how much? 1 byte only gives us 256 values (assuming <code>CHAR_BIT<\/code>\nis 8). What about 2 bytes? If we see 2 billion distinct objects per day, we\nend up using 28GB of memory <em>just to hold counters<\/em> after a week of uptime.\nThis is memory that we cannot use to actually cache objects.<\/p>\n\n<h3 id=\"admission\">Admission<\/h3>\n\n<p>LFU is sometimes implemented as a cache admission policy, not an eviction\npolicy. What this means is that when a cache lookup results in a miss, and\nthe resolution of the miss results in storing a cacheable item, that item\ndoes not become cached until it surpasses some access frequency. For example,\nif every cached item has a access frequency of (say) at least 100 per aging\nperiod, this new item will effectively result in a miss every time <em>until<\/em> its\naccess frequency meets or exceeds this threshold.<\/p>\n\n<p>We typically solve this problem by creating a &ldquo;window&rdquo; cache in front of the\nLFU that caches low frequency items using some simpler approach, like an LRU.\nWhen items from the window cache are evicted, their access frequency during\ntheir time in the windowed cache is compared to their LFU frequency count\nto determine whether or not these items should be inserted into the main\ncache.<\/p>\n\n<h1 id=\"tinylfu\">TinyLFU<\/h1>\n\n<p>These problems are hard to address, but given the promise of LFU, much research\nhas gone into solving them. Enter TinyLFU. TinyLFU provides means for solving\nthe problems of aging and memory overhead. In a <a href=\"http:\/\/arxiv.org\/pdf\/1512.00727.pdf\" title=\"Window TinyLFU paper\">subsequent paper<\/a>, Ben Manes\nintroduces Window-TinyLFU, which solves what it terms the &ldquo;sparse burst&rdquo;\nproblem where repeated access to a new object results in many cache misses.<\/p>\n\n<h2 id=\"dealing-with-aging\">Dealing with Aging<\/h2>\n\n<p>First, we should consider the novel way TinyLFU handles aging. To keep\nfrequencies &ldquo;fresh&rdquo;, a counter is maintained that attempts to target the sample\nsize. The sample size is effectively the period at which we age. This period is\nnot duration based, but is instead measured on incoming requests. The paper\nrefers to this as <em>W<\/em>. (The paper tries very hard not to confuse the reader\nabout the term window, but I was too dense to not be confused for a period of\nmonths.)<\/p>\n\n<p>When this counter reaches the sample size (<em>W<\/em>), both the counter and all\nfrequency sketches are divided by two. The paper goes on to prove that the\nerror bounds resulting from integer divisions are reasonable and maintain a\nlevel of correctness desired from such a structure.<\/p>\n\n<p>This division operation surprisingly simple (at least moreso than sliding\nwindow-based strategies), and usually rather efficient on modern processors.<\/p>\n\n<h2 id=\"solving-memory-overhead\">Solving Memory Overhead<\/h2>\n\n<p>As we explored in the previous section, simply reducing counter size is not\nsufficient to reduce memory overhead to a usable amount. TinyLFU makes a novel\nobservation about cache frequency patterns that not only allows us to\ndrastically reduce counter size to under one byte, but also employs\nprobabilistic algorithms to reduce the total number of counters we must store.<\/p>\n\n<h3 id=\"reducing-the-number-of-counters\">Reducing the Number of Counters<\/h3>\n\n<p>The first novel suggestion made by the paper is to reduce the number of\ncounters by using probabilistic algorithms. This can be done by using an\napproximate count; section 2.2 of the paper details a number of possible\nschemes for this, but in this post, I will focus on discussing what is called\na &ldquo;minimal increment counting Bloom filter&rdquo;.<\/p>\n\n<h4 id=\"minimal-increment-counting-bloom-filters\">Minimal Increment Counting Bloom Filters<\/h4>\n\n<p>Bloom filters are probabilistic data structures that can be used to determine\nwhether some item is a member of some set. Instead of storing the entire set of\nitems, a Bloom filter relies on computing several hashes of the items in a set.\nThe result of this hash is used as an index into a bit set &ndash; if the bit is\nset, the item is probably in the set. If it is not set, the item is definitely\nnot in the set. By increasing the number of distinct hash functions calculated\non the elements, and increasing the total bit space, we can increase our\ncertainty of whether the item is actually a member of the set on a hit.<\/p>\n\n<p>For instance, using N hash functions, this lookup means the item is probably\nin the set:<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/bloomyes.png\" alt=\"Bloom filters - probably in set\" \/><\/p>\n\n<p>This lookup means the item is definitely not in the set:<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/bloomno.png\" alt=\"Bloom filters - definitely not in set\" \/><\/p>\n\n<p>But Bloom filters can say more than just &ldquo;probably&rdquo; and &ldquo;definitely not&rdquo;. A\n&ldquo;counting Bloom filter&rdquo; can keep track of counts (in fact, the standard Bloom\nfilter could be considered a 1-bit counting Bloom filter). In this case, each\nelement in the Bloom filter space represents a (possibly saturating) counter.<\/p>\n\n<p>The problem with this is that multiple items share counters. To keep our\ncounters small, we need a means of making sure counters shared between objects\nwith wildly different access frequencies do not skew each other too much. This\nis where the &ldquo;minimal increment&rdquo; counters come into play. Effectively, this\nstructure only increments the smallest counters by one.<\/p>\n\n<p>Let&rsquo;s visualize this. All counts start off at zero. And let&rsquo;s assume we have\ntwo items for which to track access frequency. At the beginning we have a\nset of distinct, zeroed counters <code>{0, 0, 0, 0, 0, 0}<\/code>:<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/cbf-0.png\" alt=\"min-inc counting bloom filter - initial\" \/><\/p>\n\n<p>After we record a hit on item <code>A<\/code>, we increment its smallest counters by one.\nSince all counters are zero, the counters for item <code>A<\/code> become <code>{1, 1, 1}<\/code>, and\nthe total set becomes <code>{1, 0, 1, 0, 0, 1}<\/code>:<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/cbf-a.png\" alt=\"min-inc counting bloom filter - hit on a\" \/><\/p>\n\n<p>Once we record a hit on item <code>B<\/code>, we increment only its smallest counters &ndash;\nbut note that it shares a counter with item <code>A<\/code>. Therefore, we only increment\nthe two non-shared counters. Item <code>B<\/code>&rsquo;s counters also become <code>{1, 1, 1}<\/code>, and\nthe total set becomes <code>{1, 1, 1, 0, 1, 1}<\/code>:<\/p>\n\n<p><img src=\"https:\/\/9vx.org\/images\/cbf-ab.png\" alt=\"min-inc counting bloom filter - hit on b\" \/><\/p>\n\n<p>If we had initially had three hits on <code>A<\/code>, its counters would be <code>{3, 3, 3}<\/code>; the\ntotal state would be <code>{3, 0, 3, 0, 0, 3}<\/code>. The counters for <code>B<\/code> would be\n<code>{0, 0, 3}<\/code>. Because we only ever increment or use the least of the counters,\nitem <code>B<\/code> still has a hit rate of 0!<\/p>\n\n<hr \/>\n\n<p>Using a probabilistic data structure (like a Bloom filter) for this purpose is\nideal; it has predictable bounds of error, and it significantly reduces the\ntotal number of counters we need. (For more on probabilistic algorithms and\ndata structures, I highly recommend Tyler McMullen&rsquo;s talk,\n<a href=\"https:\/\/www.youtube.com\/watch?v=FSlPU5Nrvds\" title=\"It Probably Works\">It Probably Works<\/a>.)<\/p>\n\n<h3 id=\"reducing-size-of-counters\">Reducing Size of Counters<\/h3>\n\n<p>We already saw how reducing counters to even 1 byte can be cost prohibitive.\nWith our Bloom filter, we significantly reduce the total number of counters we\nneed. But we can still save more. Citing my favorite observation from the paper:<\/p>\n\n<blockquote>\n<p>&hellip;a frequency histogram only needs to know whether a potential cache\nreplacement victim that is already in the cache is more popular than the item\ncurrently being accessed. However, the frequency histogram need not determine\nthe exact ordering between all items in the cache.<\/p>\n<\/blockquote>\n\n<p>Because the aging policy of TinyLFU resets all counters simultaneously (instead\nof using e.g. a sliding window), and because of this observation that we only\ncare about the popularity of an individual item with respect to the popularity\nof all cached items (irrespective of their ordering), we get another nice space\nsavings win.<\/p>\n\n<p>Using this information, the draws two conclusions:<\/p>\n\n<blockquote>\n<p>[Given] a cache of size <em>C<\/em>, all items whose access frequency is above 1\/<em>C<\/em>\nbelong in the cache (under the reasonable assumption that the total number of\nitems being accessed is larger than <em>C<\/em>).<\/p>\n<\/blockquote>\n\n<p>This statement took me months to understand.<\/p>\n\n<p>We have a cache sized to <em>C<\/em>, but this size is (potentially much) smaller than\nthe total set of items <em>S<\/em> that could be accessed through it (as is true for\nmany applications of a cache). So <em>C<\/em> is smaller than <em>S<\/em>.<\/p>\n\n<p>Let&rsquo;s assume that we have <em>N<\/em> requests for <em>M<\/em> distinct objects over some time\nperiod. It doesn&rsquo;t matter what the time period is. Any distinct item <em>I<\/em>\nrequested within our <em>N<\/em> requests, may have been requested more than once.\n<em>D<\/em> is what we&rsquo;ll call the number of times <em>I<\/em> was requested during this sample.\nWhat this statement says is when <em>D\/N<\/em> is larger than <em>1\/C<\/em>, the item <em>I<\/em>\nshould be cached.<\/p>\n\n<p>But it turns out that our aging factor gives us a time period in terms of\nnumber of requests already! We sample our frequency counts based on this number\n<em>W<\/em>. So now we get this really great caching rule:<\/p>\n\n<p>Cache <em>I<\/em> when <em>D\/W<\/em> is larger than <em>1\/C<\/em>.<\/p>\n\n<p>But wait! This means that we don&rsquo;t actually care about D, just the relationship\nbetween the ratios. This means that we can cap our counters at <em>W\/C<\/em>. Since <em>W<\/em>\nis somewhat arbitrarily defined (a larger sample size increases accuracy of the\nfrequency histogram with respect to the frequency of any individual cacheable\nitem), this also means <em>we get to say how much space we want to waste<\/em>. The\npaper elightens us to the space savings:<\/p>\n\n<blockquote>\n<p>To get a feel for counter sizes, when W\/C = 8, the counters require only 3\nbits each, as the sample is 8 times larger than the cache itself. For\ncomparison, if we consider a small 2K items cache with a sample size of 16K\nitems and we do not employ the small counters optimization, then the required\ncounter size is 14 bits.<\/p>\n<\/blockquote>\n\n<hr \/>\n\n<p>This choice also affects the number of counters we need to keep track of. In\nparticular: if you define W as 8C, you <em>only need to keep enough counters to\nrepresent the number of distinct object accesses you have across 8C requests<\/em>.\nIf your cache size is something around 100 million objects, you only need to\ncare about the average uniqueness of 800 million requests. This is usually\nsomething you can measure.<\/p>\n\n<h3 id=\"the-doorkeeper\">The Doorkeeper<\/h3>\n\n<p>Especially in HTTP caches, long-tail objects account for a large portion of\ncacheable content. These are objects that are usually pretty low frequency, but\nlive forever. Furthermore, such items will usually only get a single hit (if\nany) within the sample time. Why should we keep around &ldquo;fat&rdquo; counters for items\nthat rarely see hits?<\/p>\n\n<p>The Doorkeeper solves this problem: it is a single-bit minimum-increment\ncounting Bloom filter. Any incoming requests for items must pass the Doorkeeper\nbefore being promoted to fat counters, effectively allowing us to use fewer\nfat counters than we would otherwise need: a double savings.<\/p>\n\n<hr \/>\n\n<p>Once we have all these bits in place, we can simply couple this algorithm to\nwhichever cache we choose &ndash; be it LRU, ARC, SLRU, 2Q, or whatever else floats\nyour fancy.<\/p>\n\n<h2 id=\"wrapping-up\">Wrapping Up<\/h2>\n\n<p>We&rsquo;ve looked at the components of TinyLFU, and discussed how Window TinyLFU\nsolves a number of issues that have prevented practical use of LFU strategies\nfor large caches.<\/p>\n\n<p>I&rsquo;ll be following this post up in the next couple months with some\nimplementation approaches. I have an interesting (but maybe obvious) idea to do\nwith promoting items from the window cache to the main cache, which I&rsquo;d like to\nexplore. I also have some ideas on how this can be implemented in a lock-free\nfashion, which is critical for caches with highly concurrent workloads. There\nare also a few optimizations that can be made around things like the Doorkeeper.<\/p>\n\n<p>After that, we&rsquo;ll take a look at some experimental results. The paper presents\nsome results, but I&rsquo;d like to take a look at how this works on gigantic data\nsets.<\/p>\n"}]}}