-
Notifications
You must be signed in to change notification settings - Fork 15
Task 2 homework #12
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
Open
ArtsiomMusin
wants to merge
7
commits into
spajic:master
Choose a base branch
from
ArtsiomMusin:task_2_homework
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Task 2 homework #12
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
0cd2cf6
Move and update files from task 1
ArtsiomMusin aaf2ad7
Refactor include? method
ArtsiomMusin 89511c3
Add Oj to speed up json generation
ArtsiomMusin 60972f7
Refactor collect user data
ArtsiomMusin d4e49b7
Add some changes
ArtsiomMusin 4f28ec5
Refactor upcase method
931d607
Update case study
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| data* | ||
| result.json | ||
| tmp/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| source 'https://rubygems.org' | ||
|
|
||
| #ruby '2.6.2' | ||
|
|
||
| gem 'ruby-prof' | ||
| gem 'oj' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| GEM | ||
| remote: https://rubygems.org/ | ||
| specs: | ||
| oj (3.7.11) | ||
| ruby-prof (0.17.0) | ||
|
|
||
| PLATFORMS | ||
| ruby | ||
|
|
||
| DEPENDENCIES | ||
| oj | ||
| ruby-prof | ||
|
|
||
| BUNDLED WITH | ||
| 1.17.2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,30 +1,82 @@ | ||
| ### Note | ||
| *Для работы скрипта требуется Ruby 2.4+* | ||
|
|
||
| # Задание №2 | ||
|
|
||
| ## Описание | ||
| В этом задании надо дооптимизировать задание 1й недели с использованием инструментов профилирования `CPU` и разъяснений, сделанных на 2й лекции. | ||
|
|
||
| ### Что нужно сделать | ||
| - Построить и проанализировать отчёт `ruby-prof` в режиме `Flat`; | ||
| - Построить и проанализировать отчёт `ruby-prof` в режиме `Graph`; | ||
| - Построить и проанализировать отчёт `ruby-prof` в режиме `CallStack`; | ||
| - Построить и проанализировать отчёт `ruby-prof` в режиме `CallTree` c визуализацией в `QCachegrind`; | ||
| - Профилировать работающий процесс `rbspy`; | ||
| - Построить и проанализировать отчёт `flamegraph` с помощью `rbspy`; | ||
| - Добавить в программу `ProgressBar`; | ||
| - Научиться пользоваться `Valgrind massif` с `massif-visualizer`. Построить профиль использования памяти для итоговой версии вашей программы и добавить скриншот в `PR`; | ||
| - Написать хотя бы самый простой тест на производительность: `assert`, что время выполнения скрипта меньше значения, которое чуть больше реального, чтобы не давать ложных срабатываний; | ||
|
|
||
| ### Главное | ||
| Нужно потренироваться методично работать по схеме с фидбек-лупом: | ||
| - построили отчёт каким-то из профилировщиков | ||
| - осознали его | ||
| - поняли, какая самая большая точка роста | ||
| - внесли минимальные изменения, чтобы использовать только эту точку роста | ||
| - перестроили отчёт, убедились, что проблема решена | ||
| - вычислили метрику - оценили, как изменение повлияло на метрику | ||
| - записали полученные результаты | ||
| - закоммитились | ||
| - перешли к следующей итерации | ||
| # Задание №1 | ||
| В файле `task-1.rb` находится ruby-программа, которая выполняет обработку данных из файла. | ||
|
|
||
| В файл встроен тест, который показывает, как программа должна работать. | ||
|
|
||
| С помощью этой программы нужно обработать файл данных `data_large.txt`. | ||
|
|
||
| **Проблема в том, что это происходит слишком долго, дождаться пока никому не удавалось.** | ||
|
|
||
|
|
||
| ## Задача | ||
| - Оптимизировать эту программу, выстроив процесс согласно "общему фреймворку оптимизации" из первой лекции; | ||
| - Профилировать программу с помощью инструментов, с которыми мы познакомились в первой лекции; | ||
| - Добиться того, чтобы программа корректно обработала файл `data_large.txt`; | ||
| - Написать кейс-стади о вашей оптимизации по шаблону `case-study-template.md`. | ||
|
|
||
| ## Сдача задания | ||
| Для сдачи задания нужно сделать `PR` в этот репозиторий. | ||
|
|
||
| В `PR` | ||
| - должны быть внесены оптимизации в `task-1.rb`; | ||
| - должен быть файл `case-study.md` с описанием проделанной оптимизации; | ||
|
|
||
|
|
||
| # Комментарии | ||
|
|
||
| ## Какую пользу нужно получить от этого задания | ||
| Задание моделирует такую ситуацию: вы получили неффективную систему, в которой код и производительность оставляет желать лучшего. При этом актуальной проблемой является именно производительность. | ||
| Вам нужно оптимизировать эту систему. | ||
|
|
||
| С какими искушениями вы сталкиваететь: | ||
| - вы “с ходу” видите проблемы в коде и у вас возникает импульс потратить время на их рефакторинг; | ||
| - вы “с ходу” замечаете какие-то непроизводительные идиомы, и у вас возникает соблазн их сразу исправить; | ||
|
|
||
| Эти искушения типичны и часто возникают в моделируемой ситуации. | ||
|
|
||
| Их риски: | ||
| - перед рефакторингом “очевидных” косяков не написать тестов и незаметно внести регрессию; | ||
| - потратить время на рефакторинг, хотя время было только на оптимизацию; | ||
| - исправить все очевидные на глаз проблемы производительности, не получить заметного результата, решить что наверное просто Ruby слишком медленный для этой задачи | ||
|
|
||
| ## Советы | ||
| - Найдите объём данных, на которых программа отрабатывает достаточно быстро - это позволит вам выстроить фидбек-луп; если улучшите метрику для части данных, то улучшите и для полного объёма данных; | ||
| - Попробуйте прикинуть ассимтотику роста времени работы в зависимости от объёма входных данных (попробуйте объём x, 2x, 4x, 8x) | ||
| - Оцените, как долго программа будет обрабатывать полный обём данных | ||
| - Оцените, сколько времени занимает работа GC | ||
|
|
||
| Возможно, что оптимизаций только памяти вам не хватит, чтобы довести производительность системы до приемлемой. | ||
|
|
||
| Если вы придёте к такому выводу, то возможны такие варианты: | ||
| - попробуйте использовать какой-нибудь из рассмотренных нами профилировщиков в режиме профилирования CPU (wall), найти точки роста и оптимизировать их; | ||
| - подойдёт и вариант, если вы с помощью профилирования найдёте несколько главных точек роста, оптимизируете их, покажете, что дальнейшие оптимизации только памяти не приведут к желаемому результату, и опишете это в case-study. | ||
|
|
||
| ## Что можно делать | ||
| - рефакторить код | ||
| - рефакторить/дописывать тесты | ||
| - разбивать скрипт на несколько файлов | ||
|
|
||
| ## Что нужно делать | ||
| - исследовать предложенную вам на рассмотрение систему | ||
| - построить фидбек-луп, который позволит вам быстро тестировать гипотезы и измерять их эффект | ||
| - применить инструменты профилирования памяти, интроспекции GC, чтобы найти самые горячие проблемы по памяти, оценить кол-во времени которое уходит на сборку мусора | ||
| - выписывать в case-study несколько пунктов: каким профилировщиком вы нашли точку роста, как её оптимизировали, какой получили прирост метрики; | ||
|
|
||
| ## Что не нужно делать | ||
| - переписывать с нуля | ||
| - забивать на выстраивание фидбек-лупа | ||
| - вносить оптимизации по наитию, без профилировщика и без оценки эффективности | ||
|
|
||
| ## Основная польза задания | ||
| Главная польза этого задания - попрактиковаться в применении грамотного подхода к оптимизации, почуствовать этот процесс: | ||
| - как взяли незнакомую систему и исследовали её | ||
| - как выстроили фидбек луп | ||
| - как с помощью профилировщиков нашли что именно даст вам наибольший эффект | ||
| - как быстро протестировали гипотезу, получили измеримый результат и зафиксировали его | ||
| - как в итоге написали небольшой отчёт об успешных шагах этого процесса | ||
|
|
||
| ## PS | ||
| Обсудим с Виталием, возможно уделю время на 2й лекции разбору подобной ситуации, а заданием 2й недели будет дооптимизировать эту программу, уже используя инструментарий оптимизации CPU. | ||
|
|
||
| ## PPS | ||
| Делайте задание, изучайте предложенную систему, смотрите на неё под разными углами с помощью разных инстурментов. Оптимизируйте. Попрактикуйтесь в этой работе |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,40 +7,69 @@ | |
|
|
||
| У нас уже была программа на `ruby`, которая умела делать нужную обработку. | ||
|
|
||
| Она успешно работала на файлах размером пару мегабайт, но для большого файла она работала слишком долго, и не было понятно, закончит ли она вообще работу за какое-то разумное время. | ||
|
|
||
| Я решил исправить эту проблему, оптимизировав эту программу. | ||
| Она успешно справлялась с большими файлами за 91-92 секунды, но мы решили ее дооптимизировать. | ||
|
|
||
| ## Формирование метрики | ||
| Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: *тут ваша метрика* | ||
| Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: замер времени работы скрипта и количевсто используемой памяти. | ||
|
|
||
| ## Гарантия корректности работы оптимизированной программы | ||
| Программа поставлялась с тестом. Выполнение этого теста позволяет не допустить изменения логики программы при оптимизации. | ||
|
|
||
| ## Feedback-Loop | ||
| Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный `feedback-loop`, который позволил мне получать обратную связь по эффективности сделанных изменений за *время, которое у вас получилось* | ||
| Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный `feedback-loop`, который позволил мне получать обратную связь по эффективности сделанных изменений за 7 секунд обработки файла. | ||
|
|
||
| Вот как я построил `feedback_loop`: *как вы построили feedback_loop* | ||
| Вот как я построил `feedback_loop`: | ||
| 1. Создал shell скрипт с тремя запусками для 50k, 100k и 200k строками из data_large файла. | ||
| 2. Добавил профилеровщик памяти | ||
| 3. Вносил изменения в исходный код | ||
| 4. Проверял скорость работы обработки файла | ||
| 5. Если скорость не менялась, переходил к шагу 3. Если ускорялось, к шагу 6. | ||
| 6. Переходим к следующей точке роста. | ||
|
|
||
| ## Вникаем в детали системы, чтобы найти 20% точек роста | ||
| Для того, чтобы найти "точки роста" для оптимизации я воспользовался *инструментами, которыми вы воспользовались* | ||
| Для того, чтобы найти "точки роста" для оптимизации я воспользовался ruby-prof. | ||
|
|
||
| Вот какие проблемы удалось найти и решить | ||
|
|
||
| ### Ваша находка №1 | ||
| О вашей находке №1 | ||
| Используя Callstack отчет, обнаружил, что код из задания 1 имеет точку роста(12% всего времени) в методе include? для сбора уникальных браузеров. | ||
|
|
||
| После рефакторинга, точка роста упала до менее 2% от всего времени. | ||
| Время работы всего срипта упало до 86 секунд. | ||
| Количество выделяемой памяти практически не поменялось. | ||
|
|
||
| ### Ваша находка №2 | ||
| О вашей находке №2 | ||
| Следующая точка роста была в Hash#to_json(около 14%) с Callstack отчетом: | ||
| 14.02% (14.02%) JSON::Ext::Generator::GeneratorMethods::Hash#to_json [1 calls, 1 total] | ||
|
|
||
| ### Ваша находка №X | ||
| О вашей находке №X | ||
| Использование гема oj помогло ужать генерацию json до ~3%: | ||
| 2.62% (2.62%) <Module::Oj>#dump [1 calls, 1 total] | ||
| Время работы всего срипта упало до 76-78 секунд. | ||
|
|
||
| ## Результаты | ||
| В результате проделанной оптимизации наконец удалось обработать файл с данными. | ||
| Удалось улучшить метрику системы с *того, что у вас было в начале, до того, что получилось в конце* | ||
| ### Ваша находка №3 | ||
| Собрал flamegraph из rbspy. Проанализировал и понял, что callstask отчет от ruby-prof показывает тоже самое. | ||
| Но информации чуть меньше от rbspy и ruby 2.6.x не поддерживается в rbspy. | ||
|
|
||
| ### Ваша находка №4 | ||
| Около 47% всего времени занимал сбор статистики в TaskClass#collect_stats_from_users. | ||
| Повторяющиеся или похожие map методы были вынесены в общие методы, что помогло сократить все время с 47% до 33%. | ||
| Время работы всего срипта упало до 60-65 секунд. | ||
|
|
||
| ### Ваша находка №5 | ||
| При переходе на другую систему с 8 ядрами с 16 RAM. Скорость обработки файла упала сама собой до 45 секунд... | ||
|
Owner
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. Хех |
||
| Возможно ноутбук с 4 ядрами и 8 RAM был подгружен другими процессами... | ||
|
|
||
| *Какими ещё результами можете поделиться* | ||
| ### Ваша находка №6 | ||
| Upcase вызывался достаточно часто в TaskClass#parse_session: | ||
| 3.87% (29.93%) String#upcase | ||
|
|
||
| Было решено зарефакторить этот метод. После рефакторинга скорость оботки файла упала до 39 секунд. | ||
|
|
||
| ### Ваша находка №7 | ||
| Самую жирную точку роста в split(',') не удалось решить. split занимал 23% всего времени на конечном файле после всех изменений перечисленных выше. | ||
|
|
||
| ## Результаты | ||
| В результате проделанной оптимизации удалось сократить время обработки файла с ~90 секунд до ~39 секунд. | ||
|
Owner
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. 👍 |
||
|
|
||
| ## Защита от регресса производительности | ||
| Для защиты от потери достигнутого прогресса при дальнейших изменениях программы сделано *то, что вы для этого сделали* | ||
| Для защиты от потери достигнутого прогресса при дальнейших изменениях программы изменен старый юнит тест, чтобы не использовать весь файл с данными, а только маленький файл для ускорения прогона тестов. Но с погрешностью в еще 20% минуты с учетом загруженность или других факторов на системы, которые могут повлиять на скорость обработки данных. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| echo '--- 50k records ---' | ||
| ruby task-2.rb data_large_50k.txt | ||
| echo '--- 100k records ---' | ||
| ruby task-2.rb data_large_100k.txt | ||
| echo '--- 200k records ---' | ||
| ruby task-2.rb data_large_200k.txt |
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
👍