Skip to content

Commit d273a26

Browse files
committed
Итерация 1:
* Добавлена информация в `case-study` и графики. * Бенчмарк вынесен в отдельный файл. * Тест вынесен в отдельный файл. * Создан спек для хакрепления результата. * Изменена основная программа для возможности работы с разными файлами.
1 parent c34987e commit d273a26

8 files changed

+99
-18
lines changed

case-study.md

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,27 @@
1111

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

14+
Для начала я запустил небольшой бенчмарк, чтобы понять, какая асимптотика у программы. Для каждого измерения взял первые `n` строк из файла с полными данными. Результаты бенчмарка выглядели так:
15+
16+
```
17+
user system total real
18+
line_count = 10 0.001417 0.000258 0.001675 ( 0.011769)
19+
line_count = 50 0.001417 0.000258 0.001675 ( 0.011346)
20+
line_count = 100 0.002608 0.000000 0.002608 ( 0.013075)
21+
line_count = 500 0.002582 0.009544 0.012126 ( 0.022563)
22+
line_count = 1000 0.009544 0.010132 0.019676 ( 0.030331)
23+
line_count = 5000 0.218717 0.020935 0.239652 ( 0.253703)
24+
line_count = 10000 0.979160 0.000000 0.979160 ( 1.010272)
25+
line_count = 20000 4.887525 0.017847 4.905372 ( 4.930110)
26+
line_count = 30000 13.817134 0.099889 13.917023 ( 13.935027)
27+
line_count = 40000 29.488743 0.109932 29.598675 ( 29.619598)
28+
line_count = 50000 50.501303 0.109971 50.611274 ( 50.648952)
29+
```
1430
График зависимости времени работы программы от объёма входных данных выглядел так:
1531

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

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

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

29-
Вот как я построил `feedback_loop`: *как вы построили feedback_loop*
45+
Вот как я построил `feedback_loop`:
46+
1. Запуск профилировщик `rbspy` на малом объёме данных.
47+
2. Анализ отчёта профилировщика и выявление главной точки роста.
48+
3. Оптимизация главной точки роста.
49+
4. Запуск теста идущего в комплекте с программой.
50+
5. Запуск бенчмарка на малых объёмах данных и проверка попадания в бюджет.
51+
6. Закрепление изменений в системе контроля версий.
52+
7. Обновление бюджета в бенчмарке.
3053

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

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

36-
### Ваша находка №1
37-
- какой отчёт показал главную точку роста
38-
- как вы решили её оптимизировать
39-
- как изменилась метрика
40-
- как изменился отчёт профилировщика - исправленная проблема перестала быть главной точкой роста?
59+
### Итерация №1
60+
- Использовался отчёт профилировщика `rbspy` при запуске на файле с 10000 строк. Отчёт показал, что больше всего времени программа тратит на выборку сессий по пользователю из массива сессий.
61+
- Было принято решение вынести эту строку из цикла и вместо этого выполнить `Array#group_by`.
62+
- Время выполнения программы на файле с 10000 строками уменьшилось с 1.010272 до 0.283054 секунды.
63+
- Также уменьшилось время выполнения программы на файлах большего размера.
64+
```
65+
user system total real
66+
line_count = 11 0.001461 0.000487 0.001948 ( 0.012805)
67+
line_count = 51 0.002695 0.000898 0.003593 ( 0.009555)
68+
line_count = 100 0.003591 0.001197 0.004788 ( 0.010995)
69+
line_count = 501 0.006650 0.000000 0.008918 ( 0.019601)
70+
line_count = 1000 0.012564 0.000000 0.015094 ( 0.029157)
71+
line_count = 5000 0.050438 0.019783 0.073774 ( 0.103414)
72+
line_count = 10000 0.160801 0.000000 0.170127 ( 0.283054)
73+
line_count = 20001 0.400933 0.048675 0.455922 ( 0.547208)
74+
line_count = 30000 0.637398 0.019211 0.666364 ( 0.770074)
75+
line_count = 40000 0.978085 0.010174 1.003803 ( 1.200759)
76+
line_count = 50000 1.290851 0.019795 1.325995 ( 1.518988)
77+
```
78+
- График зависимости времени работы программы от объёма входных данных стал выглядеть так (синие точки):
79+
80+
![График зависимости времени работы программы](execution%20time%20graph%20-%20after%201st%20iteration.png "График зависимости времени работы программы")
81+
![График зависимости времени работы программы](execution%20time%20graph%20-%20after%201st%20iteration%20-%20zoom.png "График зависимости времени работы программы")
82+
Судя по изменившейся форме графика можно сделать вывод, что асимптотика стала линейной в данном диапазоне обрабатываемых строкб возможно на более крупном масштабе мы увидим ещё одну степенную асимптоту.
83+
- Отчёт профилировщика изменился значительно: исправленная проблема перестала быть главной точкой роста, время подключения библиотек стало основным.
4184

