diff --git a/case_study.md b/case_study.md new file mode 100644 index 0000000..e75369a --- /dev/null +++ b/case_study.md @@ -0,0 +1,123 @@ +## Актуальная проблема +`test-suite` рабочего проекта выполняется достаточно долго. Необходимо оптимизировать его и ускорить процесс прохождения тестов. + +## Начальные данные +`test-suite` содержит 7673 экземпляров + +```bash +Finished in 6 minutes 36 seconds (files took 7.37 seconds to load) +7673 examples, 0 failures +Line Coverage: 94.95% (12600 / 13270) +``` + +## Оптимизация +### Оптимизация - 1 +Для начала решил прогнал тесты в параллельном режиме с помощью `parallel_tests`. +Использовал 3 потока. +```bash +Finished in 3 minutes 11.7 seconds (files took 2.88 seconds to load) +``` +В результате получил ускорение прохождения тестов более чем в 2 раза. +*** +### Оптимизация - 2 +Решил `ударить по площадям` дабы еще меньше сократить время на feedback loop и +написал скрипт который заменяет все вхождения `create` на `build_stubbed`, если тест проходит, то оставляет изменение. + +В результате изменения произошли в 170 файлах(к сожалению забыл поставить счетчик непосредственно замен) +Как итог: +- в последовательном режиме ускорение на 1 минуту: с `6 minutes 36 seconds` до `5 minutes 35 seconds` +- в параллельном режиме на удивление улучшения практически не произошло: с `3 minutes 11.7 seconds` до `3 minutes 4.3 seconds` +*** +### Оптимизация - 3 +Решил продолжить тактику и сначала искать точки роста с наименьшими затратами, а уже потоим переходить к более затратным по времени +изучениям больших отчетов профилировщиков, таких как speedscope, cachegrind, где точки роста не всегда очевидны(в случае если в тестах нет откровенных проблем) + +Поэтому я воспользовался инструментом `RSpec-DISSECT`: +```shell +Total `let` time: 04:12.176 +Total `before(:each)` time: 04:09.653 + +Top 5 slowest suites (by `let` time): + +Boardgames::RollDice::MegaRoll (.../mega_roll_spec.rb:6) – 00:20.066 of 00:26.192 (401) + ↳ player – 2666 + ↳ user – 1586 + ↳ boardgame – 1274 +Collections::...tiblePresenter (.../collectible_presenter_spec.rb:6) – 00:11.224 of 00:12.200 (93) + ↳ player – 287 + ↳ user – 186 + ↳ account – 186 +Minigames::PlayProcessor (.../play_processor_spec.rb:7) – 00:09.942 of 00:10.964 (49) + ↳ player – 246 + ↳ user – 147 + ↳ minigame – 117 +Events::Instance (.../instance_spec.rb:5) – 00:09.523 of 00:12.136 (133) + ↳ player – 616 + ↳ event – 443 + ↳ user – 399 +Collections::...eshipPresenter (.../battleship_presenter_spec.rb:6) – 00:08.142 of 00:09.192 (95) + ↳ ships_storage – 309 + ↳ player – 286 + ↳ user – 190 + +Top 5 slowest suites (by `before(:each)` time): + +Boardgames::RollDice::MegaRoll (.../mega_roll_spec.rb:6) – 00:20.374 of 00:26.192 (401) +Events::Instance (.../instance_spec.rb:5) – 00:11.653 of 00:12.136 (133) +Collections::...tiblePresenter (.../collectible_presenter_spec.rb:6) – 00:08.166 of 00:12.200 (93) +Events::ProgressBuilder (.../progress_builder_spec.rb:5) – 00:07.653 of 00:09.040 (167) +Events::V3::CurrentPresenter (.../current_presenter_spec.rb:6) – 00:06.789 of 00:09.037 (66) +``` +Очень удобно, сразу видна необходимость в первую очередь поработать с Boardgames::RollDice::MegaRoll тестами. + +Заменил в самых медленных тестах `let` на `let_it_be`. + +Кроме того в нашем проекте в хелпере для всех тестов глобально определяются через `let` player, user и account. + +Заменил их также на `let_it_be`. + +Как результат, избавился от основных точек роста и почти в 2 раза уменьшил время прохождения самого медленного теста c 00:26.192 до 00:13:543. + +Промежуточный итог: +- в последовательном режиме ускорение с `5 minutes 35 seconds` до `4 minutes 20.5 seconds` +- в параллельном режиме улучшения не произошло: `3 minutes 8.3 seconds` +*** +### Оптимизация - 4 +C помощью `EVENT_PROF="factory.create" bundle exec rspec` сделал отчет о 5 самых медленных наборов: +```shell +Top 5 slowest suites (by time): + +Collections::...tiblePresenter (.../collectible_presenter_spec.rb:6) – 00:06.944 (361 / 93) of 00:09.022 (76.97%) +Events::Instance (.../instance_spec.rb:5) – 00:06.631 (776 / 133) of 00:07.745 (85.62%) +Collections::...eshipPresenter (.../battleship_presenter_spec.rb:6) – 00:05.952 (370 / 95) of 00:07.777 (76.54%) +Events::V3::CurrentPresenter (.../current_presenter_spec.rb:6) – 00:05.881 (1406 / 66) of 00:08.221 (71.54%) +Events::V1::CurrentPresenter (.../current_presenter_spec.rb:6) – 00:05.731 (1384 / 65) of 00:08.142 (70.39%) +``` + +Оптимизировал каждый из этих тестов следующим образом: +- Строил `flamegraph` отчет по тесту, где видна проблемная фабрика +- Оптимизировал фабрики в основном 2 путями: где то использовал `trait` для ассоциаций, где то вынес определение ассоциаций +из фабрики прямо в тесты только там где они необходимы. + +Приведу пример результата оптимизации на примере наиболее затратного по работе фабрик теста `Collections::...tiblePresenter` +В отчете можно увидеть довольно неприятный каскад, который удалось сгладить путем выноса ассоциации из фабрики. +Ускорение получилось с 00:09.022 до 00:04.561 + +До оптимизации: +![flamegraph_before.png](images/flamegraph_before.png) + +После оптимизации: +![flamegraph_after.png](images/flamegraph_after.png) + +Промежуточный итог: +- в последовательном режиме ускорение на 1 минуту: с `4 minutes 20.5 seconds` до `4 minutes 0.7 seconds` +- в параллельном режиме улучшения не произошло: `3 minutes 0.2 seconds` +*** + +**Подведу промежуточный итог. Используя только общих приемы, без вникания в логику и составление stackprof отчетов удалось** +**сократить время выполнения тестов в последовательном режиме на 40% с 6 minutes 36 seconds до 4 minutes 0.7 seconds** + +## TODO: +Запланировал на следующую неделю продолжить оптимизацию и использовать `stackprof`. + +План построить json отчет с сэмплированием и найти точки роста с использованием `speedscope`. diff --git a/images/flamegraph_after.png b/images/flamegraph_after.png new file mode 100644 index 0000000..cb48e0b Binary files /dev/null and b/images/flamegraph_after.png differ diff --git a/images/flamegraph_before.png b/images/flamegraph_before.png new file mode 100644 index 0000000..a0d72b7 Binary files /dev/null and b/images/flamegraph_before.png differ