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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
result.json
data_large.txt
13 changes: 13 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require "rake/testtask"
Copy link
Owner

Choose a reason for hiding this comment

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

Интересно, первое использование rake среди сданных заданий.


Rake::TestTask.new(:test) do |t|
t.libs = %w(test)
t.pattern = 'test/**/*_test.rb'
end

Rake::TestTask.new(:bench) do |t|
t.libs = %w(test)
t.pattern = 'test/**/*_benchmark.rb'
end

task :default => :test
13 changes: 13 additions & 0 deletions benchmarks/asymptotics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require './task-2.rb'
require 'benchmark/ips'

Benchmark.ips do |bench|
bench.warmup = 0
bench.report("Process 0.0625Mb") { work('data/data_62500.txt') }
bench.report("Process 0.125Mb") { work('data/data_125k.txt') }
bench.report("Process 0.25Mb") { work('data/data_250k.txt') }
bench.report("Process 0.5Mb") { work('data/data_500k.txt') }
bench.report("Process 1Mb") { work('data/data_1m.txt') }

bench.compare!
end
7 changes: 7 additions & 0 deletions benchmarks/memprof.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require './task-2.rb'
require 'memory_profiler'

report = MemoryProfiler.report do
work('data/data_125k.txt')
end
report.pretty_print(scale_bytes: true)
17 changes: 17 additions & 0 deletions benchmarks/rubyprof.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require './task-2.rb'
require 'ruby-prof'

RubyProf.measure_mode = RubyProf::WALL_TIME

result = RubyProf.profile do
work('data/data_250k.txt', disable_gc: true)
end

# printer = RubyProf::FlatPrinter.new(result)
# printer.print(File.open("tmp/ruby_prof_flat.txt", "w+"))

# printer2 = RubyProf::GraphHtmlPrinter.new(result)
# printer2.print(File.open("ruby_prof_graph.html", "w+"))

printer3 = RubyProf::CallStackPrinter.new(result)
printer3.print(File.open("tmp/ruby_prof_callstack.html", "w+"))
12 changes: 12 additions & 0 deletions benchmarks/rubyprof_calltree.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require './task-2.rb'
require 'ruby-prof'

# stackprof tmp/stackprof.dump --text --limit 5
RubyProf.measure_mode = RubyProf::MEMORY

result = RubyProf.profile do
work('data/data_500k.txt')
end

printer = RubyProf::CallTreePrinter.new(result)
printer.print(:path => "tmp", :profile => 'calltree')
8 changes: 8 additions & 0 deletions benchmarks/stackprof.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require './task-2.rb'
require 'stackprof'

# stackprof tmp/stackprof.dump --text --limit 5
GC.disable
StackProf.run(mode: :wall, out: './tmp/stackprof.dump', raw: true) do
work('data/data_125k.txt')
end
14 changes: 14 additions & 0 deletions benchmarks/time.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'benchmark'
require './task-2.rb'

def print_memory_usage
"%d MB" % (`ps -o rss= -p #{Process.pid}`.to_i / 1024)
end

time = Benchmark.realtime do
puts "rss before work: #{print_memory_usage}"
work('data/data_large.txt')
puts "rss after work: #{print_memory_usage}"
end

puts "Finish in #{time.round(2)}"
135 changes: 121 additions & 14 deletions case-study-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,31 @@
Я решил исправить эту проблему, оптимизировав эту программу.

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

```
Calculating -------------------------------------
Process 0.0625Mb 16.293 (±12.3%) i/s - 80.000 in 5.026007s
Process 0.125Mb 4.957 (±20.2%) i/s - 25.000 in 5.107268s
Process 0.25Mb 1.379 (± 0.0%) i/s - 8.000 in 5.848466s
Process 0.5Mb 0.283 (± 0.0%) i/s - 2.000 in 7.424395s
Process 1Mb 0.040 (± 0.0%) i/s - 1.000 in 24.695813s

Comparison:
Process 0.0625Mb: 16.3 i/s
Process 0.125Mb: 5.0 i/s - 3.29x slower
Process 0.25Mb: 1.4 i/s - 11.81x slower
Process 0.5Mb: 0.3 i/s - 57.52x slower
Process 1Mb: 0.0 i/s - 402.36x slower

```
rss after work: 124 MB
Finish in 12.45

## Massif Visualizer
График построен на исходном коде с файлом объемом 250км, пиковое потребление памяти 92.3мб
[graph]: https://imgur.com/TM3Uc98

