1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Go】reflectパッケージを使用した処理のベンチマークテスト

Last updated at Posted at 2024-11-30

概要

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は使用する場所を選びたい
  • ストリーム処理 に修正した場合のパフォーマンスが気になるので、そちらもベンチマークテストを行ってみる
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?