Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

gocon_after_party_osaki.pdf

Avatar for zaiho zaiho
November 22, 2025

 gocon_after_party_osaki.pdf

Avatar for zaiho

zaiho

November 22, 2025
Tweet

Other Decks in Programming

Transcript

  1. 1. パーツの不具合を切り分け a. ❌メモリを交換 b. ❌マザボを交換 c. ❌SSDを交換 d. ❌グラボを交換

    2. 操作の切り分け a. ✅ブラウジング b. ✅ベンチマークツール c. ❌ゲーム 原因の切り分け ベンチマークツールも ブラウジングもできるし、 CPUは怪しくなさそうやな ⾃分
  2. 1. パーツの不具合を切り分け a. ❌メモリを交換 b. ❌マザボを交換 c. ❌SSDを交換 d. ❌グラボを交換

    2. 操作の切り分け a. ✅ブラウジング b. ベンチマークツール c. ❌某FPSゲーム 原因の切り分け ゲームってCPUキャッシュ めっちゃ使うからそれが怪 しいじゃない? 友⼈ ...なるほど?
  3. CPUとメモリ • キャッシュライン ◦ 固定の⼤きさの連続した64バイトのメモリセグメント ◦ 1つの変数ではなく、まとまった単位でメインメモリからフェッチ • データアライメント ◦

    メモリアクセス⾼速化のための、データの割り当て s := make([]int64, 64) for i := range s { s[i] = int64(i + 1) } fmt.Printf("s0: %p (%d)\n", &s[0], uintptr(unsafe.Pointer(&s[0]))) fmt.Printf("s1: %p (%d)\n", &s[1], uintptr(unsafe.Pointer(&s[1]))) fmt.Printf("s2: %p (%d)\n", &s[2], uintptr(unsafe.Pointer(&s[2]))) fmt.Printf("s3: %p (%d)\n", &s[3], uintptr(unsafe.Pointer(&s[3]))) // s0: 0x1400011a000 (1374390689792) // s1: 0x1400011a008 (1374390689800) // s2: 0x1400011a010 (1374390689808) // s3: 0x1400011a018 (1374390689816) s[0] s[1] s[2] s[3]
  4. CPUとメモリ - 寄り道 - • データアライメント ◦ 型によってはパディングが発⽣する type Book

    struct { id int32 enable bool title string author string sealed bool publisher string } // Size: 64 実は末尾に 余裕がある type Book struct { title string author string publisher string id int32 enable bool sealed bool } // Size: 56 type Book struct { title string author string publisher string id int32 enable bool sealed bool bool1 bool bool2 bool } // Size: 56 最適化
  5. 検証⽅針 • 隙間なくキャッシュラインに格納できるスライスを⽤意! ◦ パディングを発⽣させない • 無限ループで参照しまくるように! • いざ参らん!!! package

    main import ( "fmt" "runtime" "sync/atomic" "time" "unsafe" ) type CacheLineData struct { val0 int64 val1 int64 val2 int64 val3 int64 val4 int64 val5 int64 val6 int64 val7 int64 } func printMemStats(label string) { var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Printf("\n=== %s ===\n", label) fmt.Printf("Alloc = %v MiB", m.Alloc/1024/1024) fmt.Printf("\tTotalAlloc = %v MiB", m.TotalAlloc/1024/1024) fmt.Printf("\tSys = %v MiB", m.Sys/1024/1024) fmt.Printf("\tNumGC = %v\n", m.NumGC) } func main() { fmt.Printf("CacheLineData size: %d bytes\n", unsafe.Sizeof(CacheLineData{})) const sliceSize = 512 data := make([]CacheLineData, sliceSize) printMemStats("スライス確保後") // 初期化 for i := range data { data[i].val0 = int64(i) data[i].val1 = int64(i * 2) data[i].val2 = int64(i * 3) data[i].val3 = int64(i * 4) data[i].val4 = int64(i * 5) data[i].val5 = int64(i * 6) data[i].val6 = int64(i * 7) data[i].val7 = int64(i * 8) } printMemStats("初期化後") var iterations atomic.Uint64 ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() fmt.Println("\nL1キャッシュアクセスループ開始 (Ctrl+Cで停止)") go func() { for { <-ticker.C count := iterations.Swap(0) fmt.Printf("Iterations: %d million (avg: %d M/sec)\n", count/1_000_000, count/1_000_000) printMemStats("実行中") } }() var sum int64 for { for i := range data { sum += data[i].val0 sum += data[i].val1 sum += data[i].val2 sum += data[i].val3 sum += data[i].val4 sum += data[i].val5 sum += data[i].val6 sum += data[i].val7 } iterations.Add(1) fmt.Println(sum) } }
  6. まとめ • 学び ◦ メモリの仕組み ▪ まだまだCPUやメモリは不思議がたくさん ◦ メモリを意識したGo ▪

    データアライメント ◦ 計測ツール ▪ pprof ▪ -bench, -gcflags PCが壊れたことがきっかけで普段、意識していない領域に触れられた 今後さらに知⾒を深めることで業務に活かしたい