mcpField
G
go-performance

go-performance

github.com/cxuu開発1861.3k 回インストール19 週間前に更新
リポジトリ

go-performance について

Go のホットパスを最適化する開発者向けのスキルです。メモリー割り当てを意識した実装、文字列処理、ベンチマークを扱い、変更の効果を測定してから採用する方針を示します。

主な機能

  1. 01strconv による基本型変換
  2. 02文字列からバイト列への反復変換の回避
  3. 03マップとスライスの容量指定
  4. 04文字列連結の選択指針
  5. 05ベンチマークとプロファイリングの指針

活用シーン

  1. 01ホットパスのメモリー割り当て削減
  2. 02文字列変換と構築の最適化
  3. 03ベンチマークによる性能変化の測定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/cxuu/golang-skills --skill go-performance

インストール後、Claude Code で/go-performanceを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
BENCHMARKS.md
STRING-OPTIMIZATION.md
scripts/
bench-compare.sh

詳細

Go のパフォーマンスパターン

リソースの振り分け

  • scripts/bench-compare.sh - ベンチマーク結果の比較、ベースラインの保存、または JSON ベンチマークメタデータの生成を行う場合に実行します。
  • references/BENCHMARKS.md - ベンチマークの作成、benchstat の使用、または pprof によるプロファイリングを行う場合に読みます。
  • references/STRING-OPTIMIZATION.md - 文字列変換、連結、またはバイト列と文字列の境界を最適化する場合に読みます。

パフォーマンスに関するガイドラインは、ホットパスにのみ適用してください。時期尚早な最適化は避け、最も効果のある箇所でこれらのパターンに注目してください。


fmt より strconv を優先する

プリミティブ型を文字列に変換する場合、または文字列から変換する場合は、strconv のほうが fmt より高速です。

s := strconv.Itoa(rand.Int()) // ~2x faster than fmt.Sprint()
方法速度アロケーション
fmt.Sprint143 ns/op2 allocs/op
strconv.Itoa64.2 ns/op1 allocs/op

文字列からバイト列への変換の繰り返しを避ける

固定文字列はループの外で一度だけ []byte に変換します。

data := []byte("Hello world")
for b.Loop() { // Go 1.24+; use b.N loops only for older Go
    w.Write(data) // ~7x faster than []byte("...") each iteration
}

コンテナの容量を指定する

可能な場合はコンテナの容量を指定して、メモリをあらかじめ割り当てます。これにより、要素の追加に伴うコピーやサイズ変更による後続のアロケーションを最小限に抑えられます。

map の容量ヒント

make() で map を初期化する際は、容量ヒントを指定します。

m := make(map[string]os.DirEntry, len(files))

注: スライスとは異なり、map の容量ヒントは事前割り当てを完全に保証するものではなく、必要なハッシュマップのバケット数を概算します。

スライスの容量

make() でスライスを初期化する際は、特に追加操作を行う場合に容量ヒントを指定します。

data := make([]int, 0, size)

map とは異なり、スライスの容量はヒントではありません。コンパイラーは指定された量のメモリを正確に割り当てます。容量に達するまでは、後続の append() 操作でアロケーションは発生しません。

方法時間(1 億回の反復)
容量指定なし2.48s
容量指定あり0.21s

append 中の再割り当てが発生しないため、容量指定版は約 12 倍高速です。


値渡しにする

数バイトを節約するためだけに、関数の引数としてポインターを渡さないでください。関数内で引数 x を一貫して *x としてのみ参照する場合、その引数はポインターにすべきではありません。

func process(s string) { // not *string — strings are small fixed-size headers
    fmt.Println(s)
}

値渡しでよく使われる型: string、io.Reader、小さな構造体。

例外:

  • コピーのコストが高い大きな構造体
  • 将来的に大きくなる可能性がある小さな構造体

文字列の連結

複雑さに応じて適切な方法を選びます。

方法最適な用途
+文字列が少数の場合の単純な連結
fmt.Sprintf異なる型を含む書式付き出力
strings.Builderループ内での構築や、少しずつ組み立てる場合
strings.Joinスライスの結合
バッククォートリテラル複数行の定数テキスト

ベンチマークとプロファイリング

最適化の前後で必ず測定してください。Go に組み込まれているベンチマークフレームワークとプロファイリングツールを使用します。

go test -bench=. -benchmem -count=10 ./...

検証: 最適化を適用した後、bash scripts/bench-compare.sh を実行して実際の効果を測定してください。測定可能な改善がある最適化のみを残してください。


クイックリファレンス

パターン悪い例良い例改善
int から文字列への変換fmt.Sprint(n)strconv.Itoa(n)約 2 倍高速
[]byte の繰り返し変換ループ内の []byte("str")ループの外で一度だけ変換約 7 倍高速
map の初期化make(map[K]V)make(map[K]V, size)アロケーションの削減
スライスの初期化make([]T, 0)make([]T, 0, cap)約 12 倍高速
小さな固定サイズの引数*string、*io.Readerstring、io.Reader間接参照なし
単純な文字列の結合s1 + " " + s2(すでに適切)文字列が少数なら + を使用
ループ内での文字列構築+= の繰り返しstrings.BuilderO(n²) に対して O(n)

関連スキル

  • データ構造:スライス、マップ、配列のどれを選ぶか、またはメモリ割り当てのセマンティクスを理解するには、go-data-structures を参照してください
  • 宣言パターン:容量のヒントを指定して make を使用する場合や、マップとスライスを初期化する場合は、go-declarations を参照してください
  • 並行処理:ゴルーチン間で作業を並列化する場合や、バッファーを再利用するために sync.Pool を使用する場合は、go-concurrency を参照してください
  • スタイルの原則:最適化に伴う可読性の低下が許容できるかどうかを判断する場合は、go-style-core を参照してください

コメント