{"id":92,"date":"2025-02-03T08:48:02","date_gmt":"2025-02-03T08:48:02","guid":{"rendered":"https:\/\/www.mastertoncodes.com\/?p=92"},"modified":"2025-02-03T08:48:02","modified_gmt":"2025-02-03T08:48:02","slug":"thanks-for-the-linkedin-memories","status":"publish","type":"post","link":"https:\/\/www.mastertoncodes.com\/?p=92","title":{"rendered":"Thanks for the (LinkedIn) memories"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">I got sucked into a LinkedIn post on &#8220;Why Dictionaries are 7 x slower than arrays&#8221;, and shuddered as the BigO crowd leapt all over it, completely missing the point entirely. It wasn&#8217;t supposed to be about O(n)Log(banana)^Transformer. It was about memory. And computer architecture. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So please, indulge me, here is the real reason why a C# Dictionary is 7x slower than an array.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you have a <strong>standard array<\/strong> (e.g., <code>MyClass[]<\/code>, <em>not<\/em> a <code>List&lt;&gt;<\/code>!), it&#8217;s <strong>contiguous in memory<\/strong>, which means:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Sequential access with great cache locality<\/strong><\/li>\n\n\n\n<li><strong>No-brainer prefetching<\/strong> for the CPU<\/li>\n\n\n\n<li><strong>Little to no pointer indirection<\/strong> since the size and location of each instance are known ahead of time<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;ve got the size, you&#8217;ve got the location, it&#8217;s already in the cache\u2014you\u2019re golden! <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Now, enter <code>Dictionary&lt;int, MyClass&gt;<\/code>.<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Oh boy. First, we need a <strong>hash<\/strong>. Not great, but whatever, we can do a <code>%<\/code> without overheating, no big deal. Now we\u2019ve got a <strong>bucket<\/strong>. If we\u2019re lucky, the first entry is what we\u2019re looking for\u2014no need to walk the chain looking for our hash. Phew! <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But even if we do have to probe further, it&#8217;s <strong>not that bad<\/strong> because the dictionary stores its entries in a <strong>contiguous array of structs (<code>Entry&lt;TKey, TValue&gt;<\/code>)<\/strong>, which means:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>When you pull in one entry, you\u2019re <strong>pulling in a cacheline\u2019s worth of them<\/strong> (~24 bytes per entry, rounded up).<\/li>\n\n\n\n<li>So, as long as you stay within that cacheline, things remain decently fast.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Already, this is getting a bit beefy. <strong>And we still haven\u2019t even touched our actual data yet.<\/strong> <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The real problem? <strong>Heap allocation.<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">At this point, we&#8217;ve found our <code>TValue<\/code> in the <code>Entry[]<\/code> array. But now, <strong>all bets are off<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With a <strong>contiguous array<\/strong>, the next bit of data <strong>is guaranteed<\/strong> to be right after the last bit.<br>With a <strong>dictionary? Nope.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Your <code>dict[0]<\/code>, <code>dict[1]<\/code>, and <code>dict[2]<\/code> could live <strong>literally anywhere<\/strong> in memory.<br>Each lookup could cause <strong>cache misses, cache evictions, and CPU stalls<\/strong> all over the place. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But <code>dict[0]<\/code>, <code>dict[1]<\/code>, and <code>dict[2]<\/code> looks so convincing. That delicious syntactic sugar. But remember when you allocated the memory for those entries? It was off the heap. Was it immediately after each other? Maybe. Does that it make it contiguous? Hell no!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>And that&#8217;s the real reason dictionaries are slower than arrays.<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Memory is slow.<\/strong> CPUs <strong>stall<\/strong> if they don\u2019t get the data they need. That\u2019s it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>The big caveat<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">That said, <strong>computers are plenty fast<\/strong>, and <strong>dictionaries are super useful<\/strong>. I use them all over the place. And when I don\u2019t, I use <code>List&lt;&gt;<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Why?<\/strong> Because <strong>convenience outweighs speed&#8230; until it doesn\u2019t.<\/strong><br>And when it doesn\u2019t?<br>You better know how to <strong>diagnose, debug, and fix it.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Randall Hyde has a fantastic series of books on Amazon called Write Great Code. if you haven&#8217;t, go read them. When you better understand the machine you will write better code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Or at least, when you write sloppy code, you will know why its sloppy and you&#8217;ll be able to justify it \ud83d\ude42 <\/p>\n","protected":false},"excerpt":{"rendered":"<p>I got sucked into a LinkedIn post on &#8220;Why Dictionaries are 7 x slower than arrays&#8221;, and shuddered as the BigO crowd leapt all over it, completely missing the point entirely. It wasn&#8217;t supposed to be about O(n)Log(banana)^Transformer. It was about memory. And computer architecture. So please, indulge me, here is the real reason why&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-92","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.mastertoncodes.com\/index.php?rest_route=\/wp\/v2\/posts\/92","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mastertoncodes.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mastertoncodes.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mastertoncodes.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mastertoncodes.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=92"}],"version-history":[{"count":2,"href":"https:\/\/www.mastertoncodes.com\/index.php?rest_route=\/wp\/v2\/posts\/92\/revisions"}],"predecessor-version":[{"id":95,"href":"https:\/\/www.mastertoncodes.com\/index.php?rest_route=\/wp\/v2\/posts\/92\/revisions\/95"}],"wp:attachment":[{"href":"https:\/\/www.mastertoncodes.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=92"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mastertoncodes.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=92"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mastertoncodes.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=92"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}