Skip to content

Latest commit

 

History

History
65 lines (47 loc) · 5.29 KB

File metadata and controls

65 lines (47 loc) · 5.29 KB

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

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

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

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

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

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

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

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

Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: использованая память (memory usage)

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

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

Feedback-Loop

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

Вот как я построил feedback_loop: замер -> изменения в точках роста -> проверка работоспособности -> замер

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

Для того, чтобы найти "точки роста" для оптимизации я воспользовался профайлерами памяти, в первую очередь гемом memory_profiler, файлом с 50 000 строк

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

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

  • гем memory_profiler показал строчку sessions = sessions + [parse_session(line)] if cols[0] == 'session'
  • убрал клонирование массива и начал использовать оператор <<
  • метроика улучшилась с 8 ГБ до 3.5 ГБ
  • теперь показывает другую строку

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

  • гем memory_profiler показал строчку user_sessions = sessions.select { |session| session['user_id'] == user['id'] }
  • использовать ту же оптимизацию что использовал в прошлом задании
  • метроика улучшилась с 3.5 ГБ до 772 МБ
  • теперь показывает другую строку

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

  • гем memory_profiler показал строчку users = users + [parse_user(line)] if cols[0] == 'user'
  • так же как и в первой находке
  • метроика улучшилась с 772 МБ до 517 МБ
  • теперь показывает другую строку

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

  • гем memory_profiler показал строчку { 'dates' => user.sessions.map{|s| s['date']}.map {|d| Date.parse(d)}.sort.reverse.map { |d| d.iso8601 } }
  • использовать ту же оптимизацию что использовал в прошлом задании
  • метроика улучшилась с 517 МБ до 295 МБ
  • теперь показывает другую строку

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

  • прочитал в задании что программу надо переписать в потоковом стиле
  • попробовал переписать читая по строчке за раз
  • метроика улучшилась с 295 МБ до 35 МБ
  • теперь показывает другую строку

Результаты

В результате проделанной оптимизации наконец удалось обработать файл с данными. Удалось улучшить метрику системы с более 8000+ мегабайт с оригинальным кодом и файлом данными на 50000 строк до 35 МБ с исправленым кодом и файлом data_large.txt и уложиться в заданный бюджет.

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

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