開発環境
> go version
go version go1.16.3 linux/amd64
やること
- 容量の大きい CSV ファイル(レコード数 45000)に対して下記処理を行う
- 処理
- CSV 読み込み
- 各レコードに対して、空文字判定と重複判定
- CSV 書き込み
- 処理
- 読み込みと書き込みにおける、レコードの順序は整合性は取らない
CSV ファイル
raw,roman
頭金,atamakin
大砲,taihou
溢れでる,ahurederu
検閲官,kenetukan
吃逆,syakkuri
.
.
.
基本形
package main
import (
"encoding/csv"
"io"
"log"
"os"
"time"
)
func processing(lines [][]string) [][]string {
var r [][]string
set := make(map[string]struct{})
for _, line := range lines {
if line[1] == "" {
continue
}
if _, exist := set[line[0]]; exist {
continue
}
set[line[0]] = struct{}{}
r = append(r, line)
}
return r
}
func main() {
// [start] measure time
start := time.Now()
// read csv
inputName := "debug_roman.csv"
file, err := os.Open(inputName)
if err != nil {
log.Fatal(err)
}
defer file.Close()
reader := csv.NewReader(file)
isHeader := true
var header []string
var lines [][]string
for {
line, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
if isHeader {
header = line
isHeader = false
continue
}
lines = append(lines, line)
}
outputLines := processing(lines)
// write csv
outputName := "output_" + inputName
file, err = os.OpenFile(outputName, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
log.Fatal(err)
}
writer := csv.NewWriter(file)
writer.Write(header)
writer.WriteAll(outputLines)
// [end] measure time
log.Printf("elapsed: %fs", time.Since(start).Seconds())
}
> go run main.go
2021/05/15 14:19:24 elapsed: 0.079805s
goroutine で処理を並列実行
package main
import (
"encoding/csv"
"io"
"log"
"os"
"time"
)
func processing(lines [][]string, w *csv.Writer) {
var r [][]string
set := make(map[string]struct{})
for _, line := range lines {
if line[1] == "" {
continue
}
if _, exist := set[line[0]]; exist {
continue
}
set[line[0]] = struct{}{}
r = append(r, line)
}
w.WriteAll(r)
}
func main() {
// [start] measure time
start := time.Now()
// 一つの goroutine で処理するレコード数
v := 1000
// read csv
inputName := "debug_roman.csv"
file, err := os.Open(inputName)
if err != nil {
log.Fatal(err)
}
defer file.Close()
reader := csv.NewReader(file)
header, err := reader.Read()
if err != nil {
log.Fatal(err)
}
// write csv
outputName := "output_" + inputName
file, err = os.OpenFile(outputName, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
log.Fatal(err)
}
writer := csv.NewWriter(file)
writer.Write(header)
isEOF := false
for {
var lines [][]string
for i := 0; i < v; i++ {
line, err := reader.Read()
if err == io.EOF {
isEOF = true
break
}
if err != nil {
log.Fatal(err)
}
lines = append(lines, line)
}
go processing(lines, writer)
if isEOF {
break
}
}
// [end] measure time
log.Printf("elapsed: %fs", time.Since(start).Seconds())
}
> go run main.go
2021/05/15 14:47:41 elapsed: 0.031551s
0.079805s
から 0.031551s
と実行速度は2倍程度に高速化された。
所感
- 雑に goroutine で処理を行ったけどうまくいった
- goroutine の数についての記事が結構ヒットしたので、数は管理する必要がありそう(今回はたまたまうまくいっただけ)
- 変数名に迷いを感じた