## Гарантия корректности работы оптимизированной программы
Программа поставлялась с тестом. Выполнение этого теста позволяет не допустить изменения логики программы при оптимизации.
Expand All @@ -27,20 +51,103 @@

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

### Ваша находка №1
О вашей находке №1
### Method Array.select занимает ~ 83% wall_time
Измерения: ruby-prof, режим walltime, FlatPrinter
Вывод: неопитмальный выбор типа данных
Copy link
Owner

Choose a reason for hiding this comment

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

Скорее структуры данных

Действия: замена массива session на hash с ключом user_id
Результат:
rss after work: 38 MB
Finish in 0.26
Process 0.5Mb: 4.9 i/s

### Метод Array.each занимает 99% времени
Измерения: ruby-prof, режим walltime, GraphPrinter
Вывод: дочерние вызовы этих методов написпны не оптимально
Действия: вынос дочерних вызовов в отдельные методы, показал, что 42% занмиет вызов Array.all?,
замена на Set
Результат:
rss after work: 37 MB
Finish in 0.21
Process 0.5Mb: 6.5 i/s


### collect_stats_from_users вызывается 7 раз и занимает 62%, в нем 30% приходится на Date.parse
Измерения: ruby-prof, режим walltime, CallStackPrinter
Вывод: Выбор неоптимального метода
Действия: замена на Date.strf
Результат:
rss after work: 36 MB
Finish in 0.17
Process 0.5Mb: 9.1 i/s

### parse_file занимает 30% памяти
Измерения: ruby-prof, режим memory, CallTreePrinter
Вывод: Каждый раз для пользователей создается новый массив
Действия: замена слияния массивов на push элемента
Результат:
rss after work: 34 MB
Finish in 0.15
Process 0.5Mb: 9.1 i/s

### create_users_objects занимет 30% памяти
Измерения: ruby-prof, режим memory, CallTreePrinter
Вывод: Лишнее создание объектов
Действия: удаление создания User
Результат: время обработки файла 500кб, уменьшилось с 0.19 сек до 0.16 сек, rss уменьшилось с 31мб до 27мб
rss after work: 33 MB
Finish in 0.14
Process 0.5Mb: 9.3 i/s


### В методе parse_file дочерние методы parse_session и parse_user занимают около 30-40% памяти
Измерения: ruby-prof, режим memory, CallTreePrinter
Вывод: Неоптимальная структура данных
Действия: Hash заменен на Struct
Результат:
rss after work: 34 MB
Finish in 0.14
Process 0.5Mb: 9.6 i/s

### Method to_json занимал 15% памяти
Измерения: ruby-prof, режим memory, CallTreePrinter
Вывод: Неоптималльная библотека
Действия: Замена на oj
Результат:
rss after work: 34 MB
Finish in 0.12
Process 0.5Mb: 11.5 i/s



### Method collect_user_stats занимает 30% времени
Измерения: ruby-prof, режим memory, CallTreePrinter
Вывод: Нужно ускорить метод
Действия: Много мелких оптимизаций, закешировны в переменные данные, убран ненужный парсинг дат,
убраны повторяющеся методы
Process 0.5Mb: 17.9 i/s
rss after work: 27 MB
Finish in 0.1

### Ваша находка №2
О вашей находке №2

### Ваша находка №X
О вашей находке №X

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

*Какими ещё результами можете поделиться*
Удалось улучшить метрику системы файл data_large обрабатывается за 27сек, сложность возрастает
линейно, пиковое потребление памяти сниженно в 2 раза, скорость выполнения программы выросла ~ 15
раз, потреблемая память снизилась ~ 4 раза
Copy link
Owner

Choose a reason for hiding this comment

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

👍

```
Calculating -------------------------------------
Process 0.0625Mb 227.687 (±11.0%) i/s - 1.121k in 5.000806s
Process 0.125Mb 117.790 (± 9.3%) i/s - 582.000 in 5.006790s
Process 0.25Mb 62.355 (± 4.8%) i/s - 311.000 in 5.000711s
Process 0.5Mb 24.562 (± 4.1%) i/s - 123.000 in 5.019487s
Process 1Mb 10.148 (± 9.9%) i/s - 51.000 in 5.063927s

Comparison:
Process 0.0625Mb: 227.7 i/s
Process 0.125Mb: 117.8 i/s - 1.93x slower
Process 0.25Mb: 62.4 i/s - 3.65x slower
Process 0.5Mb: 24.6 i/s - 9.27x slower
Process 1Mb: 10.1 i/s - 22.44x slower

```

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