<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
  <title>James on Software</title>
  <subtitle>Thoughts on software, testing...</subtitle>
  <link href="http://jamesgolick.com//" rel="self" />
  <link href="http://jamesgolick.com/" />
  <updated>2014-09-30T12:24:48-04:00</updated>
  <author>
    <name>James Golick</name>
    <email>jamesgolick@gmail.com</email>
  </author>
  <id>http://jamesgolick.com/</id>
  
  <entry>
    <title>Introducing packagecloud.io</title>
    <link href="/2014/4/13/introducing-packagecloud.html" />
    <id>tag:jamesgolick.com,2014-04-13:1397438597</id>
    <updated>2014-04-13T21:23:17-04:00</updated>
    <content type="html">&lt;p&gt;Every operation of any sufficient complexity winds up accumulating a bunch of internal debs, rpms, rubygems, and whatever other package types are relevant to the company. Unfortunately, finding a place to put them is usually a big headache.&lt;/p&gt;

&lt;p&gt;The tooling for managing package repositories is buggy, frustrating, and outdated at best. Even if you do manage to get a repository setup, getting https, gpg signing, backups, and all the other stuff right takes forever, and even then, deployment of new packages is usually some bizarre process involving &lt;tt&gt;scp&lt;/tt&gt; and running a script.&lt;/p&gt;

&lt;p&gt;Having managed this process (poorly) several times ourselves, &lt;a href=&quot;http://twitter.com/joedamato&quot;&gt;Joe&lt;/a&gt; and I set out to try to come up with a better solution. We've been in private beta for a couple of months, and we're finally ready to do a public release.&lt;/p&gt;

&lt;p&gt;If you've ever managed a package repository before, I bet this'll make you as excited as it makes me:&lt;/p&gt;

&lt;div style=&quot;text-align: center&quot;&gt;
  &lt;img src=&quot;/images/packagecloud-multi.png&quot;/&gt;
&lt;/div&gt;




&lt;br /&gt;


&lt;p&gt;Check it out at: &lt;a href=&quot;https://packagecloud.io&quot;&gt;packagecloud.io&lt;/a&gt; and join us in #packagecloud on freenode. We'd love to hear your feedback.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Women in Open Source</title>
    <link href="/2013/10/16/women-in-open-source.html" />
    <id>tag:jamesgolick.com,2013-10-16:1381935656</id>
    <updated>2013-10-16T11:00:56-04:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;tl;dr&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doing open source work is a path to success in the software industry.&lt;/li&gt;
&lt;li&gt;Women are underrepresented in open source, and therefore largely not currently capitalizing on this substantial opportunity.&lt;/li&gt;
&lt;li&gt;The open source world is daunting for everyone, and it's especially bad for women.&lt;/li&gt;
&lt;li&gt;I am offering my time to mentor a woman who is interested in getting involved in open source &amp;mdash; particularly deeply technical and/or low level work. Also, &lt;a href=&quot;#informal&quot;&gt;this&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The last paragraph explains how to get in touch if you're interested.&lt;/li&gt;
&lt;/ul&gt;


&lt;hr/&gt;


&lt;p&gt;The work that I've done in open source has led to nearly every major leap I've made since I started working as a programmer. It's how I got my last job (an executive position), how I get speaking gigs, and how I met nearly every one of my current set of friends and mentors.&lt;/p&gt;

