Upgrade to Pro — share decks privately, control downloads, hide ads and more …

fieldalignmentから見るGoの構造体

kuro
April 24, 2025

 fieldalignmentから見るGoの構造体

社内LT会で使ったスライドです。

参考リンク
- fieldalignment https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignment
- Big Sky :: Go の struct は小さくできる(fieldalignment のススメ)
https://mattn.kaoriya.net/software/lang/go/20220127151742.htm
- "Why Memory Alignment Matters in Go: Making Your Structs Lean and Fast" by Anh Tu Nguyen
https://dev.to/tuna99/why-memory-alignment-matters-in-go-making-your-structs-lean-and-fast-1kfk
- What’s false sharing and how to solve it (using Golang as example) by Genchi Lu
https://medium.com/p/whats-false-sharing-and-how-to-solve-it-using-golang-as-example-ef978a305e10?source=social.tw

kuro

April 24, 2025
Tweet

More Decks by kuro

Other Decks in Programming

Transcript

  1. // before type PoorlyAligned struct { a byte // 1バイト

    b int64 // 8バイト (7バイトのパディングが必要) c byte // 1バイト } // 合計: 24バイト (実データ10バイト + パディング14バイト) // after type WellAligned struct { b int64 // 8バイト a byte // 1バイト c byte // 1バイト // 6バイトのパディング } // 合計: 16バイト (実データ10バイト + パディング6バイト) fieldalignmentの提案 4 / 19
  2. func optimalOrder(str *types.Struct, sizes *gcSizes) (*types.Struct, []int) { nf :=

    str.NumFields() type elem struct { index int alignof int64 sizeof int64 ptrdata int64 } elems := make([]elem, nf) for i := range nf { field := str.Field(i) ft := field.Type() elems[i] = elem{ i, sizes.Alignof(ft), sizes.Sizeof(ft), sizes.ptrdata(ft), } } fieldalignmentの具体的な実装 6 / 19
  3. sort.Slice(elems, func(i, j int) bool { ei := &elems[i] ej

    := &elems[j] // 1. サイズが0のフィールドを先に配置 // Place zero sized objects before non-zero sized objects. zeroi := ei.sizeof == 0 zeroj := ej.sizeof == 0 if zeroi != zeroj { return zeroi } // 2. アラインメント要件の大きいフィールドを先に配置 // Next, place more tightly aligned objects before less tightly aligned objects. if ei.alignof != ej.alignof { return ei.alignof > ej.alignof } // 3. ポインタを含むフィールドを先に配置 // Place pointerful objects before pointer-free objects. noptrsi := ei.ptrdata == 0 noptrsj := ej.ptrdata == 0 if noptrsi != noptrsj { return noptrsj } 7 / 19
  4. // 4. ポインタを含む場合、末尾の非ポインタ部分が小さいものを先に if !noptrsi { // If both have

    pointers... // ... then place objects with less trailing // non-pointer bytes earlier. That is, place // the field with the most trailing // non-pointer bytes at the end of the // pointerful section. traili := ei.sizeof - ei.ptrdata trailj := ej.sizeof - ej.ptrdata if traili != trailj { return traili < trailj } } // 5. サイズの大きいフィールドを先に配置 // Lastly, order by size. if ei.sizeof != ej.sizeof { return ei.sizeof > ej.sizeof } return false }) fields := make([]*types.Var, nf) indexes := make([]int, nf) for i, e := range elems { fields[i] = str.Field(e.index) indexes[i] = e.index } return types.NewStruct(fields, nil), indexes } 8 / 19
  5. fieldalignmentのgodocにも This analyzer find structs that can be rearranged to

    use less memory, and provides a suggested edit with the most compact order. Note that there are two different diagnostics reported. One checks struct size, and the other reports "pointer bytes" used. Pointer bytes is how many bytes of the object that the garbage collector has to potentially scan for pointers とある。 struct size pointer bytes の2つを元に配置している。 fieldalignmentの配置の原則 10 / 19
  6. 文字列( string ) - 内部的にはポインタと長さの組み合わせ スライス( []T ) - 配列に対するポインタ

    マップ( map[K]V ) チャネル( chan T ) 関数( func() ) インターフェース( interface{} ) 明示的なポインタ ( *T ) ポインタを含むフィールドの例 11 / 19
  7. またfieldalignmentのgodocの続きには以下のようなことがある。 Be aware that the most compact order is not

    always the most efficient. In rare cases it may cause two variables each updated by its own goroutine to occupy the same CPU cache line, inducing a form of memory contention known as "false sharing" that slows down both goroutines. fieldalignmentで配置を変更すると、フォールスシェアリングの可能性がある。 フォールスシェアリング(偽共有)の可能性 12 / 19
  8. 1. 複数のcoreが同じキャッシュラインを共有 main memory a b ..... other variables キャッシュにロード

    core1 の cache a b ..... core2 の cache a b ..... Core1 Core2 フォールスシェアリング(偽共有)とは? 14 / 19
  9. 2. 1つのcoreがキャッシュラインのaを更新 main memory a b ..... other variables core1's

    cache a b ..... 更新 書き込み core2's cache a b ..... Core1 Core2 フォールスシェアリング(偽共有)とは? 15 / 19
  10. 3. 別のcoreがキャッシュラインのbを読み込む この時に同じキャッシュラインを共有しているaが更新されたので、一貫性(キ ャッシュコヒーレンシ)を保つためにメインメモリから再度読み込む main memory a b ..... other

    variables キャッシュライン にロード core1 の cache a b ..... core2 の cache a b ..... 読み込み Core1 Core2 フォールスシェアリング(偽共有)とは? 16 / 19
  11. // フォールスシェアリングを起こす可能性のある構造体 type SharedCounterBad struct { a int64 // ゴルーチンAが更新

    b int64 // ゴルーチンBが更新 } // パディングによる対策 type SharedCounterGood struct { a int64 _ [56]byte // パディング (64バイト - 8バイト) b int64 } フォールスシェアリングの対策 17 / 19
  12. fieldalignment https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignment Big Sky :: Go の struct は小さくできる(fieldalignment のススメ)

    https://mattn.kaoriya.net/software/lang/go/20220127151742.htm "Why Memory Alignment Matters in Go: Making Your Structs Lean and Fast" by Anh Tu Nguyen https://dev.to/tuna99/why-memory-alignment-matters-in-go-making-your- structs-lean-and-fast-1kfk What’s false sharing and how to solve it (using Golang as example) by Genchi Lu https://medium.com/p/whats-false-sharing-and-how-to-solve-it-using-golang- as-example-ef978a305e10?source=social.tw 参考 19 / 19