go-mysql-driver のアロケーションを調査していて気づいた小ネタ。
--- a/benchmark_test.go +++ b/benchmark_test.go @@ -423,9 +423,9 @@ func BenchmarkReceiveMassiveRows(b *testing.B) { b.Errorf("failed to select: %v", err) return } + var i int + var s sql.RawBytes for rows.Next() { - var i int - var s sql.RawBytes err = rows.Scan(&i, &s) if err != nil { b.Errorf("failed to scan: %v", err)
こう言うふうに、Scanに渡す変数は rows.Next() ループの外で宣言した方がいい。
rows.Scan() に &i
のようにアドレスを渡しているが、Scan() に渡された変数はエスケープしていると判断される。
C言語などではこのアドレスがScan()を呼び出した後に利用されないことをプログラマが保証するのでコンパイラはスタックに変数を宣言できるが、Goではエスケープするならヒープアロケーションしてしまう。
ループの外で宣言することで、ヒープアロケーションをループごとではなく1度だけに限定できる。