&lt;p&gt;That last point is important: research shows a link between something called &quot;social capital&quot; and career advancement. Your circle of friends &amp;mdash; a representation of your social capital &amp;mdash; acts as a source of information, resources, and credibility &lt;sup&gt;[&lt;a href=&quot;#foot1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt;. In other words, your friends teach you stuff, help you out, and if they're successful and prominent, they make you look good (twitter retweets might make this an especially big deal in our world).&lt;/p&gt;

&lt;p&gt;I've experienced this first hand. In my last job, I was &amp;mdash; quite frankly &amp;mdash; failing miserably until I met a couple of guys who helped me understand how to think about what I was doing in the right way. With their help, I turned things around so well that I get invited to speak all over the world about my successes in that area. I met those guys as a result of open source work. Quite a snowball.&lt;/p&gt;

&lt;p&gt;I have personal examples for days, but thankfully, you don't need to take my word for it. The bibliography for &lt;sup&gt;[&lt;a href=&quot;#foot1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt; is literally a list of research on this topic.&lt;/p&gt;

&lt;hr /&gt;


&lt;p&gt;Women are terribly underrepresented in open source &lt;sup&gt;[&lt;a href=&quot;#foot2&quot;&gt;2&lt;/a&gt;]&lt;/sup&gt;. Since open source is an effective way to accumulate social capital in the software world, womens' underrepresentation is an enormous missed opportunity for career advancement, success, and &amp;mdash; frankly &amp;mdash; making money.&lt;/p&gt;

&lt;p&gt;I get it, though: the open source world is inhospitable to basically everyone, and it's a lot worse for women. Whether we consciously intend it to be or not, open source is a boys club. Although I have observed the men I hang around with be very welcoming and inclusive towards women who are interested in joining our circle, being passively inclusive isn't enough. We need to actively recruit and mentor women until we reach critical mass &lt;sup&gt;[&lt;a href=&quot;#foot3&quot;&gt;3&lt;/a&gt;]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;That's why I've decided to offer my time as a mentor to a woman who is interested in getting involved in open source &amp;mdash; particularly in something deeply technical and/or low level, since that's my area of interest and one where women are particularly poorly represented. Specifically, I'm talking about working on things like databases (distributed or otherwise), memory allocators, virtual machines, operations tooling, etc. The kinds of things I &lt;a href=&quot;/&quot;&gt;write&lt;/a&gt; about on this blog.&lt;/p&gt;

&lt;p&gt;I'm willing to offer half a day a week of my time for whatever I can do to help, whether it's teaching, pair programming, debugging, connecting with smart people I know, or any and every combination of those things and whatever else. I'm new at this, so we'll have to figure it out as we go.&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;informal&quot;&gt;&lt;/a&gt;
If you don't have time for &amp;mdash; or interest in &amp;mdash; open source work, but feel that you might benefit from more informal regular chats (IMs, IRC, whatever) about whatever it is that you're working on, I'd love to help in any way I can, so definitely still get in touch. This is the type of relationship that I have with many of my mentors and it's tremendously useful.&lt;/p&gt;

&lt;p&gt;Drop me an &lt;a href=&quot;mailto:jamesgolick@gmail.com&quot;&gt;email&lt;/a&gt; with a bit of background about who you are and the type of thing you might be interested in working on. &lt;strong&gt;You don't already have to be working on low level or deeply technical stuff.&lt;/strong&gt; A few years ago, I wasn't either. All you need to be successful is interest and motivation.&lt;/p&gt;

&lt;ol style=&quot;font-size:12px&quot;&gt;
  &lt;li&gt;&lt;a name=&quot;foot1&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://www.aom.pace.edu/amj/April2001/seibert.pdf&quot;&gt;A Social Capital Theory of Career Success, Scott E. Seibert, Maria L. Kraimer and Robert C. Liden&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a name=&quot;foot2&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://www.flosspols.org/deliverables/FLOSSPOLS-D16-Gender_Integrated_Report_of_Findings.pdf&quot;&gt;Gender: Integrated Report of Findings, Dawn Nafus, James Leach, Bernhard Krieger&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a name=&quot;foot3&quot;&gt;&lt;/a&gt;Incidentally, the research shows that mentoring is a key contributor to career advancement, higher salaries, and more job satisfaction. It also shows that women typically have less access to mentoring, arguably a key facet of the glass ceiling.
    &lt;ol&gt;
      &lt;li&gt;&lt;a href=&quot;http://onlinelibrary.wiley.com/doi/10.1111/j.1468-0432.2010.00521.x/pdf&quot;&gt;Impressing for Success: A Gendered Analysis of a Key Social Capital Accumulation Strategy, Savita Kumra, Susan Vinnicombe&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

</content>
  </entry>
  
  <entry>
    <title>How tcmalloc Works</title>
    <link href="/2013/5/19/how-tcmalloc-works.html" />
    <id>tag:jamesgolick.com,2013-05-19:1368979797</id>
    <updated>2013-05-19T12:09:57-04:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;tl;dr&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a long blog post that goes in to a bunch of detail about one of the highest performance memory allocators around.&lt;/li&gt;
&lt;li&gt;You should probably read &lt;a href=&quot;http://gperftools.googlecode.com/svn/trunk/&quot;&gt;the code&lt;/a&gt; instead of reading this article.&lt;/li&gt;
&lt;li&gt;If you aren't familiar with the topic of memory allocation, you should read my last blog post &lt;a href=&quot;/2013/5/15/memory-allocators-101.html&quot;&gt;Memory Allocators 101&lt;/a&gt; first.&lt;/li&gt;
&lt;/ul&gt;


&lt;hr /&gt;


&lt;p&gt;&lt;code&gt;tcmalloc&lt;/code&gt; is a memory allocator that's optimized for high concurrency situations. The &lt;code&gt;tc&lt;/code&gt; in &lt;code&gt;tcmalloc&lt;/code&gt; stands for &lt;code&gt;thread cache&lt;/code&gt; &amp;mdash; the mechanism through which this particular allocator is able to satisfy certain (often most) allocations locklessly. It's probably the most well-conceived piece of software I've ever had the pleasure of reading, and although I can't realistically cover every detail, I'll do my best to go over the important points.&lt;/p&gt;

&lt;p&gt;Like most modern allocators, &lt;code&gt;tcmalloc&lt;/code&gt; is page-oriented, meaning that the internal unit of measure is usually pages rather than bytes. This has the effect of making it easier to reduce fragmentation, and increase locality in various ways. It also makes keeping track of metadata far simpler. &lt;code&gt;tcmalloc&lt;/code&gt; defines a page as &lt;code&gt;8192&lt;/code&gt; bytes&lt;sup&gt;&lt;a href=&quot;#footnote1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, which is actually 2 pages on most linux systems.&lt;/p&gt;

&lt;p&gt;Chunks can be thought of as divided in to two top-level categories. &quot;Small&quot; chunks are smaller than &lt;code&gt;kMaxPages&lt;/code&gt; (defaults to 128) and are further divided in to size classes and satisfied by the thread caches or the central per-size class caches. &quot;Large&quot; chunks are &lt;code&gt;&amp;gt;= kMaxPages&lt;/code&gt; and are always satisfied by the central &lt;code&gt;PageHeap&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Size Classes&lt;/h3&gt;

&lt;p&gt;By default, &lt;code&gt;tcmalloc&lt;/code&gt; creates &lt;code&gt;86&lt;/code&gt; size classes for &quot;small&quot; chunks, each of which have several important properties which define thread cache behaviour as well as fragmentation and waste characteristics.&lt;/p&gt;

&lt;p&gt;The number of pages allocated at once for a particular size class is one such property. It is carefully defined such that transfers between the central and thread caches are within a range that strikes a balance between wasting chunks sitting around unused in thread caches, and having to go to the central cache too often, causing contention for its lock. The code which determines this number also guarantees that the amount of waste per size class is at most &lt;code&gt;12.5%&lt;/code&gt;, and that the alignment guarantees of the &lt;code&gt;malloc&lt;/code&gt; API are respected.&lt;/p&gt;

&lt;p&gt;Size class data is stored in &lt;code&gt;SizeMap&lt;/code&gt; and the first thing to be initialized on startup.&lt;/p&gt;

&lt;h3&gt;Thread Caches&lt;/h3&gt;

&lt;p&gt;Thread caches are a lazily initialized &lt;a href=&quot;http://en.wikipedia.org/wiki/Thread-local_storage&quot;&gt;thread-local&lt;/a&gt; data structure which contains one free list (singly-linked) per size class. They also contain metadata regarding the current total size of their contents.&lt;/p&gt;

&lt;p&gt;Allocations and deallocations from thread caches are lockless and &lt;a href=&quot;http://en.wikipedia.org/wiki/Time_complexity&quot;&gt;constant-time&lt;/a&gt; in the best case. If the thread cache doesn't already contain a chunk for the size class that is being allocated, it has to fetch some chunks for that class from the central cache, of which there is one per size class. If the thread cache becomes too full (more on what that means in a second) on deallocation, chunks are migrated back to the central cache. Each central cache has its own lock to reduce contention during such migrations.&lt;/p&gt;

&lt;p&gt;As chunks are migrated in and out of a thread cache, it bounds its own size in two interesting ways.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, there is an overall total size of all the combined thread caches. Each cache keeps track of its total contents as chunks are migrated to and from the central caches, as well as allocated or deallocated. Initially, each cache is assigned an equal amount of space from the overall total. However, as some caches inevitably need more or less space, there is a clever algorithm whereby one cache can &quot;steal&quot; unused space from one of its neighbours.&lt;/li&gt;
&lt;li&gt;Second, each free list has a maximum size, which gets increased in an interesting way as objects are migrated in to it from the central cache. If the list exceeds its maximum size, chunks are released to the central cache.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;If a thread cache has exceeeded its maximum size after a migration from the central cache or on deallocation, it first attempts to find some extra headroom in its own free-lists by checking to see if they have any excess that can be released to the central caches. Chunks are considered excess if they have been added to a free list since the last allocation that the list satisfied&lt;sup&gt;&lt;a href=&quot;#footnote3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;. If it can't free up any space that way, it will attempt to &quot;steal&quot; space from one of its neighbouring thread caches, which requires holding the &lt;code&gt;pageheap_lock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Central caches have their own system for managing space across all the caches in the system. Each is capped at either &lt;code&gt;1MB&lt;/code&gt; of chunks or 1 entry, whichever is greater. As central caches need more space, they can &quot;steal&quot; it from their neighbours, using a similar mechanism to the one employed by thread caches. If a thread cache attempts to migrate objects back to a central cache that is full and unable to acquire more space, the central cache will release those objects to the &lt;code&gt;PageHeap&lt;/code&gt;, which is where it got them in the first place.&lt;/p&gt;

&lt;h3&gt;Page Heap&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;PageHeap&lt;/code&gt; can be thought of as the root of the whole system. When chunks aren't floating around the caches or allocated in the running application, they're living in one of the &lt;code&gt;PageHeap&lt;/code&gt;'s free lists. This is where chunks are allocated in the first place, using &lt;code&gt;TCMalloc_SystemAlloc&lt;/code&gt; and ultimately released back to the operating system, using &lt;code&gt;TCMalloc_SystemRelease&lt;/code&gt;. It's also where &quot;large&quot; allocations are satisfied and provides the interface for tracking heap metadata.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;PageHeap&lt;/code&gt; manages &lt;code&gt;Span&lt;/code&gt; objects, which represent a contiguous run of pages. Each &lt;code&gt;Span&lt;/code&gt; has several important properties.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PageID start&lt;/code&gt; is the start address of the memory the &lt;code&gt;Span&lt;/code&gt; describes. &lt;code&gt;PageID&lt;/code&gt; is &lt;code&gt;typedef&lt;/code&gt;'d to &lt;code&gt;uintptr_t&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Length length&lt;/code&gt; is the number of pages in the &lt;code&gt;Span&lt;/code&gt;. &lt;code&gt;Length&lt;/code&gt; is also &lt;code&gt;typedef&lt;/code&gt;'d to &lt;code&gt;uintptr_t&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Span *next&lt;/code&gt; and &lt;code&gt;Span *prev&lt;/code&gt; are pointers for when the &lt;code&gt;Span&lt;/code&gt; is in one of the doubly linked free-listsb in the &lt;code&gt;PageHeap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A bunch more stuff, but this post is getting really long.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The &lt;code&gt;PageHeap&lt;/code&gt; has &lt;code&gt;kMaxPages + 1&lt;/code&gt; free lists &amp;mdash; one for each span length from &lt;code&gt;0...kMaxPages&lt;/code&gt; and one for lengths greater than that. The lists are doubly linked and split in to &lt;code&gt;normal&lt;/code&gt; and &lt;code&gt;returned&lt;/code&gt; sections.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;normal&lt;/code&gt; section contains &lt;code&gt;Span&lt;/code&gt;s whose pages are definitely mapped in to the process's address space.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;returned&lt;/code&gt; section contains &lt;code&gt;Span&lt;/code&gt;s whose pages have been returned to the operating system using &lt;code&gt;madvise&lt;/code&gt; with &lt;code&gt;MADV_FREE&lt;/code&gt;. The OS is free to reclaim those pages as necessary. However, if the application uses that memory before it has been reclaimed, the call to &lt;code&gt;madvise&lt;/code&gt; is effectively negated. Even in the case that the memory &lt;em&gt;has&lt;/em&gt; been reclaimed, the kernel will remap those addresses to a freshly zero'd region of memory. So, not only is it safe to reuse pages that have been returned, it's an important strategy for reducing heap fragmentation.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The &lt;code&gt;PageHeap&lt;/code&gt; also contains the &lt;code&gt;PageMap&lt;/code&gt;, which is a &lt;a href=&quot;http://en.wikipedia.org/wiki/Radix_tree&quot;&gt;radix tree&lt;/a&gt; that maps addresses to their respective &lt;code&gt;Span&lt;/code&gt; objects, and the &lt;code&gt;PageMapCache&lt;/code&gt;, which  maps a chunk's &lt;code&gt;PageID&lt;/code&gt; to its size class for chunks that are in the cache system. This is the mechanism through which &lt;code&gt;tcmalloc&lt;/code&gt; stores its metadata, rather than using headers and footers to the actual pointers. Although it is somewhat less space efficient, it is substantially more cache efficient since all of the involved data structures are slab allocated.&lt;/p&gt;

&lt;p&gt;Allocations from the &lt;code&gt;PageHeap&lt;/code&gt; are performed via &lt;code&gt;PageHeap::New(Length n)&lt;/code&gt;, where &lt;code&gt;n&lt;/code&gt; is the number of pages being requested.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, the free lists &lt;code&gt;&amp;gt;=&lt;/code&gt; to &lt;code&gt;n&lt;/code&gt; (unless &lt;code&gt;n&lt;/code&gt; is &lt;code&gt;&amp;gt;= kMaxPages&lt;/code&gt;) are traversed looking for a &lt;code&gt;Span&lt;/code&gt; big enough to satisfy &lt;code&gt;n&lt;/code&gt;. If one is found, it is removed from the list and returned. This type of allocation is best-fit, but because it's not address ordered, it is suboptimal as far as fragmentation is concerned &amp;mdash; presumably a performance tradeoff. The &lt;code&gt;normal&lt;/code&gt; lists are all checked before moving on to checking the &lt;code&gt;returned&lt;/code&gt; lists. I'm not sure exactly why.&lt;/li&gt;
&lt;li&gt;If none of those lists have a fitting &lt;code&gt;Span&lt;/code&gt;, the large lists are traversed, looking for an address-ordered best fit. This algorithm is &lt;code&gt;O(n)&lt;/code&gt; accross all the &lt;code&gt;Span&lt;/code&gt;s in both large lists, which can get very expensive in situations where concurrency is fluctuating dramatically and the heap has become fragmented. I have written &lt;a href=&quot;https://code.google.com/p/gperftools/issues/detail?id=532&amp;amp;thanks=532&amp;amp;ts=1369179481&quot;&gt;a patch&lt;/a&gt; which reorganizes the large lists in to a &lt;a href=&quot;http://en.wikipedia.org/wiki/Skip_list&quot;&gt;skip list&lt;/a&gt; if they exceed a configurable total size to improve large allocation performance for applications which encounter this circumstance.&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;Span&lt;/code&gt; that has been found is at least one page bigger than the requested allocation, it is split in to a chunk sufficient to satisfy the allocation, and whatever is leftover is re-added to the appropriate free list before returning the newly allocated chunk.&lt;/li&gt;
&lt;li&gt;If no suitable &lt;code&gt;Span&lt;/code&gt; is found, the &lt;code&gt;PageHeap&lt;/code&gt; attempts to grow itself by at least &lt;code&gt;n&lt;/code&gt; pages before starting the process again from the beginning. If it is unsuccessful at finding a suitable chunk the second time around, it returns &lt;code&gt;NULL&lt;/code&gt;, which ultimately results in &lt;code&gt;ENOMEM&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Deallocations to the &lt;code&gt;PageHeap&lt;/code&gt; are performed via &lt;code&gt;PageHeap::Delete(Span* span)&lt;/code&gt;. Their effect is that the &lt;code&gt;Span&lt;/code&gt; is merged in to the appropriate free-list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, the adjacent &lt;code&gt;Span&lt;/code&gt; objects (both left and right) are acquired from the &lt;code&gt;PageMap&lt;/code&gt;. If either or both of them are free, they are removed from whatever free-list they happen to be on and coalesced together with &lt;code&gt;span&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Then, &lt;code&gt;span&lt;/code&gt; is prepended to whichever free list it now belongs on.&lt;/li&gt;
&lt;li&gt;Finally, the &lt;code&gt;PageHeap&lt;/code&gt; checks to see whether it's time to release memory to the operating system, and releases some if it is.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Each time a &lt;code&gt;Span&lt;/code&gt; is returned to &lt;code&gt;PageHeap&lt;/code&gt;, its member &lt;code&gt;scavenge_counter_&lt;/code&gt; is decremented by the &lt;code&gt;length&lt;/code&gt; of that &lt;code&gt;Span&lt;/code&gt;. If &lt;code&gt;scavenge_counter_&lt;/code&gt; drops below &lt;code&gt;0&lt;/code&gt;, the last &lt;code&gt;Span&lt;/code&gt; is released from one of the free lists or the &lt;code&gt;large&lt;/code&gt; list, removed from the &lt;code&gt;normal&lt;/code&gt; list, and then added to the appropriate &lt;code&gt;returned&lt;/code&gt; list for possible reuse later. &lt;code&gt;scavenge_counter_&lt;/code&gt; is then reset to &lt;code&gt;min(kMaxReleaseDelay, (1000.0 / FLAGS_tcmalloc_release_rate) * number_of_pages_released)&lt;/code&gt;. So, tuning &lt;code&gt;FLAGS_tcmalloc_release_rate&lt;/code&gt; has a substantial effect on when memory gets released.&lt;/p&gt;

&lt;h3&gt;Conclusions&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This blog post is incredibly long. Congratulations for getting here. And yet I barely feel like I've covered anything.&lt;/li&gt;
&lt;li&gt;If this kind of problem is interesting to you, I &lt;em&gt;highly&lt;/em&gt; recommend reading the &lt;a href=&quot;http://gperftools.googlecode.com/svn/trunk/&quot;&gt;source code&lt;/a&gt;. Although &lt;code&gt;tcmalloc&lt;/code&gt; is very complex, the code is extremely approachable and well commented. I barely know &lt;code&gt;C++&lt;/code&gt; and was still able to write a substantial patch. Particularly with this blog post as a guide, there's not much to be afraid of.&lt;/li&gt;
&lt;li&gt;I'll cover &lt;code&gt;jemalloc&lt;/code&gt; in a future episode.&lt;/li&gt;
&lt;li&gt;Listen to my (and &lt;a href=&quot;http://timetobleed.com&quot;&gt;Joe Damato&lt;/a&gt;'s) &lt;a href=&quot;http://realtalk.io&quot;&gt;podcast&lt;/a&gt; &amp;mdash; it's about this kind of stuff.&lt;/li&gt;
&lt;/ul&gt;


&lt;div style=&quot;font-size: 12px;&quot;&gt;
  &lt;li&gt;&lt;a name=&quot;footnote1&quot;&gt;&lt;/a&gt;[1] Unless the experimental feature `TCMALLOC_LARGE_PAGES` is enabled.&lt;/li&gt;
  &lt;li&gt;&lt;a name=&quot;footnote2&quot;&gt;&lt;/a&gt;[2] This is sort of a simplification of a more complicated system, but should be good enough for this purpose.&lt;/li&gt;
&lt;/div&gt;




&lt;br/&gt;

</content>
  </entry>
  
  <entry>
    <title>Memory Allocators 101</title>
    <link href="/2013/5/15/memory-allocators-101.html" />
    <id>tag:jamesgolick.com,2013-05-15:1368632222</id>
    <updated>2013-05-15T11:37:02-04:00</updated>
    <content type="html">&lt;p&gt;For the last few weeks, I've been working on a couple of patches to tcmalloc, Google's super high performance memory allocator. I'm going to post about them soon, but first I thought it would be cool to give some background about what a memory allocator actually does. So, if you've ever wondered what happens when you call &lt;code&gt;malloc&lt;/code&gt; or &lt;code&gt;free&lt;/code&gt;, read on.&lt;/p&gt;

&lt;hr /&gt;


&lt;p&gt;A memory allocator's responsibility is to manage free blocks of memory. If you've never read a &lt;code&gt;malloc&lt;/code&gt; implementation, you may have assumed that calling &lt;code&gt;free&lt;/code&gt; simply causes memory to be released to the operating system. But acquiring memory from the OS has a cost, so allocators tend to keep free chunks around for a while for possible re-use before deciding to release them.&lt;/p&gt;

&lt;p&gt;Managing &lt;code&gt;free&lt;/code&gt;d memory is an incredibly interesting and hard problem with two main concerns: performance and reducing heap fragmentation / waste:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do we organize free blocks of memory such that we can quickly locate a sufficiently large block (or determine that we lack one) when someone calls &lt;code&gt;malloc&lt;/code&gt; without making calls to &lt;code&gt;free&lt;/code&gt; prohibitively expensive?&lt;/li&gt;
&lt;li&gt;What can we do to reduce fragmentation and waste in the face of sometimes drastically changing allocation patterns over the lifetime of a (potentially long-running) program? It's worth noting that heap fragmentation can have a substantial impact on CPU cache efficiency.&lt;/li&gt;
&lt;li&gt;As a bonus, there's also the matter of concurrency, but that's probably beyond the scope of this post.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The most fun part of this problem is that our two primary objectives are often in direct opposition. For example, keeping one linked list of free blocks per allocation size (say, rounded up to number of &lt;a href=&quot;http://en.wikipedia.org/wiki/Page_(computer_memory)&quot;&gt;pages&lt;/a&gt;) can make calls to &lt;code&gt;malloc&lt;/code&gt; best case constant time, but unless some waste is accepted and chunks are kept around long enough to be reused, the worst case path will be taken more often than not.&lt;/p&gt;

&lt;p&gt;Of course, there also are a multitude of other issues to consider, such as how to decide to release memory to the operating system, and how to avoid becoming the bottleneck in a concurrent program (I'm looking at you, glibc). And the implementation details are interesting, too.&lt;/p&gt;

&lt;h3&gt;Implementation&lt;/h3&gt;

&lt;p&gt;A very basic &lt;code&gt;malloc&lt;/code&gt; implementation might use the linux system call &lt;a href=&quot;http://linux.die.net/man/2/sbrk&quot;&gt;&lt;code&gt;sbrk(2)&lt;/code&gt;&lt;/a&gt; to acquire memory from the operating system and a linked list to store free chunks. That would make calls to &lt;code&gt;free&lt;/code&gt; constant time, but &lt;code&gt;malloc&lt;/code&gt; would be &lt;code&gt;O(n)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Of course, the allocator needs to store metadata about each chunk it manages, such as its size, free/in-use status, free-list pointer(s), etc. But since you can't exactly call &lt;code&gt;malloc&lt;/code&gt; in an allocator, it's common to store metadata in a &quot;header&quot; that just precedes the address that is handed to the application. So, if the header is 16 bytes in size, then the header would start at &lt;code&gt;ptr - 16&lt;/code&gt;. Pointer arithmetic galore.&lt;/p&gt;

&lt;div style=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;/images/malloc_header.png&quot; /&gt;&lt;/div&gt;


&lt;br/&gt;




&lt;script src=&quot;https://gist.github.com/jamesgolick/5593158.js?file=header.c&quot;&gt;&lt;/script&gt;


&lt;p&gt;In order to reduce fragmentation and promote memory reuse, it's common for &lt;code&gt;malloc&lt;/code&gt; implementations to attempt to coalesce free blocks of memory with adjacent ones, if they happen to also be free. If metadata is being stored in a header, then it's easy to determine the size and status of the pointer to the right.&lt;/p&gt;

&lt;div style=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;/images/malloc_right.png&quot; /&gt;&lt;/div&gt;


&lt;br/&gt;




&lt;script src=&quot;https://gist.github.com/jamesgolick/5593158.js?file=right.c&quot;&gt;&lt;/script&gt;


&lt;p&gt;But the header doesn't provide any way of determining the size of the chunk to the left, so coalescing &lt;code&gt;malloc&lt;/code&gt;s frequently put the size of each block in a footer, which is typically sized to fit a &lt;code&gt;size_t&lt;/code&gt;. Then finding the chunk to the left would look something like this:&lt;/p&gt;

&lt;div style=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;/images/malloc_left.png&quot; /&gt;&lt;/div&gt;


&lt;br/&gt;




&lt;script src=&quot;https://gist.github.com/jamesgolick/5593158.js?file=left.c&quot;&gt;&lt;/script&gt;


&lt;p&gt;Things get even more complicated because subsequent invocations of system calls like &lt;code&gt;sbrk&lt;/code&gt; or &lt;code&gt;mmap&lt;/code&gt; aren't guaranteed to return contiguous virtual addresses. So, when looking for chunks to coalesce, care has to be taken to make sure that invalid pointers aren't dereferenced by adding to a pointer that's on the edge of what's being managed.&lt;/p&gt;

&lt;p&gt;Typically this means creating and maintaining a separate data structure with which to keep track of the regions of virtual address space that the allocator is managing. Some allocators, such as &lt;code&gt;tcmalloc&lt;/code&gt;, simply store their metadata in that data structure rather than in headers and footers, which avoids a lot of error-prone pointer arithmetic.&lt;/p&gt;

&lt;hr /&gt;


&lt;p&gt;I could probably continue writing about this forever, but this seems like a good place to stop for now. If your interest is piqued and you'd like to learn more about memory allocators, I highly recommend diving in to writing your own &lt;code&gt;malloc&lt;/code&gt; implementation. It's a challenging project, but it's fun and it'll give you a lot of insight in to an important part of how your computer works.&lt;/p&gt;

&lt;p&gt;Soon I'll follow up on this post with one about the allocator work I've been doing lately. Also, if you're interested in this kind of stuff, check out my new &lt;a href=&quot;http://realtalk.io&quot;&gt;podcast&lt;/a&gt; where &lt;a href=&quot;http://timetobleed.com&quot;&gt;Joe Damato&lt;/a&gt; and I talk about systems programming. We'll definitely be covering allocators in the next few weeks sometime.&lt;/p&gt;

&lt;p&gt;Some allocator resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://man7.org/linux/man-pages/man3/malloc.3.html&quot;&gt;malloc(3) man page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cs.cmu.edu/afs/cs/academic/class/15213-f10/www/lectures/17-allocation-basic.pdf&quot;&gt;http://www.cs.cmu.edu/afs/cs/academic/class/15213-f10/www/lectures/17-allocation-basic.pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://goog-perftools.sourceforge.net/doc/tcmalloc.html&quot;&gt;tcmalloc docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jeremie-koenig/glibc/tree/master-beware-rebase/malloc&quot;&gt;glibc malloc source&lt;/a&gt; &amp;mdash; this is some random github repo, so I have no idea how much its been fucked with&lt;/li&gt;
&lt;/ul&gt;

</content>
  </entry>
  
  <entry>
    <title>Introducing The Real Talk Podcast</title>
    <link href="/2013/4/28/introducing-the-real-talk-podcast.html" />
    <id>tag:jamesgolick.com,2013-04-29:1367209602</id>
    <updated>2013-04-29T00:26:42-04:00</updated>
    <content type="html">&lt;p&gt;[Joe Damato] and I have released the inaugural episode of our new, highly technical podcast realtalk.io.&lt;/p&gt;

&lt;p&gt;We will be doing frequent technical deep dives and releasing our conversations raw and unedited with all errors, omissions, awkward pauses, and curse words intact.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href=&quot;http://realtalk.io&quot;&gt;website&lt;/a&gt;, &lt;a href=&quot;tune%20in&quot;&gt;soundcloud&lt;/a&gt;, and &lt;a href=&quot;http://feeds.feedburner.com/realtalkio&quot;&gt;subscribe&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>MRI's Method Caches</title>
    <link href="/2013/4/14/mris-method-caches.html" />
    <id>tag:jamesgolick.com,2013-04-14:1365955274</id>
    <updated>2013-04-14T12:01:14-04:00</updated>
    <content type="html">&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Method resolution is expensive, so method caches are crucial to invocation performance.&lt;/li&gt;
&lt;li&gt;Your Ruby code probably calls methods kind of often, so invocation performance matters.&lt;/li&gt;
&lt;li&gt;MRI's method cache invalidation strategy is quite naive, leading to very low hit rates in most Ruby code.&lt;/li&gt;
&lt;li&gt;I wrote &lt;a href=&quot;https://github.com/jamesgolick/ruby/tree/jamesgolick&quot;&gt;some patches&lt;/a&gt; that substantially improve the situation.&lt;/li&gt;
&lt;li&gt;This blog post is surprisingly uninflammatory.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;The Long Version&lt;/h3&gt;

&lt;p&gt;One of MRI's big performance problems is that method cache expiry is global. That is, any time you make a change to any class anywhere, the entire VM's method caches get busted at the same time. This is why you'll frequently hear people saying that &quot;calling &lt;code&gt;Object#extend&lt;/code&gt; is bad&quot;.&lt;/p&gt;

&lt;p&gt;Actually, it's not just &lt;code&gt;Object#extend&lt;/code&gt;. &lt;a href=&quot;https://twitter.com/charliesome&quot;&gt;Charlie Somerville&lt;/a&gt; put together what I believe to be an &lt;a href=&quot;http://charlie.bz/blog/things-that-clear-rubys-method-cache&quot;&gt;exhaustive list&lt;/a&gt; of things that clear MRI's method caches. Method cache busting is so pervasive that it's almost impossible to avoid using somebody's code that does it. Disaster.&lt;/p&gt;

&lt;p&gt;Let's back up for a second, though. What is a method cache and why are they important?&lt;/p&gt;

&lt;h3&gt;Method Cache Basics&lt;/h3&gt;

&lt;p&gt;Take the following class hierarchy:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/jamesgolick/5347185.js&quot;&gt;&lt;/script&gt;


&lt;p&gt;Internally, MRI stores methods in a hash table on the &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/include/ruby/ruby.h#L630&quot;&gt;&lt;code&gt;RClass struct&lt;/code&gt;&lt;/a&gt;. When you call an inherited method on a descendent, MRI has to walk up the class hierarchy to find it, checking the method table at each step to see if there's anything there to call.&lt;/p&gt;

&lt;p&gt;So, in our above example, if we wanted to call &lt;code&gt;hello&lt;/code&gt; on an instance of &lt;code&gt;E&lt;/code&gt;, MRI would have to execute method lookups on &lt;code&gt;E&lt;/code&gt;, &lt;code&gt;D&lt;/code&gt;, &lt;code&gt;C&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, only to finally find the method in &lt;code&gt;A&lt;/code&gt;'s method table.&lt;/p&gt;

&lt;p&gt;It turns out that method resolution is actually quite expensive, which is why method caches exist. Rather than resolving a method each time we want to call it, we cache a reference to the method somewhere we can get to it cheaply, substantially reducing the cost of subsequent invocations.&lt;/p&gt;

&lt;p&gt;But Ruby is dynamic, so those caches can't necessarily live forever. If we call &lt;code&gt;String#gsub&lt;/code&gt;, for example, and then &lt;code&gt;undef&lt;/code&gt; it without expiring the method cache, it'll still be reachable. Cache invalidation is hard, as we know, so MRI takes a somewhat brute force approach.&lt;/p&gt;

&lt;h3&gt;How MRI's Method Caches Work&lt;/h3&gt;

&lt;p&gt;Currently, MRI has two types of method caches. Ruby code is compiled down to MRI's &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/insns.def&quot;&gt;instructions&lt;/a&gt;. Each instruction has some data associated with it. The &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/insns.def#L998&quot;&gt;send&lt;/a&gt; instruction has an &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/vm_core.h#L128&quot;&gt;&lt;code&gt;iseq_inline_cache_entry&lt;/code&gt;&lt;/a&gt;, which acts as an inline method cache.&lt;/p&gt;

&lt;p&gt;You can read the logic &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/vm_insnhelper.c#L1369&quot;&gt;here&lt;/a&gt;. Basically, it works like this: look at the inline cache. &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/vm_insnhelper.c#L1374-L1375&quot;&gt;If it's valid&lt;/a&gt;, &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/vm_insnhelper.c#L1376&quot;&gt;use it&lt;/a&gt;. If not, &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/vm_insnhelper.c#L1379-1382&quot;&gt;go actually look up the method and cache it&lt;/a&gt;. Pretty much exactly what you'd expect.&lt;/p&gt;

&lt;p&gt;In the case of an inline instruction cache miss, there's actually a &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/vm_method.c#L25&quot;&gt;secondary, global method cache&lt;/a&gt;. Oddly, though, the global method cache is limited to &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/vm_method.c#L8&quot;&gt;2048&lt;/a&gt; entries, and its semantics for deciding what to keep and what to dump are &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/vm_method.c#L10&quot;&gt;essentially random&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's not as unlikely as you might hope for two methods in a tight loop to be clobbering each others' entries in the global method cache table.&lt;/p&gt;

&lt;p&gt;Both caches' entries have a field that stores the &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/094f2f438ae79f0b9afe9b4f5966c5bf1a6a3d9c/vm_insnhelper.h#L224&quot;&gt;&lt;code&gt;ruby_vm_global_state_version&lt;/code&gt;&lt;/a&gt; from when they were filled. A cache entry is considered valid if it matches the &lt;code&gt;klass&lt;/code&gt; pointer and the current &lt;code&gt;ruby_vm_global_state_version&lt;/code&gt;. So, incrementing the global state version by &lt;code&gt;1&lt;/code&gt; invalidates all of the inline instruction caches as well as the global method cache.&lt;/p&gt;

&lt;p&gt;This has the effect of making invalidation very cheap, but far reaching. Whenever you make a change to any class, call extend, or do any of the other things detailed in &lt;a href=&quot;http://charlie.bz/blog/things-that-clear-rubys-method-cache&quot;&gt;Charlie's article&lt;/a&gt;, all of the method caches that have built up since your program started become invalid and you have to repay the cost of method resolution all over again.&lt;/p&gt;

&lt;h3&gt;The Numbers&lt;/h3&gt;

&lt;p&gt;After years of complaining about Ruby's method caching behaviour, I finally decided to instrument it a couple of weeks ago. I found that for our application, the method cache was being invalidated at least 20 times per request, and that around 10% of our request profile was spent performing method resolution. For our application, the cost of Ruby's global method cache invalidation was extremely high.&lt;/p&gt;

&lt;p&gt;The average cost of each method resolution for our production application is around one microsecond, which doesn't sound like a lot but it adds up. We were seeing at least 8000 cache misses per request, totalling 8ms or more.&lt;/p&gt;

&lt;p&gt;As part of my instrumentation patchset, I also created a mechanism that logs a stacktrace each time the method cache is invalidated. I found that the majority of invalidations in our app were from inside of ActiveRecord - &lt;a href=&quot;https://github.com/rails/rails/pull/10058&quot;&gt;some&lt;/a&gt; easier to fix than others. Many were also caused by random gems doing things like instantiating &lt;code&gt;OpenStruct&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;At this point, it started seeming somewhat impractical to go and patch rails and all these other gems that I use, so I decided to investigate the amount of effort that would be required to actually solve the problem in MRI.&lt;/p&gt;

&lt;h3&gt;Hierarchical Method Cache Invalidation&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;I'll be using &lt;code&gt;class&lt;/code&gt; to mean &lt;code&gt;class&lt;/code&gt; or &lt;code&gt;module&lt;/code&gt; here, since they have the same backing structure in the VM.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ruby's inheritance tree is a &lt;a href=&quot;http://en.wikipedia.org/wiki/Directed_acyclic_graph&quot;&gt;directed acyclic graph&lt;/a&gt;, and the semantics of method resolution mean that a change to a given class only affects it and its descendents. So, in an ideal scenario, we would only need to invalidate the method caches for those branches of the inheritance tree.&lt;/p&gt;

&lt;p&gt;I've written a patch for MRI that implements such an algorithm, and it's currently serving 100% of our production traffic. We've seen around a 9% reduction in average latency with this patch. Others who've tried it haven't seen such big jumps. Your mileage may vary.&lt;/p&gt;

&lt;p&gt;The algorithm is actually quite simple (credit to Charlie Nutter / JRuby for the idea). We have a 64 bit global (one per VM instance) sequence that is monotonically increasing. Every time we alloc a new &lt;code&gt;RClass&lt;/code&gt;, we increment the sequence, and assign the class a unique value.&lt;/p&gt;

&lt;p&gt;Method and inline cache entries are tagged with the class's sequence value when they're filled. When a class is modified, we traverse the class hierarchy downwards from the modification point, assigning each class a new sequence number.&lt;/p&gt;

&lt;p&gt;A method cache entry is considered valid if its sequence number matches the current sequence of the class. So, if the class or one of its parents has been modified since the cache entry was created, its class will have a new sequence number, and it will have therefore been invalidated.&lt;/p&gt;

&lt;h3&gt;Performance&lt;/h3&gt;

&lt;p&gt;For our application, this cache invalidation strategy substantially reduces the number of method cache misses we see in production and has reduced request latency by ~8-9%, but there &lt;em&gt;are&lt;/em&gt; tradeoffs involved. Since invalidation requires a graph traversal, it's a lot more expensive than the current strategy of merely incrementing an integer.&lt;/p&gt;

&lt;p&gt;If your application makes frequent modifications to classes and modules which have a large number of descendents, the cost of invalidation may outweigh the increase in method cache hit rate. That said, I would imagine that such modifications are relatively uncommon and should be considered a bad practice either way.&lt;/p&gt;

&lt;p&gt;It's also worth noting here that while this patch will likely improve the performance of apps that employ the strategy of extending arbitrary objects to implement &lt;a href=&quot;http://en.wikipedia.org/wiki/Data,_context_and_interaction&quot;&gt;DCI&lt;/a&gt;, that pattern is still a performance problem, because it creates tons of one-off metaclasses whose methods wind up being mostly uncacheable.&lt;/p&gt;

&lt;h3&gt;The Code&lt;/h3&gt;

&lt;p&gt;My patchset includes several things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subclass tracking: &lt;code&gt;Class#subclasses&lt;/code&gt;, and &lt;code&gt;Module#included_in&lt;/code&gt;. Rails implements this with an O(n) traversal of &lt;code&gt;ObjectSpace&lt;/code&gt;. With my patches, that's no longer necessary.&lt;/li&gt;
&lt;li&gt;Hierarchical method cache invalidation: the subject of this whole article.&lt;/li&gt;
&lt;li&gt;Method cache instrumentation: &lt;code&gt;RubyVM::MethodCache&lt;/code&gt; has several useful singleton methods you may want to track, including &lt;code&gt;hits&lt;/code&gt;, &lt;code&gt;misses&lt;/code&gt;, &lt;code&gt;miss_time&lt;/code&gt;, &lt;code&gt;invalidation_time&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;You can find the code in &lt;a href=&quot;https://github.com/jamesgolick/ruby/tree/jamesgolick&quot;&gt;my branch&lt;/a&gt; or install it with &lt;code&gt;rvm install jamesgolick&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I am planning to submit these patches back upstream, but I have to port them to Ruby 2.0 first, so I guess that's my next project. Huge thanks and credit to &lt;a href=&quot;https://twitter.com/tmm1&quot;&gt;Aman Gupta&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/charliesome&quot;&gt;Charlie Somerville&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/headius&quot;&gt;Charles Nutter&lt;/a&gt;, and &lt;a href=&quot;https://github.com/funny-falcon&quot;&gt;funny-falcon&lt;/a&gt; for all their code, help, and testing!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The Cost of Ruby 1.9.3's GC::Profiler</title>
    <link href="/2012/11/19/the-cost-of-ruby-1.9.3-s-gc-profiler.html" />
    <id>tag:jamesgolick.com,2012-11-19:1353356029</id>
    <updated>2012-11-19T15:13:49-05:00</updated>
    <content type="html">&lt;p&gt;This is a long one, and y'all are busy I'm sure so here's the tl;dr:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you run ruby in production, you need to keep track of GC stats.&lt;/li&gt;
&lt;li&gt;Ruby 1.9.3's &lt;a href=&quot;http://www.ruby-doc.org/core-1.9.3/GC/Profiler.html&quot;&gt;&lt;code&gt;GC::Profiler&lt;/code&gt;&lt;/a&gt; does a bunch of really weird shit.

&lt;ul&gt;
&lt;li&gt;It keeps a 104 byte sample of every GC run since it was enabled forever.&lt;/li&gt;
&lt;li&gt;Calling &lt;a href=&quot;http://www.ruby-doc.org/core-1.9.3/GC/Profiler.html#method-c-total_time&quot;&gt;&lt;code&gt;GC::Profiler.total_time&lt;/code&gt;&lt;/a&gt; loops over every sample in memory to calculate the total.&lt;/li&gt;
&lt;li&gt;The space used to keep those samples in memory is &lt;strong&gt;never freed&lt;/strong&gt;. However, it does get reused when you call &lt;a href=&quot;http://www.ruby-doc.org/core-1.9.3/GC/Profiler.html#method-c-clear&quot;&gt;&lt;code&gt;GC::Profiler.clear&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Therefore: if you are using &lt;code&gt;GC::Profiler&lt;/code&gt; in production, and you're not calling &lt;code&gt;GC::Profiler.clear&lt;/code&gt; regularly, you're leaking a substantial amount of memory (&amp;gt;1GB / machine for us), slowing down garbage collection somewhat, and the cost of retreiving the stats (&lt;code&gt;GC::Profiler.total_time&lt;/code&gt;) will continue to increase unbounded until the process is restarted&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I am working on an alternative, low-overhead GC Profiler that is designed to be run in production. It's called &lt;code&gt;GC::BasicProfiler&lt;/code&gt;. You can find the patch &lt;a href=&quot;https://github.com/jamesgolick/ruby/commit/576cba1e79842f7c5ee80d3668958e1571da13d7#L0R3992&quot;&gt;here&lt;/a&gt; and follow development &lt;a href=&quot;https://github.com/jamesgolick/ruby/commit/576cba1e79842f7c5ee80d3668958e1571da13d7#L0R3992&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Also, you may want to check out &lt;a href=&quot;https://github.com/thecodeshop/ruby/commits/tcs-ruby_1_9_3&quot;&gt;this fork&lt;/a&gt; for some backports from ruby 2.0 &amp;mdash; including the COW-friendly garbage collector. Good stuff.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;The Long Version&lt;/h3&gt;

&lt;p&gt;Ruby's GC is a steaming pile of shit &amp;mdash; but that's not news to anybody. If you're running ruby in production, tracking GC behaviour is essential so that you can minimize its effects on perceived performance (hence &lt;a href=&quot;http://unicorn.bogomips.org/Unicorn/OobGC.html&quot;&gt;oob_gc&lt;/a&gt;, etc). Fortunately, Ruby 1.9.3 ships with &lt;a href=&quot;http://www.ruby-doc.org/core-1.9.3/GC/Profiler.html&quot;&gt;GC::Profiler&lt;/a&gt;, which provides detailed instrumentation on GC runs.&lt;/p&gt;

&lt;p&gt;Over the last month or so, I've been working on some rails performance tooling. Last night, I noticed that requests with my instrumentation enabled were taking around an order of magnitude longer than those without it. Weird. So, I installed &lt;a href=&quot;https://github.com/tmm1/perftools.rb&quot;&gt;perftools.rb&lt;/a&gt; and &lt;a href=&quot;https://github.com/bhb/rack-perftools_profiler&quot;&gt;rack-perftools_profiler&lt;/a&gt; and got a really surprising result (irrelevant lines ommitted):&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4113649.js?file=profile1.txt&quot;&gt;&lt;/script&gt;


&lt;p&gt;Apparently calling &lt;code&gt;GC::Profiler.total_time&lt;/code&gt; is so slow that more than 50% of request time was spent in there? Is that actually possible? My instrumentation calls &lt;code&gt;GC::Profiler.total_time&lt;/code&gt; frequently under the assumption that it's inexpensive, but obviously that was a faulty assumption unless perftools.rb is wrong. Let's take a look at the implementation. (the code in context is &lt;a href=&quot;https://github.com/ruby/ruby/blob/ruby_1_9_3/gc.c#L3627-3647&quot;&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4113649.js?file=total_time.c&quot;&gt;&lt;/script&gt;


&lt;p&gt;There's a loop in &lt;code&gt;total_time&lt;/code&gt;? What the fuck is going on here?&lt;/p&gt;

&lt;p&gt;Turns out that if you have &lt;code&gt;GC::Profiler&lt;/code&gt; enabled, the VM records a &lt;a href=&quot;https://github.com/ruby/ruby/blob/ruby_1_9_3/gc.c#L106-124&quot;&gt;&lt;code&gt;gc_profile_record&lt;/code&gt;&lt;/a&gt; every time the garbage collector runs.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4113649.js?file=gc_profile_record.c&quot;&gt;&lt;/script&gt;


&lt;p&gt;Then, when you call &lt;code&gt;total_time&lt;/code&gt;, it loops over all of the &lt;code&gt;gc_profile_record&lt;/code&gt;s that have been created in order to sum the total. According to my profile, &lt;code&gt;total_time&lt;/code&gt; was responsible for more than 50% of request time. How many &lt;code&gt;gc_profile_record&lt;/code&gt;s could there actually be?&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4113649.js?file=gdb1.txt&quot;&gt;&lt;/script&gt;


&lt;p&gt;Oh. Well I guess that explains that. So &amp;mdash; stupid question, but when do these things get freed? Apparently only in &lt;a href=&quot;https://github.com/ruby/ruby/blob/ruby_1_9_3/gc.c#L479-506&quot;&gt;&lt;code&gt;rb_objspace_free&lt;/code&gt;&lt;/a&gt; which only ever gets called &lt;a href=&quot;https://github.com/ruby/ruby/blob/ruby_1_9_3/vm.c#L1624&quot;&gt;when the VM terminates&lt;/a&gt;, so the answer is &lt;em&gt;never&lt;/em&gt;. Cool.&lt;/p&gt;

&lt;p&gt;Upon further investigation, it's pretty clear that this whole system was designed with the expectation that you'd call &lt;a href=&quot;http://www.ruby-doc.org/core-1.9.3/GC/Profiler.html#method-c-clear&quot;&gt;&lt;code&gt;GC::Profiler.clear&lt;/code&gt;&lt;/a&gt; regularly. The profiler keeps its samples in an array at &lt;code&gt;objspace-&amp;gt;profile.record&lt;/code&gt; that it &lt;a href=&quot;https://github.com/ruby/ruby/blob/ruby_1_9_3/gc.c#L168-171&quot;&gt;increases in size by 1000&lt;/a&gt; every time it runs out of space.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4113649.js?file=sample-array-size-increase.c&quot;&gt;&lt;/script&gt;


&lt;p&gt;&lt;strong&gt;If you don't call &lt;code&gt;GC::Profiler.clear&lt;/code&gt;, that array keeps increasing in size forever.&lt;/strong&gt; This is not documented. Obviously.&lt;/p&gt;

&lt;p&gt;On our production systems, unicorn workers that have been running for a few hours had an &lt;code&gt;objspace-&amp;gt;profile.size&lt;/code&gt; of around 350000. On x86_64, &lt;code&gt;sizeof(struct gc_profile_record)&lt;/code&gt; == 104, so around 35MB of overhead per process multiplied by 25 processes per machine for a total of nearly 1GB per machine &amp;mdash; after only 3 hours. That will grow forever until the processes are restarted.&lt;/p&gt;

&lt;p&gt;That's the bad news.&lt;/p&gt;

&lt;h3&gt;The good news: GC::BasicProfiler&lt;/h3&gt;

&lt;p&gt;Ultimately, &lt;code&gt;GC::Profiler&lt;/code&gt; was designed to provide detailed information about every GC run &amp;mdash; probably for the VM implementers to use when tuning the GC (haha yeah right). But seriously, somebody probably wants that, but it isn't me. For those of us who simply want to keep track of GC stats on our production applications, we need a less expensive implementation.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/jamesgolick/ruby/commit/576cba1e79842f7c5ee80d3668958e1571da13d7&quot;&gt;&lt;code&gt;GC::BasicProfiler&lt;/code&gt;&lt;/a&gt; is a first step towards something like that. It has a very simple, low-overhead implementation, and &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/tcs-ruby_1_9_3/gc.c#L3915-3921&quot;&gt;&lt;code&gt;GC::BasicProfiler.total_time&lt;/code&gt;&lt;/a&gt; works the way you might expect.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4113649.js?file=gc_basic_profile_total_time.c&quot;&gt;&lt;/script&gt;


&lt;p&gt;Enabling and disabling &lt;code&gt;BasicProfiler&lt;/code&gt; works exactly the same as &lt;code&gt;Profiler&lt;/code&gt; but you don't need to call &lt;code&gt;clear&lt;/code&gt; to avoid leaking memory. In fact, there's no &lt;code&gt;clear&lt;/code&gt; method at all.&lt;/p&gt;

&lt;p&gt;If you're interested in following the development of this patch, it'll be &lt;a href=&quot;https://github.com/jamesgolick/ruby/blob/tcs-ruby_1_9_3&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;One more lol for the road&lt;/h3&gt;

&lt;script src=&quot;https://gist.github.com/4113649.js?file=lol.txt&quot;&gt;&lt;/script&gt;


&lt;p&gt;It's the little things. Don't worry, though &amp;mdash; fixed in &lt;code&gt;BasicProfiler&lt;/code&gt;.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4113649.js?file=trulth.txt&quot;&gt;&lt;/script&gt;

</content>
  </entry>
  
  <entry>
    <title>Moving On</title>
    <link href="/2012/9/5/moving-on.html" />
    <id>tag:jamesgolick.com,2012-09-05:1346869989</id>
    <updated>2012-09-05T14:33:09-04:00</updated>
    <content type="html">&lt;p&gt;Almost four years ago, I was speaking at a software engineering conference in Montreal. At the speakers lunches, I met up with one of the founders of the conference, and we immediately hit it off. He told me about his growing company, and a month later, the consulting firm I'd been running was closed, our office vacant, and I had joined BitLove (the company that runs FetLife &amp;mdash; which was then known as Protose) as CTO. It's bittersweet to announce that as of a few weeks ago, I've decided to move on.&lt;/p&gt;

&lt;p&gt;Over the last four years, I played a huge role in every part of running FetLife. In addition to being responsible for our technology, I made business and product decisions, helped design features, wrote copy, communicated with the community, worked on support stuff, and more. I've always had an interest in &amp;mdash; and read about &amp;mdash; all this stuff, but actually having the opportunity to participate in it, make real mistakes, and have real successes was incredible.&lt;/p&gt;

&lt;p&gt;As a technologist, it's hard to imagine somewhere I could've grown more quickly. When I joined, I knew a few things about writing Rails apps. While I was there, I got the opportunity to do everything with every part of the stack. I learned how to make it all run in production for a big user base and a lot of traffic, with a tiny team.&lt;/p&gt;

&lt;p&gt;When I joined, we had ~100k users (I got user ID 129315) and Rails was serving around 50 million requests a month. Since then, our user base grew to over 1.5 million users &amp;mdash; our traffic to almost 500 million pageviews a month and over 1 billion Rails requests (not to mention requests to other services like chat). We did it with an engineering team that hovered around 2 people (including me).&lt;/p&gt;

&lt;p&gt;I'm really proud of the engineering work I did at BitLove. We operated an extremely high throughput MySQL installation, and I was able to &lt;a href=&quot;/2012/7/18/innodb-kernel-mutex-contention-and-memory-allocators.html&quot;&gt;solve&lt;/a&gt; various InnoDB scalability limitations that we encountered. I implemented a web-based IM system (similar to Facebook Chat) that hundreds of thousands of people use to send tens of millions of messages every month. I built everything from the presence and routing implementation in Erlang to the UI in Javascript. I also designed and built an extremely stable and fast activity stream architecture, almost single-handedly ran operations for the ~40 machine cluster in around 2 hours a week, and perhaps most importantly, I &lt;a href=&quot;/2012/7/7/how-to-lose-100-pounds.html&quot;&gt;dramatically improved my health&lt;/a&gt; in the process.&lt;/p&gt;

&lt;p&gt;The work I did at BitLove certainly represents the biggest challenges and accomplishments of my life and career to date. It was a wild ride with many ups and downs &amp;mdash; everything they promised a startup would be. So it was incredibly difficult to leave a growing and successful company that I had a big hand in building. But it's time for new challenges.&lt;/p&gt;

&lt;h3&gt;So what's next?&lt;/h3&gt;

&lt;p&gt;I've got a couple of really amazing opportunities on the table right now that I'm super excited about. Because I get so heavily invested in my work and like to stick around companies for many years, this is a very big decision, and I'm not taking it lightly. I'm certainly open to hearing about any opportunities you think I might be a fit for, so do get in touch!&lt;/p&gt;

&lt;h3&gt;Consulting&lt;/h3&gt;

&lt;p&gt;In the meantime, I'm available for consulting work. If your company needs help with any of the kinds of things I discussed above &amp;mdash; especially performance and scalability stuff, we should chat. &lt;a href=&quot;mailto:jamesgolick@gmail.com&quot;&gt;Email me&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>InnoDB kernel_mutex Contention and Memory Allocators</title>
    <link href="/2012/7/18/innodb-kernel-mutex-contention-and-memory-allocators.html" />
    <id>tag:jamesgolick.com,2012-07-18:1342651301</id>
    <updated>2012-07-18T18:41:41-04:00</updated>
    <content type="html">&lt;p&gt;&lt;em&gt;tl;dr: We found that in our case, contention for InnoDB's &lt;code&gt;kernel_mutex&lt;/code&gt; was caused by contention for a malloc arena lock. We fixed it by moving to tcmalloc. Instructions on how to do that &lt;a href=&quot;#preload-instructions&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We recently doubled the IO throughput capacity of our near-capacity MySQL master by adding a second RAID controller, and striping the two together. As we were climbing up to a record throughput peak the following weekend, there was a major db latency spike (&gt;3x).&lt;/p&gt;

&lt;p&gt;A look at SHOW ENGINE INNODB STATUS indicated quite a bit of contention for InnoDB's &lt;code&gt;kernel_mutex&lt;/code&gt;.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3146976.js?file=innodb-status&quot;&gt;&lt;/script&gt;


&lt;p&gt;&lt;em&gt;Note: the contention I observed was actually considerably worse than what I pasted above, but I didn't save the output, so this is all I have to show.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;kernel_mutex&lt;/code&gt; has &lt;a href=&quot;http://blogs.innodb.com/wp/2011/04/mysql-5-6-innodb-scalability-fix-kernel-mutex-removed/&quot;&gt;been removed&lt;/a&gt; in MySQL 5.6, but that's unfortunately not ready for production. As a workaround, the Percona guys &lt;a href=&quot;http://www.mysqlperformanceblog.com/2011/12/02/kernel_mutex-problem-or-double-throughput-with-single-variable/&quot;&gt;suggest&lt;/a&gt; modifying &lt;code&gt;innodb_sync_spin_loops&lt;/code&gt;, which had absolutely no effect for our workload. They also &lt;a href=&quot;http://www.mysqlperformanceblog.com/2011/12/02/kernel_mutex-problem-cont-or-triple-your-throughput/&quot;&gt;suggest&lt;/a&gt; lowering &lt;code&gt;innodb_thread_concurrency&lt;/code&gt;, which did reduce contention, but it also reduced concurrency, which left us right back where we started.&lt;/p&gt;

&lt;p&gt;I pulled out my &lt;a href=&quot;http://poormansprofiler.org/&quot;&gt;poor man's profiler&lt;/a&gt; to see if I could figure out exactly what was holding the lock and what it was doing with it. Here are the stacks I got.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3146976.js?file=gistfile1.txt&quot;&gt;&lt;/script&gt;


&lt;p&gt;Immediately, we can see that lots of stuff is waiting on locks inside of malloc/free-related functions. After reading through the MySQL sources, it was clear that this thread was holding the &lt;code&gt;kernel_mutex&lt;/code&gt;.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3146976.js?file=lock-holder&quot;&gt;&lt;/script&gt;


&lt;p&gt;&lt;em&gt;Note: all links to glibc code below are specifically to the version that we are using.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Reading through &lt;a href=&quot;https://github.com/cdepillabout/glibc/blob/e28c88707ef0529593fccedf1a94c3fce3df0ef3/malloc/malloc.c#L4763&quot;&gt;&lt;code&gt;_int_free&lt;/code&gt;&lt;/a&gt; in the glibc sources seemed to indicate that there was only &lt;a href=&quot;https://github.com/cdepillabout/glibc/blob/e28c88707ef0529593fccedf1a94c3fce3df0ef3/malloc/malloc.c#L2362&quot;&gt;one lock&lt;/a&gt; (&lt;code&gt;malloc_state-&amp;gt;mutex&lt;/code&gt;) in there.&lt;/p&gt;

&lt;p&gt;Our glibc &lt;em&gt;was&lt;/em&gt; built with &lt;a href=&quot;https://github.com/cdepillabout/glibc/blob/e28c88707ef0529593fccedf1a94c3fce3df0ef3/malloc/Makefile#L128&quot;&gt;&lt;code&gt;--enable-experimental-malloc&lt;/code&gt;&lt;/a&gt;, which is supposed to &lt;a href=&quot;https://github.com/cdepillabout/glibc/blob/e28c88707ef0529593fccedf1a94c3fce3df0ef3/malloc/arena.c&quot;&gt;reduce contention&lt;/a&gt; by dividing the heap in to multiple arenas, each with their own lock (at least as far as I understand it &amp;mdash; and I'm far from an expert).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc.html&quot;&gt;tcmalloc&lt;/a&gt; is a malloc implementation from &lt;a href=&quot;http://google-perftools.googlecode.com/&quot;&gt;google-perftools&lt;/a&gt; that satisfies small malloc requests without locks by using a per-thread cache. Using tcmalloc should mean that the allocations inside the &lt;code&gt;kernel_mutex&lt;/code&gt; are (at least mostly) lockless.&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;preload-instructions&quot;&gt;&lt;/a&gt;
Here's how to &lt;code&gt;LD_PRELOAD&lt;/code&gt; tcmalloc.&lt;/p&gt;

&lt;p&gt;Put this in &lt;code&gt;/usr/local/bin/mysqld_wrapper&lt;/code&gt;:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3146976.js?file=mysqld_wrapper.sh&quot;&gt;&lt;/script&gt;


&lt;p&gt;Put this fragment in my.cnf:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3146976.js?file=my.cnf&quot;&gt;&lt;/script&gt;


&lt;p&gt;Since we moved to tcmalloc, all of the contention for the &lt;code&gt;kernel_mutex&lt;/code&gt; has completely disappeared. We're also seeing better performance overall and using ~15% less memory in total. This fix probably isn't applicable in all cases, but if you're seeing &lt;code&gt;kernel_mutex&lt;/code&gt; contention, it's worth using your poor man's profiler to see whether swapping allocators might help.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>How to Lose 100 Pounds</title>
    <link href="/2012/7/7/how-to-lose-100-pounds.html" />
    <id>tag:jamesgolick.com,2012-07-07:1341683833</id>
    <updated>2012-07-07T13:57:13-04:00</updated>
    <content type="html">&lt;p&gt;I've struggled with my weight for nearly my entire life. I went from being chubby in elementary school to overweight in high school to obese in university. At my biggest, I was almost 280 pounds (I'm 5'6&quot;). Finally, around 5 years ago, I got a spark of inspiration that ultimately led to me dropping a total of 110 pounds (and counting). Here's how I did it.&lt;/p&gt;

&lt;p&gt;But first, the obligatory before and after shots:&lt;/p&gt;

&lt;p&gt;
  &lt;img src=&quot;http://farm2.staticflickr.com/1410/1476468549_ed43518cbc_z.jpg?zz=1&quot; width=&quot;350&quot; /&gt;
  &lt;img src=&quot;https://flpics0.a.ssl.fastly.net/129/129315/0004b750-74d0-e92b-397e-2b8ad87ed1f7_720.jpg&quot; width=350 /&gt;
&lt;/p&gt;


&lt;h3&gt;Motivation&lt;/h3&gt;

&lt;p&gt;Losing weight requires an enormous amount of motivation. You're going to have to change your lifestyle and make real sacrifices. It's going to be hard. Motivation will help you continue to justify the changes you've made, and prevent you from slipping back in to old habits.&lt;/p&gt;

&lt;p&gt;Funny enough, I actually got my first seed of motivation from pneumonia. I was 278 pounds at the time. After three horrible, bed-ridden weeks, I was down to 258. It was painful, but it taught me the most important weight loss lesson of all: it's possible.&lt;/p&gt;

&lt;p&gt;Like a lot of other kids from my generation, I grew up overweight. When you can't remember a time when you weren't, being fat is a part of your identity. So, silly as it sounds, I think there was a part of me that believed that weight loss was impossible on some level &amp;mdash; or at least that the amount of weight I needed to lose was insurmountable.&lt;/p&gt;

&lt;p&gt;If you only take one thing away from this article, let it be that. You can lose weight. No matter how fucked up your metabolism (more on that later), no matter how long you've been overweight, it &lt;i&gt;is&lt;/i&gt; possible.&lt;/p&gt;

&lt;h3&gt;Strategies&lt;/h3&gt;

&lt;p&gt;I'm going to talk about a few of the strategies, diets, and other random things that I have tried because I think people will find them interesting. But I'll give you an easy way out of reading the rest of this article just in case you're already bored. Ready? Here it is.&lt;/p&gt;

&lt;p&gt;STOP EATING PROCESSED FOOD. THAT INCLUDES SUGAR, WHEAT PRODUCTS, SUGAR REPLACEMENTS LIKE SUCRALOSE, ASPARTAME, ETC, AND EVERYTHING ELSE YOU'RE THINKING OF THAT MIGHT BE AN EXCEPTION. EXCEPT STEVIA. YOU CAN HAVE STEVIA.&lt;/p&gt;

&lt;p&gt;Ok, so with that yelling out of the way, here's a bit about my journey.&lt;/p&gt;

&lt;h3&gt;Briefly On Exercise&lt;/h3&gt;

&lt;p&gt;I'm going to keep this short. Exercise has never helped me lose weight. For much of the time that I was grossly over weight, I was also extremely physically active, often whitewater kayaking or downhill skiing for several hours 4 or 5 days a week, and continuing to put on fat. Despite conventional wisdom to the contrary, exercise isn't an effective weight loss strategy &lt;i&gt;for me&lt;/i&gt;.&lt;/p&gt;

&lt;h3&gt;Portion Control&lt;/h3&gt;

&lt;p&gt;After I lost the pneumonia weight, I was literally terrified that I might put it back on. So I decided to try eating less. I ate all the same things, but avoided going back for seconds. I ate pasta, pizza, and dessert until I was full, but not stuffed. I lost 20 more pounds over a few months. Then, it leveled off.&lt;/p&gt;

&lt;p&gt;That, really, is the story of my weight loss effort. Strategies, and diets that work for a while and then plateau. Sometimes, it's possible to break through a plateau, but other times, you need to up your game with better eating.&lt;/p&gt;

&lt;p&gt;I tried for another six or so months to break through the portion control plateau. It never happened. I was actually feeling pretty good about where I was, though, so I didn't really make much of an effort to progress for a few more months.&lt;/p&gt;

&lt;h3&gt;Lower Carb Diet&lt;/h3&gt;

&lt;p&gt;Shortly after moving from Montreal to Vancouver, I started seeing a personal trainer, hoping to accelerate my progress on the scale and in the gym. She had me keep a food journal, and immediately picked up on the amount of carbs that I was eating back then. I was vegetarian at the time, and I was eating tons of breads and pastas. She told me to eat more vegetables, and tofu, and watch my carb intake. I lost about 20 pounds before plateauing &lt;i&gt;hard&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;On this diet, I was still eating bread, pasta, and sugar, just less. And after a while, I found it impossible to continue losing weight. So I started looking for other solutions.&lt;/p&gt;

&lt;h3&gt;Eat to Live&lt;/h3&gt;

&lt;p&gt;Eat to Live is an all vegan diet designed by Dr. Joel Fuhrman. Only fruits, vegetables, legumes, nuts, and seeds are allowed; no oils, dairy, sugar, or even juices (bit of an exaggeration, but for our purposes this is accurate enough) are permitted. I had very mixed results on Eat to Live. I did lose about 15 pounds, but I had a very difficult time keeping it off, and found it very difficult to eat enough food to feel full for more than an hour at a time. I found that I was constantly eating, and still often feeling starvingly hungry.&lt;/p&gt;

&lt;p&gt;With that said, I actually know a lot of people who've had great success on ETL including my ex-girlfriend, who I was living with at the time (so we were eating nearly identically, though me significantly more than her), and my good friend &lt;a href=&quot;http://gilesbowkett.blogspot.com&quot;&gt;Giles&lt;/a&gt;, who actually introduced me to the book. Which brings me to another one of my weight loss conclusions.&lt;/p&gt;

&lt;p&gt;Everybody's body is different. Some people have amazing success on a diet, while others are incapable of losing weight. I no longer believe that there's one perfect diet out there that suits everybody. Your mileage will vary with every approach.&lt;/p&gt;

&lt;p&gt;The only consistent thing I've been able to identify across all my friends and family who've lost weight is avoiding processed foods.&lt;/p&gt;

&lt;h3&gt;Psoriasis and acne&lt;/h3&gt;

&lt;p&gt;An interesting aside here is that ETL led me discover that it's possible to control psoriasis with diet. The medical community doesn't seem to be aware of this, but I am completely psoriasis free after years of being covered in it.&lt;/p&gt;

&lt;p&gt;At first, I thought that it was the greens that caused my skin to clear up, but since then, I've realized that it's a balance of factors. Greens &lt;i&gt;do&lt;/i&gt; help, but merely avoiding processed foods is enough to keep me completely psoriasis free. That being said, I started drinking coffee again a little while ago, and noticed that a small amount of psoriasis came back. Upping my intake of greens seems to make it clear up. So, it's a bit of a balancing act.&lt;/p&gt;

&lt;p&gt;Oh also, I'm extremely prone to acne, but I've found that avoiding high &lt;a href=&quot;http://www.glycemicindex.com/&quot;&gt;glycemic index&lt;/a&gt; foods keeps my face and body completely clear of pimples.&lt;/p&gt;

&lt;h3&gt;On Vegetarianism&lt;/h3&gt;

&lt;p&gt;I'm definitely going to get hate mail for this, but here goes anyway. I was vegetarian for most of my weight loss journey. My conclusion was ultimately that vegetarianism made it significantly more difficult to lose weight. Here's why.&lt;/p&gt;

&lt;p&gt;At home, cooking my own meals from my own groceries, vegetarianism was perfectly fine. But, every time I ate in a restaurant, on the street, or even at a friend's place, my options were nearly invariably some combination of pasta, bread, and sugar. I probably have the shittiest metabolism in the world, but when I eat that stuff, I gain weight. Lots of it.&lt;/p&gt;

&lt;p&gt;I really enjoy eating in restaurants, which made the whole thing all the more difficult. During the whole time that I was on Eat to Live, I would painstakingly lose 7 or 8 pounds by religiously sticking to the diet for a month, then travel to a conference for a week and gain 15. It was frustrating to say the least, which led me to the very difficult conclusion that I needed to at least try breaking my nearly ten years of vegetarianism.&lt;/p&gt;

&lt;h3&gt;My Current Diet&lt;/h3&gt;

&lt;p&gt;My current diet is really simple: no processed carbs (that includes 'carbless' sugar replacements except stevia). I go through periods where I eat a ton of fruits and vegetables, but lately, I've mostly been eating meat and fish.&lt;/p&gt;

&lt;p&gt;Do I miss chocolate and ice cream? Definitely. But I eat guilt-free bacon or chicken wings whenever I feel like it, and seeing results makes the sacrifice more than worthwhile.&lt;/p&gt;

&lt;p&gt;This diet means that when I go out to eat (which I do regularly), I can have a steak without feeling guilty. I tell people that I'm allergic to sugar and flour, which gives me a reasonable excuse for being the pain in the ass guy who has to ask the waiter about the ingredients in every dish on the menu. I'd encourage you to tell similar lies if they help you stick to a diet.&lt;/p&gt;

&lt;p&gt;There've been a few periods over the last year and a half where I've started eating bread again and gained back a bunch of weight. In April of this year, though, I finally committed to this diet as a more permanent lifestyle, and have been ever since. I've dropped around 40 pounds since then, and I'm not stopping until I can see my abs.&lt;/p&gt;

&lt;h3&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;Everybody's body is different. Your friends may have had success with diet X, but you may not. Don't let that discourage you. You'll find something that works.&lt;/p&gt;

&lt;p&gt;The best diet is the one that you can stick to, even if the weight loss is slower. If a diet fights against your lifestyle, it's going to be that much harder to maintain. That was my problem with ETL. I love to eat out and I travel a lot, so I couldn't stick to it. And at the end of the day, I didn't lose weight. The less you have to change your lifestyle to accomplish your goals, the better your chances of success.&lt;/p&gt;

&lt;p&gt;You can lose weight, still enjoy the food you eat, and even go out to restaurants while you do it. Obviously, you won't be able to eat everything you're eating now, because if you could, you'd already be thin. But, it'll be a sacrifice worth making. The best thing you've ever done.&lt;/p&gt;
</content>
  </entry>
  
</feed>
