関連記事
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。忘れないうちにメモメモ。。。
csv.Reader.ReuseRecord という設定値があることを最近知りました。こんな設定値あったのですね。どうもずっと昔からあったみたい。
名前の通り、内部処理で行データを設定するレコードを再利用するようになる設定です。
ループごとに行データを処理する実装であるなら、再利用されてても問題ないですね。
メモリ消費量とパフォーマンスも良くなるみたい。
以下、抜粋。
// ReuseRecord controls whether calls to Read may return a slice sharing // the backing array of the previous call's returned slice for performance. // By default, each call to Read returns newly allocated memory owned by the caller. ReuseRecord bool
ReuseRecordは、Readの呼び出しが、パフォーマンスのために、前の呼び出しで返されたスライスのバッキング配列を共有するスライスを返すかどうかを制御します。デフォルトでは、Readの各呼び出しは、呼び出し元が所有する新しく割り当てられたメモリを返します。
デフォルトは false です。
試してみた
ベンチマーク取ってみることにしました。利用するCSVファイルは kenallのUTF-8版 (1行15列構成で124436行)です。
package main
import (
"bufio"
"encoding/csv"
"errors"
"io"
"os"
"testing"
)
const (
FilePath = "utf_ken_all.csv"
FieldCount = 15
)
func Benchmark_Csv_ReuseRecord(b *testing.B) {
for range b.N {
readCsv(true)
}
}
func Benchmark_Csv_No_ReuseRecord(b *testing.B) {
for range b.N {
readCsv(false)
}
}
func readCsv(reuse bool) error {
var (
file *os.File
err error
)
if file, err = os.Open(FilePath); err != nil {
return err
}
defer file.Close()
var (
bufR = bufio.NewReader(file)
csvR = csv.NewReader(bufR)
)
csvR.ReuseRecord = reuse
csvR.FieldsPerRecord = FieldCount
var (
rec []string
)
for {
if rec, err = csvR.Read(); errors.Is(err, io.EOF) {
break
}
if err != nil {
return err
}
for _, field := range rec {
io.Discard.Write([]byte(field))
}
}
return nil
}
Taskfile.yml
# https://taskfile.dev version: '3' vars: KEN_ALL_FILE: utf_ken_all.csv KEN_ALL_ZIP: utf_ken_all.zip KEN_ALL_URL: https://www.post.japanpost.jp/zipcode/dl/utf/zip/{{.KEN_ALL_ZIP}} tasks: default: cmds: - task: benchmark ken-all: cmds: - wget {{.KEN_ALL_URL}} - unzip {{.KEN_ALL_ZIP}} benchmark: cmds: - go test -bench=. -run='^$' -benchmem -count 1 -benchtime 10s
実行
$ task ken-all benchmark task: [ken-all] wget https://www.post.japanpost.jp/zipcode/dl/utf/zip/utf_ken_all.zip --2024-12-13 05:15:38-- https://www.post.japanpost.jp/zipcode/dl/utf/zip/utf_ken_all.zip Resolving www.post.japanpost.jp (www.post.japanpost.jp)... 43.253.212.151 Connecting to www.post.japanpost.jp (www.post.japanpost.jp)|43.253.212.151|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 2031933 (1.9M) [application/zip] Saving to: ‘utf_ken_all.zip’ utf_ken_all.zip 100%[===========================================================================================================================>] 1.94M 3.61MB/s in 0.5s 2024-12-13 05:15:40 (3.61 MB/s) - ‘utf_ken_all.zip’ saved [2031933/2031933] task: [ken-all] unzip utf_ken_all.zip Archive: utf_ken_all.zip inflating: utf_ken_all.csv task: [benchmark] go test -bench=. -run='^$' -benchmem -count 1 -benchtime 10s goos: linux goarch: amd64 pkg: github.com/devlights/try-golang/examples/singleapp/csv_reuse_record cpu: AMD EPYC 7B13 Benchmark_Csv_ReuseRecord-16 100 120650208 ns/op 39519468 B/op 1991002 allocs/op Benchmark_Csv_No_ReuseRecord-16 78 140634474 ns/op 69383930 B/op 2115437 allocs/op PASS ok github.com/devlights/try-golang/examples/singleapp/csv_reuse_record 23.311s
確かにメモリ消費もパフォーマンスもReuseRecordを有効にした方が若干速くなりますね。
サンプルは以下にアップしてあります。
参考情報
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。