diff --git a/allocs/README.md b/allocs/README.md new file mode 100644 index 0000000..cb7d78d --- /dev/null +++ b/allocs/README.md @@ -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 +``` + + \ No newline at end of file diff --git a/allocs/allocs.go b/allocs/allocs.go new file mode 100644 index 0000000..71fc8aa --- /dev/null +++ b/allocs/allocs.go @@ -0,0 +1,9 @@ +// +build !solution + +package allocs + +// implement your Counter below + +func NewEnhancedCounter() Counter { + return NewBaselineCounter() +} \ No newline at end of file diff --git a/allocs/allocs_test.go b/allocs/allocs_test.go new file mode 100644 index 0000000..81c4546 --- /dev/null +++ b/allocs/allocs_test.go @@ -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() + } + }) +} diff --git a/allocs/baseline.go b/allocs/baseline.go new file mode 100644 index 0000000..bb18838 --- /dev/null +++ b/allocs/baseline.go @@ -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 +}