Skip to content
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
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# не версионируем вывод программы
/data.txt
/result.json
data_large.txt
/tmp

.DS_Store
15 changes: 15 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
source 'https://rubygems.org'

git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

gem 'benchmark-ips'
gem 'memory_profiler'
gem 'ruby-prof'
gem 'slop'
Copy link
Owner

Choose a reason for hiding this comment

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

👍

gem 'stackprof'

gem 'ruby-progressbar'

gem 'pry-byebug'
gem 'rspec'
gem 'rubocop'
65 changes: 65 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
GEM
remote: https://rubygems.org/
specs:
ast (2.4.0)
benchmark-ips (2.7.2)
byebug (11.0.1)
coderay (1.1.2)
diff-lcs (1.3)
jaro_winkler (1.5.2)
memory_profiler (0.9.13)
method_source (0.9.2)
parallel (1.14.0)
parser (2.6.2.0)
ast (~> 2.4.0)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
pry-byebug (3.7.0)
byebug (~> 11.0)
pry (~> 0.10)
psych (3.1.0)
rainbow (3.0.0)
rspec (3.8.0)
rspec-core (~> 3.8.0)
rspec-expectations (~> 3.8.0)
rspec-mocks (~> 3.8.0)
rspec-core (3.8.0)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-mocks (3.8.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-support (3.8.0)
rubocop (0.66.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.5, != 2.5.1.1)
psych (>= 3.1.0)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.6)
ruby-prof (0.17.0)
ruby-progressbar (1.10.0)
slop (4.6.2)
stackprof (0.2.12)
unicode-display_width (1.5.0)

PLATFORMS
ruby

DEPENDENCIES
benchmark-ips
memory_profiler
pry-byebug
rspec
rubocop
ruby-prof
ruby-progressbar
slop
stackprof

BUNDLED WITH
1.16.6
111 changes: 111 additions & 0 deletions case-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Case-study Задание №2

### Построить и проанализировать отчёт ruby-prof в режиме Flat
Проанализируем этим отчетом затрачиваемое время выполнения (режим wall_time)
```
Measure Mode: wall_time
Thread ID: 70208069025840
Fiber ID: 70208065413220
Total: 0.161745
Sort by: self_time

%self total self wait child calls name
19.77 0.032 0.032 0.000 0.000 20000 String#split
16.45 0.125 0.027 0.000 0.098 5 Array#each
```
Из отчета видно, что больше всего времени мы тратим на разбиение строк(`String#split`) именно эта функция первый кандидат на улучшение, если нам не будет хватать скорости обработки. Как вариант можно попробовать заменить связку `File.readlines` + `split` на парсинг `CSV`

Следующий "пожиратель" скорости это `Array#each`, который занимает 16.45% времени выполнения, но при этом вызывается всего 5 раз. Как вариант оптимизации, можно попробовать сократить один вызов `each` за счет отказа от отдельного прохода для выборки пользователей в масcив `users`, а потом обход `users` для создания `user_object`

[Полный текст отчета ](./tools/reports/ruby_prof_flat_allocations_profile.txt)


### Построить и проанализировать отчёт ruby-prof в режиме Graph
Проанализируем этим отчетом потребление процессора (режим cpu_time)

![ruby-prof в режиме Graph](./tools/reports/RubyProfGraph.png "ruby-prof в режиме Graph")

В этом отчете первый кандидат на улучшение `Array#each` 80.94% в колонке %Total. Но 16.37% в колонке %Self подсказывают, что большую часть времени происходит ожидание других методов, из которых самый ресурсоемкий это `String#split` 20.73%

[Полный текст отчета ](./tools/reports/ruby_prof_graph_allocations_profile.html)

### Построить и проанализировать отчёт ruby-prof в режиме CallStack
Еще раз проанализируем этим отчетом затрачиваемое время выполнения (режим wall_time)

![ruby-prof в режиме CallStack](./tools/reports/RubyProfCallStack.png "ruby-prof в режиме CallStack")

А из этого отчета, получается, что самый долгий метод это `collect_stats_from_users`, что идет в разрез с выводами из отчета 1!

[Полный текст отчета ](./tools/reports/ruby_prof_stack_printer_allocations_profile.html)

### Построить и проанализировать отчёт ruby-prof в режиме CallTree c визуализацией в QCachegrind
Еще раз проанализируем этим отчетом затрачиваемое время выполнения (режим wall_time)

![ruby-prof в режиме QCachegrind-1](./tools/reports/RubyProfQCachegrind-1.png "ruby-prof в режиме QCachegrind")

А из этого отчета, получается, что самый долгий метод это `parse_file` и дальше цепочка замедляющих его вызовов выглядит как `Array#each` -> `calc_stat `

![ruby-prof в режиме QCachegrind-2](./tools/reports/RubyProfQCachegrind-2.png "ruby-prof в режиме QCachegrind")

[Сырые данные отчета ](./tools/reports/profile.callgrind.out.59260)


