-
Notifications
You must be signed in to change notification settings - Fork 180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Yaroslavzev] Homework #140
base: master
Are you sure you want to change the base?
Changes from all commits
284129a
36b5ac6
1aa4b2d
453ba8f
e328c73
0880b61
ba84cb4
9117ba9
0c3d54f
e520c49
a97bec2
be1f2f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
GEM | ||
remote: https://rubygems.org/ | ||
specs: | ||
ast (2.4.2) | ||
benchmark-ips (2.13.0) | ||
benchmark-malloc (0.2.0) | ||
benchmark-perf (0.6.0) | ||
benchmark-trend (0.4.0) | ||
coderay (1.1.3) | ||
date (3.3.4) | ||
diff-lcs (1.5.1) | ||
fasterer (0.11.0) | ||
ruby_parser (>= 3.19.1) | ||
json (2.7.2) | ||
kalibera (0.1.2) | ||
memoist (~> 0.16) | ||
rbzip2 (~> 0.3) | ||
language_server-protocol (3.17.0.3) | ||
memoist (0.16.2) | ||
method_source (1.1.0) | ||
minitest (5.22.3) | ||
parallel (1.24.0) | ||
parser (3.3.1.0) | ||
ast (~> 2.4.1) | ||
racc | ||
pry (0.14.2) | ||
coderay (~> 1.1) | ||
method_source (~> 1.0) | ||
racc (1.7.3) | ||
rainbow (3.1.1) | ||
rbzip2 (0.3.0) | ||
regexp_parser (2.9.0) | ||
rexml (3.2.6) | ||
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.0) | ||
rspec-support (~> 3.13.0) | ||
rspec-expectations (3.13.0) | ||
diff-lcs (>= 1.2.0, < 2.0) | ||
rspec-support (~> 3.13.0) | ||
rspec-mocks (3.13.0) | ||
diff-lcs (>= 1.2.0, < 2.0) | ||
rspec-support (~> 3.13.0) | ||
rspec-support (3.13.1) | ||
rubocop (1.63.4) | ||
json (~> 2.3) | ||
language_server-protocol (>= 3.17.0) | ||
parallel (~> 1.10) | ||
parser (>= 3.3.0.2) | ||
rainbow (>= 2.2.2, < 4.0) | ||
regexp_parser (>= 1.8, < 3.0) | ||
rexml (>= 3.2.5, < 4.0) | ||
rubocop-ast (>= 1.31.1, < 2.0) | ||
ruby-progressbar (~> 1.7) | ||
unicode-display_width (>= 2.4.0, < 3.0) | ||
rubocop-ast (1.31.2) | ||
parser (>= 3.3.0.4) | ||
rubocop-performance (1.21.0) | ||
rubocop (>= 1.48.1, < 2.0) | ||
rubocop-ast (>= 1.31.1, < 2.0) | ||
ruby-prof (1.7.0) | ||
ruby-progressbar (1.13.0) | ||
ruby_parser (3.21.0) | ||
racc (~> 1.5) | ||
sexp_processor (~> 4.16) | ||
sexp_processor (4.17.1) | ||
stackprof (0.2.26) | ||
unicode-display_width (2.5.0) | ||
|
||
PLATFORMS | ||
arm64-darwin-22 | ||
x86_64-darwin-21 | ||
|
||
DEPENDENCIES | ||
benchmark-ips | ||
date | ||
fasterer | ||
json | ||
kalibera | ||
minitest | ||
pry | ||
rspec-benchmark | ||
rubocop-performance | ||
ruby-prof | ||
stackprof | ||
|
||
RUBY VERSION | ||
ruby 3.2.2p53 | ||
|
||
BUNDLED WITH | ||
2.3.7 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
# Case-study оптимизации | ||
|
||
## Актуальная проблема | ||
|
||
В нашем проекте возникла серьёзная проблема. | ||
|
||
Необходимо было обработать файл с данными, чуть больше ста мегабайт. | ||
|
@@ -12,45 +13,68 @@ | |
Я решил исправить эту проблему, оптимизировав эту программу. | ||
|
||
## Формирование метрики | ||
Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: *тут ваша метрика* | ||
|
||
Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: скорость исполнения программы на 4000 тысячах строк должна укладываться в заданный временной интервал. | ||
|
||
## Гарантия корректности работы оптимизированной программы | ||
|
||
Программа поставлялась с тестом. Выполнение этого теста в фидбек-лупе позволяет не допустить изменения логики программы при оптимизации. | ||
|
||
## Feedback-Loop | ||
Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный `feedback-loop`, который позволил мне получать обратную связь по эффективности сделанных изменений за *время, которое у вас получилось* | ||
|
||
Вот как я построил `feedback_loop`: *как вы построили feedback_loop* | ||
Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный `feedback-loop`, который позволил мне получать обратную связь по эффективности сделанных изменений за _время, которое у вас получилось_ | ||
|
||
Вот как я построил `feedback_loop`: | ||
Изначальный код отметил как начальный и определил перфоманс для 4000 строк | ||
Последующие итерации помещены в версии и для каждой из версий определен прирост производительности на основе тестовю | ||
|
||
## Вникаем в детали системы, чтобы найти главные точки роста | ||
Для того, чтобы найти "точки роста" для оптимизации я воспользовался *инструментами, которыми вы воспользовались* | ||
|
||
Для того, чтобы найти "точки роста" для оптимизации я воспользовался профилированием программы | ||
|
||
Вот какие проблемы удалось найти и решить | ||
|
||
### Ваша находка №1 | ||
- какой отчёт показал главную точку роста | ||
- как вы решили её оптимизировать | ||
- как изменилась метрика | ||
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста? | ||
|
||
- флейм граф показал, что Array#select занимает значительное время исполнения | ||
- Array#select происводит поиск по всему массиву сессий и это занимает много времени. Для ускорения поиска, я изменил структуру оранизации сессий и в реализации V1 сессии сгруппированы по user_id в хеш. Таким образом, используются оптимизированный механизмы поиска ruby и достигается прирост происховодительности | ||
- Скрость выполнения увеличина в 4 раза для 4000 строк | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. тут главное, что асимптотика стала качественно лучше и приблизилась к линейное Потому что фетч из хэша это O(1), а select из массива это O(N) |
||
- исправленная проблема перестала быть точкой роста | ||
|
||
### Ваша находка №2 | ||
- какой отчёт показал главную точку роста | ||
- как вы решили её оптимизировать | ||
- как изменилась метрика | ||
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста? | ||
|
||
### Ваша находка №X | ||
- какой отчёт показал главную точку роста | ||
- как вы решили её оптимизировать | ||
- как изменилась метрика | ||
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста? | ||
- флейм граф показал, что Array#+ занимает значительное время исполнения | ||
- На основе https://github.com/fastruby/fast-ruby?tab=readme-ov-file#arrayconcat-vs-array-code Array#+ можно заменить на Array#concat | ||
- Замена Array#+ на Array#concat дала прирост как минимум 10% на 4000 строк | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 4000 строк это маловато уже хорошая идея взять какую-то часть входных данных, чтобы программа не выполнялась вечность, но хоршо подбирать объём данных так, чтобы программа успевала покрутиться пару секунд. Если она завершается слишком быстро (“не успевает поработать”) могут возникнуть какие-то перекосы (например, на полном объёме основная часть времени тратится в основном цикле, а если данных мало - то большая часть уходит на инициализацию и финализацию, например на чтение из файла и запись потом в файл) И плюс когда время уже на миллисекунды - больше влияние погрешностей. |
||
- исправленная проблема перестала быть точкой роста и следует увеличить количство строк | ||
|
||
### Ваша находка №3 | ||
|
||
- флейм граф показал, что метод String#split и Date#parse занимает значительное время | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. всегда лучше по одному делать, иначе не понятно что из двух изменений как сыграло |
||
- Заменил String#split на String#match и убрал Date#parse | ||
- Указанные изменения повысили производительность на 40% на файле 30 000 строк | ||
|
||
### Ваша находка №4 | ||
|
||
- флейм граф показал, что поиск уникальных браузеров занимает значительное время | ||
- Рефакторинг вычисления сессий как в находке 1 | ||
- Указанные изменения повысили производительность на 50% на файле 30 000 строк | ||
|
||
### Ваша находка №5 | ||
|
||
- общий рефакторинг кода | ||
- Вызов upcase во время парсинга(а не несколько раз), эмуляция метода any? c break и др | ||
- Указанные изменения не повлияли значительно на производительность | ||
|
||
## Результаты | ||
|
||
В результате проделанной оптимизации наконец удалось обработать файл с данными. | ||
Удалось улучшить метрику системы с *того, что у вас было в начале, до того, что получилось в конце* и уложиться в заданный бюджет. | ||
Удалось улучшить метрику системы с того, что нельзя дождать окончания результата обработки, до того, что получилось уложиться в выполнению программы в заданный бюджет 30 секунд. | ||
Измеренный прирост производительности составил 33 раз для обработки файла в 10 000 строк | ||
|
||
Заданный бюджет выполнения программы в 30 секунд был достигнут на M1 | ||
|
||
*Какими ещё результами можете поделиться* | ||
|
||
## Защита от регрессии производительности | ||
Для защиты от потери достигнутого прогресса при дальнейших изменениях программы *о performance-тестах, которые вы написали* | ||
|
||
Для защиты от потери достигнутого прогресса при дальнейших изменениях программы написаны тесты производительности. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
source 'https://rubygems.org' | ||
|
||
ruby '3.2.2' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
gem 'json' | ||
gem 'pry' | ||
gem 'date' | ||
gem 'minitest' | ||
gem 'stackprof' | ||
gem 'ruby-prof' | ||
gem 'fasterer' | ||
gem 'rspec-benchmark' | ||
gem 'kalibera' | ||
gem 'benchmark-ips' | ||
gem 'rubocop-performance' | ||
gem 'fasterer' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'benchmark/ips' | ||
require_relative '../../task-1' | ||
|
||
file_name = './data1000.txt' | ||
|
||
Benchmark.ips do |x| | ||
# The default is :stats => :sd, which doesn't have a configurable confidence | ||
# confidence is 95% by default, so it can be omitted | ||
x.config( | ||
stats: :bootstrap, | ||
confidence: 95 | ||
) | ||
|
||
x.report('slow string concatenation') do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wrong name |
||
work(file_name) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_it was a placeholder_