0
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】ファイルへの書き込み処理をバッファリングに修正してみた

Last updated at Posted at 2024-12-07

概要

DBからレコードを取得してテキストファイルに出力する処理の中でfmt.Fprintfを使用している箇所をbufio.Writer のバッファリングを利用するように修正してみた。

どのくらいパフォーマンスが向上したのかベンチマークテストも行ってみた。

環境

  • MacBook Air M1 メモリ16GB
  • go 1.23.0、PostgreSQL 14.5

計測方法

  • Todosテーブルから10,000件のレコードを取得しテキストファイルに出力する

サンプルコード

fmt.Fprintf使用

func BenchmarkFprintf(b *testing.B) {
	db, _ := dbConn()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		FileOutPutTodosWithFprintf(db, "file_output.txt")
		os.Remove("file_output.txt")
	}
}

func FileOutPutTodosWithFprintf(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
}

bufio.Writer使用

func BenchmarkBufio(b *testing.B) {
	db, _ := dbConn()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		FileOutPutTodosWithBufio(db, "file_output.txt")
		os.Remove("file_output.txt")
	}
}

func FileOutPutTodosWithBufio(db *gorm.DB, fileName string) error {
	file, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer file.Close()

	writer := bufio.NewWriter(file)
	defer writer.Flush()

	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),
		}
		line := fmt.Sprintf("{%s},\n", strings.Join(fields, ", "))
		_, err = writer.Write([]byte(line))
		if err != nil {
			return err
		}
	}

	return nil
}

計測結果

=== RUN   BenchmarkFprintf
BenchmarkFprintf
BenchmarkFprintf-8  31  33787055 ns/op  11289062 B/op  249601 allocs/op

=== RUN   BenchmarkBufio
BenchmarkBufio
BenchmarkBufio-8    61  19502351 ns/op  13212685 B/op  269613 allocs/op

fmt.Fprintf使用(BenchmarkFprintf)

  • 実行回数: 31回
  • 平均実行時間: 33,787,055 ns/op(約33.8ms/1回)
  • メモリ使用量: 11,289,062 B/op(約11.2MB/1回)
  • メモリアロケーション回数: 249,601回

bufio.Writer使用(BenchmarkBufio)

  • 実行回数: 61回
  • 平均実行時間: 19,502,351 ns/op(19.5ms/1回)
  • メモリ使用量: 13,212,685 B/op(約13.2MB/1回)
  • メモリアロケーション回数: 269,613回

比較

fmt.Fprintf使用 bufio.Writer使用 比較
実行回数 31回 61回 約1.9倍 増
平均実行時間 33,787,055 ns/op(約33.8ms/1回) 19,502,351 ns/op(19.5ms/1回) 約42.2% 減
メモリ使用量 11,289,062 B/op(約11.2MB/1回) 13,212,685 B/op(約13.2MB/1回) 約17.0% 増
メモリアロケーション回数 249,601回 269,613回 約8.0% 増

パフォーマンスの差の要因

I/O回数がfmt.Fprintf使用にくらべてbufio.Writer使用の方が少ないことが要因か

  • fmt.Fprintf使用のI/O回数 → 10,000回(ループの回数分)
  • bufio.Writer使用のI/O回数 → 212回(Flushの回数を計測した結果)

結論

  • 今回の計測方法ではbufio.Writerのバッファリング処理の方が実行速度いる結果となった
    • メモリに関してはfmt.Fprintfを使用したほうが若干優れている結果となった
      • バッファリングの場合は内部バッファの追加メモリ確保により、メモリ使用量とアロケーション数が増加した?
  • 処理速度を気にする場面ではバッファリングを使用していきたい
0
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
0
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?