Skip to content

Commit

Permalink
Итерация 1:
Browse files Browse the repository at this point in the history
* Добавлена информация в `case-study` и графики.
* Бенчмарк вынесен в отдельный файл.
* Тест вынесен в отдельный файл.
* Создан спек для хакрепления результата.
* Изменена основная программа для возможности работы с разными файлами.
  • Loading branch information
2xG committed Dec 9, 2023
1 parent c34987e commit d273a26
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 18 deletions.
57 changes: 50 additions & 7 deletions case-study.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,27 @@

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

Для начала я запустил небольшой бенчмарк, чтобы понять, какая асимптотика у программы. Для каждого измерения взял первые `n` строк из файла с полными данными. Результаты бенчмарка выглядели так:

```
user system total real
line_count = 10 0.001417 0.000258 0.001675 ( 0.011769)
line_count = 50 0.001417 0.000258 0.001675 ( 0.011346)
line_count = 100 0.002608 0.000000 0.002608 ( 0.013075)
line_count = 500 0.002582 0.009544 0.012126 ( 0.022563)
line_count = 1000 0.009544 0.010132 0.019676 ( 0.030331)
line_count = 5000 0.218717 0.020935 0.239652 ( 0.253703)
line_count = 10000 0.979160 0.000000 0.979160 ( 1.010272)
line_count = 20000 4.887525 0.017847 4.905372 ( 4.930110)
line_count = 30000 13.817134 0.099889 13.917023 ( 13.935027)
line_count = 40000 29.488743 0.109932 29.598675 ( 29.619598)
line_count = 50000 50.501303 0.109971 50.611274 ( 50.648952)
```
График зависимости времени работы программы от объёма входных данных выглядел так:

![График зависимости времени работы программы](execution%20time%20graph.png "График зависимости времени работы программы")

Судя по графику, программа работала с асимптотикой `O(n^2)`, что неудивительно, ведь в ней был вложенный цикл.
Судя по графику, программа работала с асимптотикой `O(n^2)`.

## Формирование метрики
Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: время работы программы на полном объёме данных.
Expand All @@ -26,18 +42,45 @@
## Feedback-Loop
Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный `feedback-loop`, который позволил мне получать обратную связь по эффективности сделанных изменений за *время, которое у вас получилось*

Вот как я построил `feedback_loop`: *как вы построили feedback_loop*
Вот как я построил `feedback_loop`:
1. Запуск профилировщик `rbspy` на малом объёме данных.
2. Анализ отчёта профилировщика и выявление главной точки роста.
3. Оптимизация главной точки роста.
4. Запуск теста идущего в комплекте с программой.
5. Запуск бенчмарка на малых объёмах данных и проверка попадания в бюджет.
6. Закрепление изменений в системе контроля версий.
7. Обновление бюджета в бенчмарке.

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

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

### Ваша находка №1
- какой отчёт показал главную точку роста
- как вы решили её оптимизировать
- как изменилась метрика
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста?
### Итерация №1
- Использовался отчёт профилировщика `rbspy` при запуске на файле с 10000 строк. Отчёт показал, что больше всего времени программа тратит на выборку сессий по пользователю из массива сессий.
- Было принято решение вынести эту строку из цикла и вместо этого выполнить `Array#group_by`.
- Время выполнения программы на файле с 10000 строками уменьшилось с 1.010272 до 0.283054 секунды.
- Также уменьшилось время выполнения программы на файлах большего размера.
```
user system total real
line_count = 11 0.001461 0.000487 0.001948 ( 0.012805)
line_count = 51 0.002695 0.000898 0.003593 ( 0.009555)
line_count = 100 0.003591 0.001197 0.004788 ( 0.010995)
line_count = 501 0.006650 0.000000 0.008918 ( 0.019601)
line_count = 1000 0.012564 0.000000 0.015094 ( 0.029157)
line_count = 5000 0.050438 0.019783 0.073774 ( 0.103414)
line_count = 10000 0.160801 0.000000 0.170127 ( 0.283054)
line_count = 20001 0.400933 0.048675 0.455922 ( 0.547208)
line_count = 30000 0.637398 0.019211 0.666364 ( 0.770074)
line_count = 40000 0.978085 0.010174 1.003803 ( 1.200759)
line_count = 50000 1.290851 0.019795 1.325995 ( 1.518988)
```
- График зависимости времени работы программы от объёма входных данных стал выглядеть так (синие точки):

