Skip to content

Latest commit

 

History

History
61 lines (45 loc) · 5.81 KB

File metadata and controls

61 lines (45 loc) · 5.81 KB

Case-study оптимизации

Актуальная проблема

В нашем проекте возникла серьёзная проблема.

Необходимо было обработать файл с данными, чуть больше ста мегабайт.

У нас уже была программа на ruby, которая умела делать нужную обработку.

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

Я решил исправить эту проблему, оптимизировав эту программу.

Формирование метрики

Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: Calculating ------------------------------------- Process 2500 4.440 (± 0.0%) i/s - 23.000 in 5.181137s Process 5000 1.295 (± 0.0%) i/s - 7.000 in 5.406855s Process 10000 0.363 (± 0.0%) i/s - 2.000 in 5.512669s Process 20000 0.069 (± 0.0%) i/s - 1.000 in 14.437365s Process 40000 0.012 (± 0.0%) i/s - 1.000 in 81.002661s

Comparison: Process 2500: 4.4 i/s Process 5000: 1.3 i/s - 3.43x slower Process 10000: 0.4 i/s - 12.23x slower Process 20000: 0.1 i/s - 64.11x slower Process 40000: 0.0 i/s - 359.68x slower

Гарантия корректности работы оптимизированной программы

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

Feedback-Loop

Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный feedback-loop, который позволил мне получать обратную связь по эффективности сделанных изменений за

Вот как я построил feedback_loop: создал из исходного файла с данными, файлы меньшим размером, ориентируясь на количество строк head -n N data_large.txt > dataN.txt Подготовил файл asymptotics.rb который автоматически собирает все метрики и делает некоторые замеры.

Вникаем в детали системы, чтобы найти 20% точек роста

Для того, чтобы найти "точки роста" для оптимизации я воспользовался rubyprof (WALL_TIME, ALLOCATIONS), rbspy, valgrind

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

Ваша находка №1

При увеличении количества обрабатываемых строк исходных данных в два раза, время выполнения программы увеличивается в пять раз, количество потребляемой памяти в четыре раза. Было обнаружено, что на выполнение следующей строки кода уходит 93% времени user_sessions = sessions.select { |session| session['user_id'] == user['id'] } В этой строке происходит выборка сессий пользователя, после чего создается новый объект Пользователь с выбранными сессиями и остальными атрибутами. Для оптимизации этой логики было принято решение избавится от объекта Пользователя в пользу Хэша, с аналогичными параметрами, которые можно собирать в момент обработки исходного файла.

Результаты

Результат оптимизации в папке metrics/optimizations Получилось избавиться от кратной регрессии - увеличение объема данных в два раза, в приблизительно в два раза увеличивает время исполнения программы и в два раза увеличивает потребляемый объем оперативной памяти

В результате проделанной оптимизации наконец удалось обработать файл с данными. Удалось улучшить метрику системы с того, что нельзя было дождаться исполнения скрипта, до того, что 1_000_000 строк обрабатывается за 36 секунд и потребляет 1Gb оперативной памяти Так как параметры необходимой оптимизации не устанавливались, оптимизацию на этом прекратил.

Защита от регресса производительности

Для защиты от потери достигнутого прогресса при дальнейших изменениях программы добавлен тест на производительность