<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged bool</title>
<link>https://coderlegion.com/tag/bool</link>
<description>Powered by Question2Answer</description>
<item>
<title>Optimizing Memory Layout in Go: A Deep Dive into Struct Design</title>
<link>https://coderlegion.com/14659/optimizing-memory-layout-in-go-a-deep-dive-into-struct-design</link>
<description>
&lt;p&gt;&lt;img src=&quot;https://blog.ratnesh-maurya.com/images/blog/Optimizing-Memory-Layout-in-Go-A-Deep-Dive-into-Struct-Design.jpg&quot; alt=&quot;Optimizing Memory Layout in Go: A Deep Dive into Struct Design&quot;&gt;&lt;/p&gt;&lt;p&gt;Reorder the fields in a Go struct and the size changes &amp;mdash; without changing the data it holds. A &lt;code&gt;bool&lt;/code&gt; next to an &lt;code&gt;int64&lt;/code&gt; wastes 7 bytes of padding. Across 10 million allocations, that's 67MB of memory you're paying for but never using.&lt;/p&gt;
&lt;p&gt;This matters in high-throughput services where struct slices dominate heap usage: event pipelines, in-memory caches, analytics collectors.&lt;/p&gt;
&lt;h2 id=&quot;how-alignment-and-padding-work&quot;&gt;How alignment and padding work&lt;/h2&gt;
&lt;p&gt;Go stores struct fields in a contiguous block of memory. Each field must be aligned to a memory address that's a multiple of its own size &amp;mdash; &lt;code&gt;int64&lt;/code&gt; aligns to 8 bytes, &lt;code&gt;int32&lt;/code&gt; to 4, &lt;code&gt;bool&lt;/code&gt; to 1. When a smaller field is followed by a larger one, the compiler inserts invisible padding bytes to satisfy the alignment requirement.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-go&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;type&lt;/span&gt; Bad &lt;span class=&quot;hljs-keyword&quot;&gt;struct&lt;/span&gt; {
    Active  &lt;span class=&quot;hljs-type&quot;&gt;bool&lt;/span&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// 1 byte&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// 7 bytes padding&lt;/span&gt;
    Balance &lt;span class=&quot;hljs-type&quot;&gt;float64&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;// 8 bytes&lt;/span&gt;
    Age     &lt;span class=&quot;hljs-type&quot;&gt;uint8&lt;/span&gt;   &lt;span class=&quot;hljs-comment&quot;&gt;// 1 byte&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// 7 bytes padding&lt;/span&gt;
}
&lt;span class=&quot;hljs-comment&quot;&gt;// Total: 24 bytes (only 10 bytes of actual data)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reorder from largest to smallest:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-go&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;type&lt;/span&gt; Good &lt;span class=&quot;hljs-keyword&quot;&gt;struct&lt;/span&gt; {
    Balance &lt;span class=&quot;hljs-type&quot;&gt;float64&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;// 8 bytes&lt;/span&gt;
    Active  &lt;span class=&quot;hljs-type&quot;&gt;bool&lt;/span&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// 1 byte&lt;/span&gt;
    Age     &lt;span class=&quot;hljs-type&quot;&gt;uint8&lt;/span&gt;   &lt;span class=&quot;hljs-comment&quot;&gt;// 1 byte&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// 6 bytes padding (struct itself aligns to 8)&lt;/span&gt;
}
&lt;span class=&quot;hljs-comment&quot;&gt;// Total: 16 bytes (same 10 bytes of data, 8 bytes less waste)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's a 33% reduction per struct, just by reordering fields.&lt;/p&gt;
&lt;h2 id=&quot;measuring-the-difference&quot;&gt;Measuring the difference&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;reflect.TypeOf&lt;/code&gt; and &lt;code&gt;unsafe.Sizeof&lt;/code&gt; to check struct sizes at runtime:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-go&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;package&lt;/span&gt; main

&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; (
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;reflect&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;unsafe&quot;&lt;/span&gt;
)

&lt;span class=&quot;hljs-keyword&quot;&gt;type&lt;/span&gt; Bad &lt;span class=&quot;hljs-keyword&quot;&gt;struct&lt;/span&gt; {
    Active  &lt;span class=&quot;hljs-type&quot;&gt;bool&lt;/span&gt;
    Balance &lt;span class=&quot;hljs-type&quot;&gt;float64&lt;/span&gt;
    Age     &lt;span class=&quot;hljs-type&quot;&gt;uint8&lt;/span&gt;
}

