Merge branch 'pprof-allocs' into 'master'

Add allocs task

See merge request slon/shad-go-private!33
This commit is contained in:
Fedor Korotkiy 2020-09-07 03:45:05 +00:00
commit 8aff7885fa
4 changed files with 148 additions and 0 deletions

31
allocs/README.md Normal file
View file

@ -0,0 +1,31 @@
# allocs
`Counter` используется для нахождения уникальных слов и подсчета вхождений каждого из них.
Его интерфейс выглядит так:
* `Count(r io.Reader) error` — функция, которая подсчитывает количество вхождений для каждого слова в тексте.
На вход подается io.Reader, в котором находится некоторый текст.
Разделителями являются только переносы строк и пробелы.
* `String() string` — преобразует мапу вида `{"слово": "количество вхождений"}` в форматировнную строку.
Необходимо написать имплементацию `EnhancedCounter` (см. файл `allocs.go`)
и снизить количество аллокаций. Бейзлайн можно найти в `baseline.go`.
Значения бенчмарков для бейзлайна:
```
goos: linux
goarch: amd64
pkg: gitlab.com/slon/shad-go/allocs
Benchmark/count-4 73200 16294 ns/op 880 B/op 5 allocs/op
Benchmark/main-4 40485 30113 ns/op 1034 B/op 9 allocs/op
```
Значения бенчмарков для авторского решения:
```goos: linux
goarch: amd64
pkg: gitlab.com/slon/shad-go/allocs
Benchmark/count-4 212850 5471 ns/op 4144 B/op 2 allocs/op
Benchmark/main-4 143937 8247 ns/op 4176 B/op 3 allocs/op
```

9
allocs/allocs.go Normal file
View file

@ -0,0 +1,9 @@
// +build !solution
package allocs
// implement your Counter below
func NewEnhancedCounter() Counter {
return NewBaselineCounter()
}

52
allocs/allocs_test.go Normal file
View file

@ -0,0 +1,52 @@
package allocs
import (
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestCounter_Count(t *testing.T) {
repeats := 10
data := strings.Repeat("a b c\n", repeats)
data = data[:len(data)-1]
r := strings.NewReader(data)
c := NewEnhancedCounter()
err := c.Count(r)
require.NoError(t, err)
expected := fmt.Sprintf("word 'a' has %d occurrences\n", repeats)
expected += fmt.Sprintf("word 'b' has %d occurrences\n", repeats)
expected += fmt.Sprintf("word 'c' has %d occurrences\n", repeats)
actual := c.String()
require.Equal(t, expected, actual)
}
func Benchmark(b *testing.B) {
repeats := 10000
data := strings.Repeat("a b c d e f g h i j k l m n o p q r s t u v w x y z\n", repeats)
data = data[:len(data)-1]
b.Run("count", func(b *testing.B) {
r := strings.NewReader(data)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
c := NewEnhancedCounter()
_ = c.Count(r)
}
})
b.Run("main", func(b *testing.B) {
r := strings.NewReader(data)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
c := NewEnhancedCounter()
_ = c.Count(r)
_ = c.String()
}
})
}

56
allocs/baseline.go Normal file
View file

@ -0,0 +1,56 @@
// +build !solution
// +build !change
package allocs
import (
"fmt"
"io"
"io/ioutil"
"sort"
"strings"
)
type Counter interface {
Count(r io.Reader) error
String() string
}
type BaselineCounter struct {
counts map[string]int
}
func NewBaselineCounter() Counter {
return BaselineCounter{counts: map[string]int{}}
}
func (c BaselineCounter) Count(r io.Reader) error {
data, err := ioutil.ReadAll(r)
if err != nil {
return err
}
dataStr := string(data)
for _, line := range strings.Split(dataStr, "\n") {
for _, word := range strings.Split(line, " ") {
c.counts[word]++
}
}
return nil
}
func (c BaselineCounter) String() string {
keys := make([]string, 0, 0)
for word := range c.counts {
keys = append(keys, word)
}
sort.Slice(keys, func(i, j int) bool {
return keys[i] < keys[j]
})
result := ""
for _, key := range keys {
line := fmt.Sprintf("word '%s' has %d occurrences\n", key, c.counts[key])
result += line
}
return result
}