4285
### Ваша находка №2
4386
- какой отчёт показал главную точку роста
Loading
43.5 KB
Loading

execution time graph.png

668 Bytes
Loading

task-1.rb

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
require 'json'
44
require 'pry'
55
require 'date'
6-
require 'benchmark'
7-
8-
include Benchmark
96

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

98+
user_sessions = sessions.group_by { |session| session['user_id'] }
10199
users.each do |user|
102100
attributes = user
103-
user_sessions = sessions.select { |session| session['user_id'] == user['id'] }
104-
user_object = User.new(attributes: attributes, sessions: user_sessions)
101+
user_object = User.new(attributes: attributes, sessions: user_sessions[user['id']])
105102
users_objects = users_objects + [user_object]
106103
end
107104

108105
report['usersStats'] = {}
109106

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

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

148-
Benchmark.bm do |x|
149-
[10, 50, 100, 500, 1_000, 5_000, 10_000, 20_000, 30_000, 40_000, 50_000].each do |n|
150-
filename = "data_large#{n}.txt"
151-
`head -n #{n} data_large.txt > data_large#{n}.txt` unless File.exist?(filename)
152-
x.report("n=#{n}") { work(filename) }
145+
# Метод для запуска работы с разным количеством строк файла `data_large.txt`
146+
# @param [Integer] n количество строк
147+
# @return [void]
148+
def work_with_line_count(n)
149+
filename = "data_large#{n}.txt"
150+
`head -n #{n} data_large.txt > data_large#{n}.txt` unless File.exist?(filename)
151+
work(filename)
152+
ensure
153+
File.delete(filename) if File.exist?(filename)
154+
end
155+
156+
if ENV.key? 'LINE_COUNT'
157+
line_count = ENV['LINE_COUNT'].to_i
158+
work_with_line_count(line_count)
159+
else
160+
if ENV.key? 'FILE_NAME'
161+
work ENV['FILE_NAME']
162+
else
163+
work
153164
end
154165
end

task-1_benchmark.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
require 'benchmark'
2+
require './task-1'
3+
4+
include Benchmark
5+
6+
Benchmark.bm do |x|
7+
[11, 51, 100, 501, 1_000, 5_000, 10_000, 20_001, 30_000, 40_000, 50_000].each do |line_count|
8+
x.report("line_count = #{line_count}") { work_with_line_count(line_count) }
9+
end
10+
end

task-1_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
require 'rspec-benchmark'
2+
require './task-1'
3+
4+
RSpec.configure do |config|
5+
config.include RSpec::Benchmark::Matchers
6+
end
7+
8+
describe 'Performance' do
9+
describe '#work' do
10+
let(:line_count) { 20_001 }
11+
let(:expected_time) { 4 }
12+
13+
it 'works under or equal expected time' do
14+
expect { work_with_line_count(line_count) }.to perform_under(expected_time).sec.warmup(2).times.sample(4).times
15+
end
16+
end
17+
end

test.rb renamed to task-1_test.rb

File renamed without changes.

0 commit comments

Comments
 (0)