go test -bench=BenchmarkSlow -benchmem
| Итераций | Времени, ns/op | Памяти, B/op | Аллокаций ,allocs/op |
|---|---|---|---|
| 88 | 13 505 000 | 18 393 863 | 182 838 |
Запустим BenchmarkSlow с профилированием по cpu и mem
GOGC=off go test -bench=BenchmarkSlow -cpuprofile cpu.out -memprofile mem.out
go tool pprof hw3.test cpu.out
(pprof) list SlowSearch
Нагрузку создают операции со строками:
- 60ms json.Unmarshal
- 40ms, 30ms regexp.MatchString
- 20ms ioutil.ReadAll
- 10ms regexp.MustCompile
- 10ms foundUsers += fmt.Sprintf
- 10ms strings.Split
(pprof) web
- Судя по графу горячие места связаны с регулярными выражениями.
go tool pprof hw3.test mem.out
(pprof) alloc_space
(pprof) list SlowSearch
Замечаем:
- 839.78MB, 538.58MB regexp.MatchString
- 125.35MB ioutil.ReadAll
- 63.32MB strings.Split
- 52.03MB json.Unmarshal
- 21.06MB fmt.Sprintf
(pprof) web
- regexp
- MatchString
- Compile
- syntax Parse
- json Unmarshal
- ioutil ReadAll
(pprof) alloc_objects
(pprof) list SlowSearch
- 7 115 884, 6 435 062 regexp.MatchString("Android", browser)
- 4 090 149 json.Unmarshal
- 196 614 user := make(map[string]interface{})
- 114 688 email := r.ReplaceAllString(user["email"].(string), " [at] ")
- 78 501 foundUsers += fmt.Sprintf("[%d] %s <%s>\n", i, user["name"], email)
- 3 277 r := regexp.MustCompile("@")
- 1 714 ioutil.ReadAll(file)
(pprof) web
- regexp
- MatchString
- Compile
- syntax Parse
- json Unmarshal
Циклично пользуемся:
GOGC=off go test -bench . -benchmem -cpuprofile cpu.out -memprofile mem.out
go tool pprof -http=:8083 hw3.test cpu.out
go tool pprof hw3.test cpu.out
последовательно исправляя горячие места:
json.Unmarshal
Заменим на easyjson
regexp.MatchString
- Заменим на strings.Contains
- Объединим 2 цикла for по browsers в 1
ioutil.ReadAll
ioutil.ReadAll и lines заменим на bufio.NewScanner и построчный Scan()
foundUsers +=
- заменим на strings.Builder.WriteString()
- пишем сразу в вывод
seenBrowsers := []string{}
т.к нам нужно только число уникальных браузеров используем set map[string]struct{}
избавляемся от преобразования []byte в []string т.к оно избыточно для easyjson
regexp.MustCompile("@")
замена на email = [2]string(strings.Split(user.Email, "@"))
не парсим ненужные поля в json
Поправив горячие места получаем:
| Итераций | Времени, ns/op | Памяти, B/op | Аллокаций ,allocs/op | |
|---|---|---|---|---|
| BenchmarkSlow-10 | 86 | 12 431 961 | 18 042 795 | 182 723 |
| BenchmarkFast-10 | 1208 | 910 658 | 543 401 | 7083 |