![График зависимости времени работы программы](execution%20time%20graph%20-%20after%201st%20iteration.png "График зависимости времени работы программы")
![График зависимости времени работы программы](execution%20time%20graph%20-%20after%201st%20iteration%20-%20zoom.png "График зависимости времени работы программы")
Судя по изменившейся форме графика можно сделать вывод, что асимптотика стала линейной в данном диапазоне обрабатываемых строкб возможно на более крупном масштабе мы увидим ещё одну степенную асимптоту.
- Отчёт профилировщика изменился значительно: исправленная проблема перестала быть главной точкой роста, время подключения библиотек стало основным.

### Ваша находка №2
- какой отчёт показал главную точку роста
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added execution time graph - after 1st iteration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified execution time graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 22 additions & 11 deletions task-1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
require 'json'
require 'pry'
require 'date'
require 'benchmark'

include Benchmark

class User
attr_reader :attributes, :sessions
Expand Down Expand Up @@ -98,18 +95,18 @@ def work(filename = 'data.txt')
# Статистика по пользователям
users_objects = []

user_sessions = sessions.group_by { |session| session['user_id'] }
users.each do |user|
attributes = user
user_sessions = sessions.select { |session| session['user_id'] == user['id'] }
user_object = User.new(attributes: attributes, sessions: user_sessions)
user_object = User.new(attributes: attributes, sessions: user_sessions[user['id']])
users_objects = users_objects + [user_object]
end

report['usersStats'] = {}

# Собираем количество сессий по пользователям
collect_stats_from_users(report, users_objects) do |user|
{ 'sessionsCount' => user.sessions.count }
{ 'sessionsCount' => user.sessions&.count.to_i }
end

# Собираем количество времени по пользователям
Expand Down Expand Up @@ -145,10 +142,24 @@ def work(filename = 'data.txt')
File.write('result.json', "#{report.to_json}\n")
end

Benchmark.bm do |x|
[10, 50, 100, 500, 1_000, 5_000, 10_000, 20_000, 30_000, 40_000, 50_000].each do |n|
filename = "data_large#{n}.txt"
`head -n #{n} data_large.txt > data_large#{n}.txt` unless File.exist?(filename)
x.report("n=#{n}") { work(filename) }
# Метод для запуска работы с разным количеством строк файла `data_large.txt`
# @param [Integer] n количество строк
# @return [void]
def work_with_line_count(n)
filename = "data_large#{n}.txt"
`head -n #{n} data_large.txt > data_large#{n}.txt` unless File.exist?(filename)
work(filename)
ensure
File.delete(filename) if File.exist?(filename)
end

if ENV.key? 'LINE_COUNT'
line_count = ENV['LINE_COUNT'].to_i
work_with_line_count(line_count)
else
if ENV.key? 'FILE_NAME'
work ENV['FILE_NAME']
else
work
end
end
10 changes: 10 additions & 0 deletions task-1_benchmark.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'benchmark'
require './task-1'

include Benchmark

Benchmark.bm do |x|
[11, 51, 100, 501, 1_000, 5_000, 10_000, 20_001, 30_000, 40_000, 50_000].each do |line_count|
x.report("line_count = #{line_count}") { work_with_line_count(line_count) }
end
end
17 changes: 17 additions & 0 deletions task-1_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require 'rspec-benchmark'
require './task-1'

RSpec.configure do |config|
config.include RSpec::Benchmark::Matchers
end

describe 'Performance' do
describe '#work' do
let(:line_count) { 20_001 }
let(:expected_time) { 4 }

it 'works under or equal expected time' do
expect { work_with_line_count(line_count) }.to perform_under(expected_time).sec.warmup(2).times.sample(4).times
end
end
end
File renamed without changes.

0 comments on commit d273a26

Please sign in to comment.