&lt;span class=&quot;hljs-keyword&quot;&gt;type&lt;/span&gt; Good &lt;span class=&quot;hljs-keyword&quot;&gt;struct&lt;/span&gt; {
    Balance &lt;span class=&quot;hljs-type&quot;&gt;float64&lt;/span&gt;
    Active  &lt;span class=&quot;hljs-type&quot;&gt;bool&lt;/span&gt;
    Age     &lt;span class=&quot;hljs-type&quot;&gt;uint8&lt;/span&gt;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt; {
    fmt.Println(&lt;span class=&quot;hljs-string&quot;&gt;&quot;Bad:&quot;&lt;/span&gt;, unsafe.Sizeof(Bad{}), &lt;span class=&quot;hljs-string&quot;&gt;&quot;bytes&quot;&lt;/span&gt;)   &lt;span class=&quot;hljs-comment&quot;&gt;// 24&lt;/span&gt;
    fmt.Println(&lt;span class=&quot;hljs-string&quot;&gt;&quot;Good:&quot;&lt;/span&gt;, unsafe.Sizeof(Good{}), &lt;span class=&quot;hljs-string&quot;&gt;&quot;bytes&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-comment&quot;&gt;// 16&lt;/span&gt;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Field-by-field inspection&lt;/span&gt;
    t := reflect.TypeOf(Bad{})
    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; i := &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;; i &amp;lt; t.NumField(); i++ {
        f := t.Field(i)
        fmt.Printf(&lt;span class=&quot;hljs-string&quot;&gt;&quot;  %s: size=%d, offset=%d\n&quot;&lt;/span&gt;, f.Name, f.Type.Size(), f.Offset)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-much-memory-this-saves-at-scale&quot;&gt;How much memory this saves at scale&lt;/h2&gt;
&lt;p&gt;Here's the math for a real scenario &amp;mdash; an analytics service tracking page view events:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-go&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;type&lt;/span&gt; PageView &lt;span class=&quot;hljs-keyword&quot;&gt;struct&lt;/span&gt; {
    &lt;span class=&quot;hljs-comment&quot;&gt;// Unoptimized layout&lt;/span&gt;
    IsBot      &lt;span class=&quot;hljs-type&quot;&gt;bool&lt;/span&gt;      &lt;span class=&quot;hljs-comment&quot;&gt;// 1 + 7 padding&lt;/span&gt;
    Timestamp  &lt;span class=&quot;hljs-type&quot;&gt;int64&lt;/span&gt;     &lt;span class=&quot;hljs-comment&quot;&gt;// 8&lt;/span&gt;
    StatusCode &lt;span class=&quot;hljs-type&quot;&gt;uint16&lt;/span&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// 2 + 6 padding&lt;/span&gt;
    Duration   &lt;span class=&quot;hljs-type&quot;&gt;int64&lt;/span&gt;     &lt;span class=&quot;hljs-comment&quot;&gt;// 8&lt;/span&gt;
    UserID     &lt;span class=&quot;hljs-type&quot;&gt;uint32&lt;/span&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// 4 + 4 padding&lt;/span&gt;
    PathHash   &lt;span class=&quot;hljs-type&quot;&gt;uint64&lt;/span&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// 8&lt;/span&gt;
}
&lt;span class=&quot;hljs-comment&quot;&gt;// Size: 48 bytes&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;type&lt;/span&gt; PageViewOptimized &lt;span class=&quot;hljs-keyword&quot;&gt;struct&lt;/span&gt; {
    &lt;span class=&quot;hljs-comment&quot;&gt;// Sorted by alignment: 8 &amp;rarr; 4 &amp;rarr; 2 &amp;rarr; 1&lt;/span&gt;
    Timestamp  &lt;span class=&quot;hljs-type&quot;&gt;int64&lt;/span&gt;
    Duration   &lt;span class=&quot;hljs-type&quot;&gt;int64&lt;/span&gt;
    PathHash   &lt;span class=&quot;hljs-type&quot;&gt;uint64&lt;/span&gt;
    UserID     &lt;span class=&quot;hljs-type&quot;&gt;uint32&lt;/span&gt;
    StatusCode &lt;span class=&quot;hljs-type&quot;&gt;uint16&lt;/span&gt;
    IsBot      &lt;span class=&quot;hljs-type&quot;&gt;bool&lt;/span&gt;
}
&lt;span class=&quot;hljs-comment&quot;&gt;// Size: 32 bytes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Struct count&lt;/th&gt;
&lt;th&gt;Unoptimized&lt;/th&gt;
&lt;th&gt;Optimized&lt;/th&gt;
&lt;th&gt;Saved&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100K&lt;/td&gt;
&lt;td&gt;4.6 MB&lt;/td&gt;
&lt;td&gt;3.1 MB&lt;/td&gt;
&lt;td&gt;1.5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1M&lt;/td&gt;
&lt;td&gt;45.8 MB&lt;/td&gt;
&lt;td&gt;30.5 MB&lt;/td&gt;
&lt;td&gt;15.3 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10M&lt;/td&gt;
&lt;td&gt;457 MB&lt;/td&gt;
&lt;td&gt;305 MB&lt;/td&gt;
&lt;td&gt;152 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;At 10 million structs, the difference is 152MB &amp;mdash; enough to matter for your container memory limits and GC pressure.&lt;/p&gt;
&lt;h2 id=&quot;automated-detection-with-fieldalignment&quot;&gt;Automated detection with fieldalignment&lt;/h2&gt;
&lt;p&gt;You don't need to manually audit every struct. The &lt;code&gt;fieldalignment&lt;/code&gt; analyzer from &lt;code&gt;golang.org/x/tools&lt;/code&gt; catches suboptimal layouts automatically:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest
fieldalignment ./...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It reports every struct that could be smaller and suggests the optimal field order. You can also run it as part of &lt;code&gt;golangci-lint&lt;/code&gt; by enabling the &lt;code&gt;govet&lt;/code&gt; linter with the &lt;code&gt;fieldalignment&lt;/code&gt; check.&lt;/p&gt;
&lt;h2 id=&quot;the-alignment-rules&quot;&gt;The alignment rules&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Alignment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;byte&lt;/code&gt;, &lt;code&gt;uint8&lt;/code&gt;, &lt;code&gt;int8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 byte&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uint16&lt;/code&gt;, &lt;code&gt;int16&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2 bytes&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uint32&lt;/code&gt;, &lt;code&gt;int32&lt;/code&gt;, &lt;code&gt;float32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uint64&lt;/code&gt;, &lt;code&gt;int64&lt;/code&gt;, &lt;code&gt;float64&lt;/code&gt;, pointer, &lt;code&gt;string&lt;/code&gt;, slice, map, interface&lt;/td&gt;
&lt;td&gt;8 bytes&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The general rule: &lt;strong&gt;sort fields from largest alignment to smallest.&lt;/strong&gt; This minimizes padding because smaller fields can pack together in the leftover space after larger fields.&lt;/p&gt;
&lt;p&gt;Structs themselves are padded to a multiple of their largest field's alignment. That's why the &lt;code&gt;Good&lt;/code&gt; struct above is 16 bytes (multiple of 8) even though the data only needs 10 bytes.&lt;/p&gt;
&lt;h2 id=&quot;when-not-to-bother&quot;&gt;When not to bother&lt;/h2&gt;
&lt;p&gt;Field ordering optimization is worth the effort when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You allocate millions of the same struct (event pipelines, time-series data, game state)&lt;/li&gt;
&lt;li&gt;The struct is stored in a large slice that stays in memory&lt;/li&gt;
&lt;li&gt;You're hitting container memory limits or seeing heavy GC pauses&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's not worth the effort when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The struct is allocated once or a handful of times&lt;/li&gt;
&lt;li&gt;Readability would suffer from rearranging logically grouped fields&lt;/li&gt;
&lt;li&gt;The struct is mostly pointers and strings (already 8-byte aligned)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Run &lt;code&gt;fieldalignment&lt;/code&gt; on your codebase as a CI check. Fix the easy wins &amp;mdash; the structs that save 8+ bytes per instance &amp;mdash; and leave the rest alone. The tool does the thinking for you.&lt;/p&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/14659/optimizing-memory-layout-in-go-a-deep-dive-into-struct-design</guid>
<pubDate>Thu, 16 Apr 2026 10:59:45 +0000</pubDate>
</item>
</channel>
</rss>