Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
reports/*
result.json
11 changes: 11 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
ruby '3.3.4'

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }


gem 'pry'
gem 'rspec-benchmark'
gem 'ruby-progressbar'
gem 'ruby-prof'
gem 'stackprof'
50 changes: 50 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
GEM
remote: https://rubygems.org/
specs:
benchmark-malloc (0.2.0)
benchmark-perf (0.6.0)
benchmark-trend (0.4.0)
coderay (1.1.3)
diff-lcs (1.5.1)
method_source (1.1.0)
pry (0.15.2)
coderay (~> 1.1)
method_source (~> 1.0)
rspec (3.13.0)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-benchmark (0.6.0)
benchmark-malloc (~> 0.2)
benchmark-perf (~> 0.6)
benchmark-trend (~> 0.4)
rspec (>= 3.0)
rspec-core (3.13.2)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.2)
ruby-prof (1.7.1)
ruby-progressbar (1.13.0)
stackprof (0.2.27)

PLATFORMS
arm64-darwin-23
ruby

DEPENDENCIES
pry
rspec-benchmark
ruby-prof
ruby-progressbar
stackprof

RUBY VERSION
ruby 3.3.4p94

BUNDLED WITH
2.5.23
47 changes: 47 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
run:
ruby bin/runner

unzip:
gzip -dk fixtures/data_large.txt.gz

prepare_data:
head -n 1000 fixtures/data_large.txt > fixtures/data1000.txt
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

лайк за makefile!

head -n 2000 fixtures/data_large.txt > fixtures/data2000.txt
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixtures лучше бы в gitignore добавить, чтобы не было 300к строк в PRе

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

head -n 4000 fixtures/data_large.txt > fixtures/data4000.txt
head -n 8000 fixtures/data_large.txt > fixtures/data8000.txt
head -n 100000 fixtures/data_large.txt > fixtures/data100000.txt

test:
ENVIRONMENT=test ruby tests/task-1_test.rb

perform_test:
ENVIRONMENT=test rspec tests/task_perform_spec.rb

all_tests:
make test
make perform_test

simple_benchmark:
ENVIRONMENT=test ruby lib/benchmarks/simple_benchmarks.rb

simple_benchmark_gb_dis:
ruby lib/benchmarks/simple_benchmarks.rb true

benchmarks_ips:
ruby lib/benchmarks/benchmarks_ips.rb

benchmarks_ips_gb_dis:
ruby lib/benchmarks/benchmarks_ips.rb true

report:
ruby lib/reporters/prof_reporter.rb $(T)

all_reports:
make report T='flat'
make report T='graph'
make report T='callstack'
make report T='callgrind'
make report T='stack-prof-cli'
make report T='stack-prof-json'

.PHONY: test
5 changes: 5 additions & 0 deletions bin/runner
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env ruby

require_relative "../lib/task-1"

work('fixtures/data.txt')
97 changes: 83 additions & 14 deletions case-study-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,114 @@
Я решил исправить эту проблему, оптимизировав эту программу.

## Формирование метрики
Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: *тут ваша метрика*
Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику:
- обрабатывать 200_000 строк не больше чем за 1,5 сек, если я уложусь в это время, то обработка всего файла уложится в бюджет
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

в таких кейсах хорошо бы добавить чуть логики и арифметики - каким соотношением руководствуемся, какие числа и как получаем такой результат

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Наверное как то так?
Целевая метрика - мы хотим что бы полной отчет формировался(3250940) не более чем за 30 сек?


Асимптотика перед рефакторингом:
``` bash
"Processing time from file 1000 rows: 0.0177"
"Processing time from file 2000 rows: 0.0504"
"Processing time from file 4000 rows: 0.1516"
"Processing time from file 8000 rows: 0.504"
```

Асимптотика после первой итерации рефаторинга(формирование и группировка сессий по user_id):

```bash
"Processing time from file 1000 rows: 0.0078"
"Processing time from file 2000 rows: 0.0185"
"Processing time from file 4000 rows: 0.0358"
"Processing time from file 8000 rows: 0.0685"
```

Асимптотика после второй итерации(создание пользователей сразу при обходе строк)

```bash
"Processing time from file 1000 rows: 0.0084"
"Processing time from file 2000 rows: 0.0176"
"Processing time from file 4000 rows: 0.0372"
"Processing time from file 8000 rows: 0.0707"
```

Асимптотика после третьей итерации(оптимизация `collect_stat_from user`)

```bash
"Processing time from file 1000 rows: 0.0102"
"Processing time from file 2000 rows: 0.0234"
"Processing time from file 4000 rows: 0.0592"
"Processing time from file 8000 rows: 0.0849"
"Processing time from file 100000 rows: 1.0904"
"Processing time from file 1000000 rows: 41.2784" // полный отчет
```

Асимптотика финальная - видно что она почти линейная
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ну она в принципе после первой итерации почти сразу и превращается в линейную

```bash
"Processing time from file 1000 rows: 0.007"
"Processing time from file 2000 rows: 0.012"
"Processing time from file 4000 rows: 0.0232"
"Processing time from file 8000 rows: 0.0452"
"Processing time from file 100000 rows: 0.5962"
"Processing time from file 1000000 rows: 29.8777"
```

## Гарантия корректности работы оптимизированной программы
Программа поставлялась с тестом. Выполнение этого теста в фидбек-лупе позволяет не допустить изменения логики программы при оптимизации.

## Feedback-Loop
Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный `feedback-loop`, который позволил мне получать обратную связь по эффективности сделанных изменений за *время, которое у вас получилось*
Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный `feedback-loop`, который позволил мне получать обратную связь по эффективности сделанных изменений за 5 минут(внес изменеия -> запустил бенчмарк и профилировщик-> посмотрел результат)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


Вот как я построил `feedback_loop`: *как вы построили feedback_loop*
Вот как я построил `feedback_loop`:
1. Создал Makefile с короткими алиасами для быстрого запуска тестов отчетов и бенчмарков
2. Запуск бенчмарков и профилировщика
3. Поиск точки роста
4. Внисение изменений
5. Проверка гипотизы
6. На каждой тиерации увеличивать данные на вход

## Вникаем в детали системы, чтобы найти главные точки роста
Для того, чтобы найти "точки роста" для оптимизации я воспользовался *инструментами, которыми вы воспользовались*

Вот какие проблемы удалось найти и решить

### Ваша находка №1
- какой отчёт показал главную точку роста
- как вы решили её оптимизировать
- как изменилась метрика
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста?
1. Отчет graph html показал что главная точка роста является вызов `Array#select` программа проводит там 24% времени
2. Недостаток текущей реализации был в том что программа на каждом пользователи фильтровала массив обектов сессий
Решение - группировать сессии пользователя по `user_id` при парсинге файла и потом достовать их уже по ключу
3. После этого при входящих данных в 8000 строк время работы уменьшилось с 0.504 до 0.0685, то есть программа ускорилась ~7 раз
4. После этого исправленная проблемма перестала быть точкой роста


### Ваша находка №2
1. Отчет callstack.htm показал что теперь основное время программа находится в `Array#each` вызовы которой происходит в нескольких местах: обход строк файла, обход пользователей для формирования объектов пользователей. Так как обход строк файла нам необходим
2. Решил оптимизировать обход объектов пользователей - не делать повторный обход, а создавть пользователя сразу при обходе строк
3. Прирост метрики по бенчмаркам был незначительный 5%
4. По отчету точка роста изменилась - теперь программа большую часть времени стала проводить в методе `collect_stats_from_users` 34%
Но я приблизился к бюджету - после оптимизации этой точки роста сборка отчета всех данных занимает 44 сек.

### Ваша находка №3
1. Отчет callstack.htm показал что теперь основное время программа находится в `collect_stats_from_users`
2. решил повторно не обходить массив обектов пользователя, а собирать по ним данные сразу во время парсинга
3. Время полног отчета хоть и уменьшилось до 41 сек, но большого прироста я не получил, по этому сделал откат назат
- какой отчёт показал главную точку роста
- как вы решили её оптимизировать
- как изменилась метрика
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста?

### Ваша находка №X
- какой отчёт показал главную точку роста
- как вы решили её оптимизировать
- как изменилась метрика
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста?
### Ваша находка №4
1. Еще раз сделал отчет callstack.htm с выключенным gc - отчет показа что есть точка рост в методе который собирает сессии пользователей, а также на инкрементирование прогресс бара
2. Оптимизация заключалась в следующем - добавлять сессии сразу в обект пользователя при обходе строк файла и отключить прогресс-бар при бенчмарках
3. Получил ощютимый прирост - формирование полного за 29.8777 сек.
4. После этого исправленная проблемма перестала быть точкой роста и я вышел на целевой бюджет - время формирования отчета за 30 сек


## Результаты
В результате проделанной оптимизации наконец удалось обработать файл с данными.
Удалось улучшить метрику системы с *того, что у вас было в начале, до того, что получилось в конце* и уложиться в заданный бюджет.
Удалось улучшить метрику системы с *9,36 до 1.3274* и уложиться в заданный бюджет(полный отчет формируется за 29.8777).

*Какими ещё результами можете поделиться*
1. Пробовал запускать коллектинг данных по пользователям в несколько тредов(на кажый вызов `collect_stats_from_users(report, users_objects) do |user|` отдельный тред) - в моем случае это привело к регресу производительности с 37 сек. до 60
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

с concurrency ещё будем разбираться в курсе

в целом в Ruby два треда не могут что-то считать одновременно; поэтому если задача именно в том чтобы что-то считать (а не ждать IO), то добавление треда не поможет, а скорее сделает хуже - как и произошло

но здорово, что проверили

2. метод `size` работает гораздо быстрее чем `count` - удалось выйграть на большом колличестве данных 3 сек.

## Защита от регрессии производительности
Для защиты от потери достигнутого прогресса при дальнейших изменениях программы *о performance-тестах, которые вы написали*
Для защиты от потери достигнутого прогресса при дальнейших изменениях программы написал perfomance тест, который запускает метод `work` 10 раз и проверяет на 200_000 строках что он исполняется не более 1,5 сек.

File renamed without changes.
Loading