Skip to content
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

Cpu optimization #149

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions benchmark.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require 'benchmark'
require 'lazy'
require_relative 'task-1'

# array = (1..10000000).to_a

# def eqeq_test(array)
# array.select { |el| el == 500000 }
# end

# def include_test(array)
# array.include?(500000)
# end

# def in_test(array)
# 500000.in?(array)
# end
# def map_test(array)
# array.map { |el| el % 10 == 0 ? el : nil }.compact
# end

# def test_select(array)
# array.select { |el| el % 10 == 0 }
# end

# def test_reject(array)
# array.reject { |el| el % 10 == 0 }
# end

# def test_slice(array)
# array.slice_after { |element| element % 10 == 0 }.to_a
# end

# def test_lazy(array)
# array.lazy.select { |el| el % 10 == 0 }
# end

# def test_set(array)
# array.to_set.select { |el| el % 10 == 0 }
# end

# Benchmark.bm do |benchmark|
# benchmark.report('eqeq') { eqeq_test(array) }
# benchmark.report('include') { include_test(array) }
# benchmark.report('in') { in_test(array) }
# end
82 changes: 82 additions & 0 deletions case-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Case-study оптимизации

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

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

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

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

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

## Формирование метрики
Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: *тут ваша метрика*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*это был плейсхолдер*


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

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*это тоже был плейсхолдер*


Вот как я построил `feedback_loop`: *как вы построили feedback_loop*

## Вникаем в детали системы, чтобы найти главные точки роста
Для того, чтобы найти "точки роста" для оптимизации я воспользовался *инструментами, которыми вы воспользовались*

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

### Ваша находка №1
- какой отчёт показал главную точку роста
- как вы решили её оптимизировать
- как изменилась метрика
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста?

### Ваша находка №2
- какой отчёт показал главную точку роста
- как вы решили её оптимизировать
- как изменилась метрика
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста?

### Ваша находка №X
- какой отчёт показал главную точку роста
- как вы решили её оптимизировать
- как изменилась метрика
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста?

## Результаты
В результате проделанной оптимизации наконец удалось обработать файл с данными.
Удалось улучшить метрику системы с *того, что у вас было в начале, до того, что получилось в конце* и уложиться в заданный бюджет.

*Какими ещё результами можете поделиться*

## Защита от регрессии производительности
Для защиты от потери достигнутого прогресса при дальнейших изменениях программы *о performance-тестах, которые вы написали*

- Подготовил тестовые образцы на 10 20 30 40 50 к строк
- Добавил скрипт для генерации флэт отчета
- Изменил исходный файл для обработки параметра с именем файла
- с помощью стакпрофа замерил время выполнения для избранных объемов
- 10000 - 3с, 20000 - 17с, 30000 - 46, 40000 - killed
- явный экспонентный рост сложности обработки
- во всех отчетах есть 2 явных ботлнека: select & ==
- нужно заменить == на более быстрый аналог
- проверяем через бенчмарк == и include? (второй гораздо быстрее)
- применяем
- 10000 - 2с, 20000 - 7с, 30000 - 15c, 40000 - killed
- select раз за разом перебирает массив сессий. Гипотеза: скорость обработки хэша гораздо быстрее. И поиск быстрее идет по ключам. разделим изначальный массив на сессии и пользователей.
- сборка сессий и пользователей на 40к пользователей в изначальном варианте занимает почти две секунды, а при использовании group_by это время около тысячных долей секунды
- сгрупировал сессии и пользователей
- распарсил в хэши
- добавил группированные сессии где ключ - это айди пользователя, а значение - массив сессий. Благодаря такоц структуре стало возможным отказаться от тяжелого селекта перебирающего сессии для каждого пользователя.
- исправил возникающие ошибки
- получилось добиться зависимости производительности, приближенной к линейной: 20к-0,2с..1кк-4с
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

да, это самая главная оптимизация тут, так как вместе с ней исправляется асимптотика всего алгоритма

- прогон 1kk записей занимает 24 сек(далеко до идеала)
- просмотрев флэт отчет видим что функция collect_stats_from_users вызывается 7 раз, внутрянка работает целых 18 секунд, а массив пользователей прогоняется в каждом блоке.
- объединил обработку данных в один блок
- отказался от сборки сессий и пользователей черех group_by использовал << при проходе по начальному файлу. получил незначительный прирост производительности
- использовал << везде где складывались массивы. Обработался файл на 3млн записей за 59 секунд. data_large упорно продолжает падать(проверял с отключенным и включенным GC).
- в последнем отчете для 3кк записей увидел что чтение данных из массива в сборке данных пользователя вызывается 13кк раз вынес сессий в переменную. Количество вызовов [] сократилось вдвое.
- последние показания 1кк ~ 19сек, 2кк ~ 38сек, 3кк ~ 58 сек, large ~ 65 сек

Loading