📝
【Go】reflectパッケージを使用した処理のベンチマークテスト
概要
DBからレコードを取得してテキストファイルに出力する処理の中でreflectパッケージを使っていたが、パフォーマンス的によろしくないという情報が多かったで使わないようにした。
実際にどのくらいパフォーマンスが違うのか気になったのでベンチマークテストを行ってみた。
環境
- MacBook Air M1 メモリ16GB、
- go 1.23.0、PostgreSQL 14.5
計測方法
- Todosテーブルから10,000件のレコードを取得しテキストファイルに出力する
サンプルコード
reflectパッケージ使用
func BenchmarkFileOutPutTodosWithRefrect(b *testing.B) {
db, _ := dbConn()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
FileOutPutTodosWithRefrect(db, "file_output.txt")
os.Remove("file_output.txt")
}
}
func FileOutPutTodosWithRefrect(db *gorm.DB, fileName string) error {
file, err := os.Create(fileName)
if err != nil {
return err
}
defer file.Close()
var todos []Todo
result := db.Find(&todos)
if result.Error != nil {
return result.Error
}
for _, todo := range todos {
val := reflect.ValueOf(todo)
typ := val.Type()
var fields []string
for i := 0; i < typ.NumField(); i++ {
key := typ.Field(i).Name
value := fmt.Sprintf("%v", val.Field(i).Interface())
fields = append(fields, fmt.Sprintf("%v: %v", key, value))
}
_, err := fmt.Fprintf(file, "{%s},\n", strings.Join(fields, ", "))
if err != nil {
return err
}
}
return nil
}
reflectパッケージ不使用
func BenchmarkFileOutPutTodos(b *testing.B) {
db, _ := dbConn()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
FileOutPutTodos(db, "file_output.txt")
os.Remove("file_output.txt")
}
}
func FileOutPutTodos(db *gorm.DB, fileName string) error {
file, err := os.Create(fileName)
if err != nil {
return err
}
defer file.Close()
var todos []Todo
result := db.Find(&todos)
if result.Error != nil {
return result.Error
}
for _, todo := range todos {
fields := []string{
fmt.Sprintf("ID: %v", todo.ID),
fmt.Sprintf("Title: %v", todo.Title),
fmt.Sprintf("Note: %v", todo.Note),
}
_, err := fmt.Fprintf(file, "{%s},\n", strings.Join(fields, ", "))
if err != nil {
return err
}
}
return nil
}
計測結果
=== RUN BenchmarkFileOutPutTodosWithRefrect
BenchmarkFileOutPutTodosWithRefrect
BenchmarkFileOutPutTodosWithRefrect-8 18 63923287 ns/op 24169018 B/op 669901 allocs/op
=== RUN BenchmarkFileOutPutTodos
BenchmarkFileOutPutTodos
BenchmarkFileOutPutTodos-8 34 33159001 ns/op 11290029 B/op 249605 allocs/op
reflectパッケージ使用(BenchmarkFileOutPutTodosWithRefrect)
- 実行回数: 18回
- 平均実行時間: 63,923,287 ns/op(約63.9ms/1回)
- メモリ使用量: 24,169,018 B/op(約24MB/1回)
- メモリアロケーション回数: 669,901回
reflectパッケージ不使用(BenchmarkFileOutPutTodos)
- 実行回数: 34回
- 平均実行時間: 33,159,001 ns/op(33.5ms/1回)
- メモリ使用量: 11,290,029 B/op(約11MB/1回)
- メモリアロケーション回数: 249,605回
比較
reflect使用 | reflect不使用 | 比較 | |
---|---|---|---|
実行回数 | 18回 | 34回 | 約1.9倍 増 |
平均実行時間 | 63,923,287 ns/op(約63.9ms/1回) | 33,159,001 ns/op(33.5ms/1回) | 約48.1% 減 |
メモリ使用量 | 24,169,018 B/op(約24MB/1回) | 11,290,029 B/op(約11MB/1回 | 約53.3% 減 |
メモリアロケーション回数 | 669,901回 | 249,605回 | 約62.7% 減 |
結論
- 今回の計測方法ではreflect使用しない方が実行速度、メモリ効率共に優れている結果となった
- reflectは使用する場所を選びたい
-
ストリーム処理
に修正した場合のパフォーマンスが気になるので、そちらもベンチマークテストを行ってみる
Discussion