### Профилировать работающий процесс rbspy
Результат профилирования работающей программы выглядит вот так
Copy link
Owner

Choose a reason for hiding this comment

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

👍

```
# sudo rbspy record --pid 66589
Time since start: 46s. Press Ctrl+C to stop.
Summary of profiling data so far:
% self % total name
68.70 100.00 <c function> - unknown
6.66 7.85 block in create_users_objects - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
6.07 31.39 calc_stat - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
3.58 10.17 parse_session - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
2.58 3.58 block in dates - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
1.97 19.18 block in parse_file - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
1.91 5.42 block in find_all_browsers - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
1.52 1.52 block in work - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
1.47 2.02 block in used_ie - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
1.39 1.39 block in count_browsers - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
1.13 32.52 block in collect_stats_from_users - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
0.87 1.15 parse_user - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
0.48 1.43 block in browsers_list - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
0.33 0.78 block in time_from_sesions - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
0.30 0.30 initialize - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
0.28 0.28 block in always_used_chrome - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
0.17 11.66 dates - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
0.17 1.67 time_from_sesions - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
0.15 0.37 longest_session - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
0.11 5.48 browsers - /Users/vikont/RoR_Project/thinknetica/RailsOptimization/task-2/task-2.rb
Wrote raw data to /Users/vikont/.cache/rbspy/records/rbspy-2019-03-24-6UxKcbpDNr.raw.gz
Writing formatted output to /Users/vikont/.cache/rbspy/records/rbspy-2019-03-24-6eLtsXXH6Z.flamegraph.svg
```

### Построить и проанализировать отчёт flamegraph с помощью rbspy
График дает данные, не противоречащие с отчетом ruby-prof в режиме CallStack, которые мы получили выше.
![rbspy flamegraph](./tools/reports/rbspy-flamegraph.png "rbspy flamegraph")

[Сырые данные отчета ](./tools/reports/rbspy-2019-03-24-tr8u1tpfx8.flamegraph.svg)

### Добавить в программу ProgressBar
После добавления в проект прогресбара он начал путаться в отчетах профилировщиков, поэтому его отображение я сделал опциональным. Он отображается при запуске скрипта с опцией `-p`. Данная функциональность релизованна при помощи гема `slop`.
Copy link
Owner

Choose a reason for hiding this comment

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

Хороший ход 👍


Отдельно хочу отметить, что построение сквозного прогресбара для всей программы явно не самое простое дело - надо выбрать единую метрику и как-то отслеживать ее прогресс. Теперь стало понятнее почему прогрессбары в некоторых программах могут зависать подолгу на одном проценте, а потом "пролетать" до конца. И более понятно почему многие линуксовые программы показывают отдельные прогресбары на каждую элементарную опирацию. Собственно по второму пути и пошел я.
Copy link
Owner

Choose a reason for hiding this comment

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

👍


```
ruby start.rb -p
Read data from file:: |=================================================================================================================================|
Count uniq browsers:: |=================================================================================================================================|
Create users:: |========================================================================================================================================|
Colculate stats:: |=====================================================================================================================================|
Read data from file:: |=================================================================================================================================|
Count uniq browsers:: |=================================================================================================================================|
Create users:: |========================================================================================================================================|
Colculate stats:: |=====================================================================================================================================|
```

### Научиться пользоваться Valgrind massif с massif-visualizer. Построить профиль использования памяти для итоговой версии вашей программы и добавить скриншот в PR
График утилизации памяти при помощи massif-visualizer показывает линейный рост - вот и славно!
![Valgrind massif с massif-visualizer](./tools/reports/massif-visualizer.png "Valgrind massif с massif-visualizer")

[Сырые данные отчета ](./tools/reports/massif.out.4381)
1 change: 0 additions & 1 deletion result.json

This file was deleted.

18 changes: 18 additions & 0 deletions spec/fixtures/data.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
user,0,Leida,Cira,0
session,0,0,Safari 29,87,2016-10-23
session,0,1,Firefox 12,118,2017-02-27
session,0,2,Internet Explorer 28,31,2017-03-28
session,0,3,Internet Explorer 28,109,2016-09-15
session,0,4,Safari 39,104,2017-09-27
session,0,5,Internet Explorer 35,6,2016-09-01
user,1,Palmer,Katrina,65
session,1,0,Safari 17,12,2016-10-21
session,1,1,Firefox 32,3,2016-12-20
session,1,2,Chrome 6,59,2016-11-11
session,1,3,Internet Explorer 10,28,2017-04-29
session,1,4,Chrome 13,116,2016-12-28
user,2,Gregory,Santos,86
session,2,0,Chrome 35,6,2018-09-21
session,2,1,Safari 49,85,2017-05-22
session,2,2,Firefox 47,17,2018-02-02
session,2,3,Chrome 20,84,2016-11-25
Binary file added spec/fixtures/data_large.txt.gz
Binary file not shown.
Loading