regex - 如何使用 pprof 优化 CSV 加载器?
问题描述
我正在尝试优化 CSV 加载过程,该过程基本上是在大型 CSV 文件中进行正则表达式搜索(+4GB - 31033993 条记录用于我的实验)我设法构建了一个多处理逻辑来读取 CSV,但是当我使用分析 CPU 分析时pprof
我认为我的正则表达式搜索没有优化。你能帮我改进这段代码,以便它可以更快地读取 CSV 吗?
到目前为止,这是我的代码:
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"regexp"
"runtime"
"runtime/pprof"
"strings"
"sync"
)
func processFile(path string) [][]string {
file, err := os.Open(path)
if err != nil {
log.Println("Error:", err)
}
var pattern = regexp.MustCompile(`^.*foo.*$`)
numCPU := runtime.NumCPU()
jobs := make(chan string, numCPU+1)
fmt.Printf("Strategy: Parallel, %d Workers ...\n", numCPU)
results := make(chan []string)
wg := new(sync.WaitGroup)
for w := 1; w <= numCPU; w++ {
wg.Add(1)
go parseRecord(jobs, results, wg, pattern)
}
go func() {
scanner := bufio.NewScanner(file)
for scanner.Scan() {
jobs <- scanner.Text()
}
close(jobs)
}()
go func() {
wg.Wait()
close(results)
}()
lines := [][]string{}
for line := range results {
lines = append(lines, line)
}
return lines
}
func parseRecord(jobs <-chan string, results chan<- []string, wg *sync.WaitGroup, pattern *regexp.Regexp) {
defer wg.Done()
for j := range jobs {
if pattern.MatchString(j) {
x := strings.Split(string(j), "\n")
results <- x
}
}
}
func split(r rune) bool {
return r == ','
}
func main() {
f, err := os.Create("perf.data")
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
pathFlag := flag.String("file", "", `The CSV file to operate on.`)
flag.Parse()
lines := processFile(*pathFlag)
fmt.Println("loaded", len(lines), "records")
}
当我在没有任何正则表达式约束的情况下处理文件时,我得到了合理的计算时间(我只是将解析的字符串加载到没有任何 2D 数组中pattern.MatchString()
)
Strategy: Parallel, 8 Workers ...
loaded 31033993 records
2018/10/09 11:46:38 readLines took 30.611246035s
相反,当我使用 Regex 约束运行上述代码时,我得到了以下结果:
Strategy: Parallel, 8 Workers ...
loaded 143090 records
2018/10/09 12:04:32 readLines took 1m24.029830907s
解决方案
MatchString 查找字符串上的任何匹配项因此您可以摆脱锚点和通配符两端的通配符在正则表达式引擎中通常很慢
在 go 1.10 上显示此示例
package reggie
import (
"regexp"
"testing"
)
var pattern = regexp.MustCompile(`^.*foo.*$`)
var pattern2 = regexp.MustCompile(`foo`)
func BenchmarkRegexp(b *testing.B) {
for i := 0; i < b.N; i++ {
pattern.MatchString("youfathairyfoobar")
}
}
func BenchmarkRegexp2(b *testing.B) {
for i := 0; i < b.N; i++ {
pattern2.MatchString("youfathairyfoobar")
}
}
$ go test -bench=.
goos: darwin
goarch: amd64
BenchmarkRegexp-4 3000000 471 ns/op
BenchmarkRegexp2-4 20000000 101 ns/op
PASS
ok _/Users/jsandrew/wip/src/reg 4.031s
推荐阅读
- javascript - TypeError:未定义不是对象(评估“settings.tableClass”)
- ios - 在 Swift 4 中在 ContainerView 中查看
- android - 如何通过okhttps在android studio中上传多个文件只上传最后一个文件
- c# - 在一个可执行文件中绑定两个文件
- bash - 谜题:这个 bash 命令有什么作用?
- node.js - 无法运行lerna的命令
- regex - 如果不是单词的一部分,则 REGEX 查找特定的子字符串
- sharepoint - SharePoint 2013 到 SharePoint 2013 的迁移
- arrays - 在 React 中为每个添加元素增加 ID
- tfs - 如何从 PBI / BUG 详细信息页面中查看构建和发布?