Goでベンチマーク！パフォーマンス改善のための第一歩

## 概要
Goを書いていると「この処理は遅いのか？速いのか？」と気になることが増えてきます。
そんなとき便利なのが Goの**ベンチマーク機能** です。

今回は基本の使い方＋よくある用途＋便利なオプション に加え、
**Mutex**と**Channel**のパフォーマンス比較 という実務にも役立つ例をご紹介します。

## ベンチマークをやる理由
コードのパフォーマンス改善に取り組むとき、
「体感で遅い」「たぶんこっちが速そう」という思い込みにハマりがちです。

でもそれは危険。

Rob Pike氏（Goの開発者の一人） の有名な言葉があります：

`"Don’t tune for speed until you’ve measured, and even then don’t unless one part of the code overwhelms the rest."
（「推測するな、計測せよ。」）`

つまり 実際に測ってから判断しろ ということです。


## Goのベンチマークの使い方
Goの標準パッケージ testing には、ベンチマーク用の仕組みが組み込まれています。
✅ 特別なツールは不要
✅ 通常の go test コマンドで実行可能

## ベンチマーク用の関数を用意しよう

```sample.go
func BenchmarkXxx(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // 測定したい処理
    }
}
```

:::note info
### 詳細
- 関数名は Benchmark プレフィックス必須
- 引数は *testing.B 型
- b.N 回ループして測定する（Goが自動調整）

※testing.Bの詳細はこちら↓
[benchmark.go](https://github.com/golang/go/blob/master/src/testing/benchmark.go "Qiita Home")
:::

## 具体例：スライスのAppend vs コピー

```samaple.go
package mypackage

import "testing"

func BenchmarkAppend(b *testing.B) {
    for i := 0; i < b.N; i++ {
        s := []int{}
        s = append(s, 1, 2, 3, 4, 5)
    }
}

func BenchmarkCopy(b *testing.B) {
    for i := 0; i < b.N; i++ {
        src := []int{1, 2, 3, 4, 5}
        dst := make([]int, len(src))
        copy(dst, src)
    }
}

```

## ベンチマークの実行方法
```sh
$ go test -bench=.
```

:::note info
### 詳細
- -bench=. → すべてのBenchmark関数を実行
- go test のサブコマンドとして動作
:::

### 結果例
```txt
BenchmarkAppend-10   	10000000	       120 ns/op
BenchmarkCopy-10     	20000000	        80 ns/op
```

:::note info
### 詳細
- 何回ループしたか → 10000000
- 1回あたりの実行時間 → 120ns/op
::::

## 用途例
- アルゴリズムの比較（map vs slice など）
- 圧縮/暗号/シリアライズなどの高速化
- MutexとChannelの比較（ロック vs メッセージパッシング）
- JSONライブラリの比較（標準 vs 高速版）
- I/O 処理のオーバーヘッド確認
👉 ボトルネック特定にも使える

## オプション例
| オプション           | 内容                      |
| --------------- | ----------------------- |
| `-bench=Regexp` | 実行するベンチマークの名前指定         |
| `-benchmem`     | メモリアロケーション数 / メモリ使用量も表示 |
| `-count=N`      | N回繰り返して平均を取る（ノイズ低減）     |

### 例：メモリ消費も確認

```sh
$ go test -bench=. -benchmem
```

出力例：
```txt
BenchmarkAppend-10   	10000000	       120 ns/op	       64 B/op	       1 allocs/op
```
👉 B/op（1回の操作で何バイト確保するか）
👉 allocs/op（何回メモリ割当が発生したか）

## Mutex vs Channel ベンチマーク例
ここが実務で **とても役立つ！** 例です。
同期処理に **sync.Mutex** を使うのと **Channel**を使うのとでは
パフォーマンスに大きな差が出ることが多いです。

ぜひ試してみましょう！

```go
package mypackage

import (
	"sync"
	"testing"
)

func BenchmarkMutex(b *testing.B) {
	var mu sync.Mutex
	var counter int

	for i := 0; i < b.N; i++ {
		mu.Lock()
		counter++
		mu.Unlock()
	}
}

func BenchmarkChannel(b *testing.B) {
	ch := make(chan int, 1)
	ch <- 0

	for i := 0; i < b.N; i++ {
		val := <-ch
		val++
		ch <- val
	}
}
```

## 実行結果例
私の環境で試すと以下の結果になりました。

```txt
BenchmarkMutex-8   	 510459456	        2.255 ns/op
BenchmarkChannel-8 	 68742958	        19.21 ns/op
```

:::note info
#### Mutex
- 約5億回ループ
- 1回あたり 2.255ナノ秒
#### Channel
- 約6800万回ループ
- 1回あたり 19.21ナノ秒
:::

### 結果考察
- 速さに関してはMutexに軍配
- Channelは「goroutine間通信」「柔軟性」「安全性」はあるが、コストが大きい
- 高頻度のロック操作にはMutexのほうが向いてそう

## まとめ
- Goのtestingパッケージはベンチマークが超簡単
- 「推測するな、計測せよ」 → まずは測ってからチューニング
- MutexとChannelでは明確な速度差がある → 用途に応じて選択
- -benchmem や -count オプションを活用しよう

## おまけ：もうひとつの格言
`"First make it correct, then make it fast."`
「まず正しく書け。速さはそのあとだ。」