Как оптимизировать загрузчик CSV с помощью pprof? - PullRequest
0 голосов
/ 09 октября 2018

Я пытаюсь оптимизировать процесс загрузки CSV, который в основном выполняет поиск регулярных выражений в большом файле CSV (+ 4 ГБ - 31033993 записей для моего эксперимента). Мне удалось создать многопроцессорную логику для чтения CSV, но когда я анализируюПрофилирование процессора с использованием pprof Я думаю, что мой поиск по регулярным выражениям не оптимизирован.Не могли бы вы помочь мне улучшить этот код, чтобы он мог намного быстрее читать CSV?

enter image description here

Вот мой код:

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

1 Ответ

0 голосов
/ 09 октября 2018

MatchString ищет любое совпадение в строке. Таким образом, вы можете избавиться от якорей и подстановочных знаков. Подстановочные знаки на обоих концах обычно медленны в механизмах регулярных выражений.

Пример, показывающий это на ходу 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...