diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..512bd052 --- /dev/null +++ b/Gemfile @@ -0,0 +1,24 @@ +source 'https://rubygems.org' + +# ruby-prof +gem 'ruby-prof' +# stackprof +gem 'stackprof' +# memory-profiler +gem 'memory-profiler' +# json +gem 'json' +# pry +gem 'pry' +# date +gem 'date' +# minitest +gem 'minitest' +# rspec +gem 'rspec' +# benchmark +gem 'benchmark' +# memory_profiler +gem 'memory_profiler' +# rspec-benchmark +gem 'rspec-benchmark' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..c67b4087 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,58 @@ +GEM + remote: https://rubygems.org/ + specs: + benchmark (0.3.0) + benchmark-malloc (0.2.0) + benchmark-perf (0.6.0) + benchmark-trend (0.4.0) + coderay (1.1.3) + date (3.3.4) + diff-lcs (1.5.1) + json (2.7.2) + memory-profiler (1.0.3) + memory_profiler (1.0.1) + method_source (1.1.0) + minitest (5.23.1) + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-benchmark (0.6.0) + benchmark-malloc (~> 0.2) + benchmark-perf (~> 0.6) + benchmark-trend (~> 0.4) + rspec (>= 3.0) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.1) + ruby-prof (1.7.0) + stackprof (0.2.26) + +PLATFORMS + arm64-darwin-23 + ruby + +DEPENDENCIES + benchmark + date + json + memory-profiler + memory_profiler + minitest + pry + rspec + rspec-benchmark + ruby-prof + stackprof + +BUNDLED WITH + 2.5.9 diff --git a/benchmark_task.rb b/benchmark_task.rb new file mode 100644 index 00000000..19dd4726 --- /dev/null +++ b/benchmark_task.rb @@ -0,0 +1,10 @@ +# Deoptimized version of homework task + +require_relative 'task-2' +require 'benchmark' + +time = Benchmark.realtime do + work('data_large.txt') +end + +puts "Программа выполнилась за #{time.round(2)} секунд" diff --git a/case-study.md b/case-study.md new file mode 100644 index 00000000..d5a7bca8 --- /dev/null +++ b/case-study.md @@ -0,0 +1,53 @@ +# Case-study оптимизации + +## Актуальная проблема +В нашем проекте возникла серьёзная проблема. + +Необходимо было обработать файл с данными, чуть больше ста мегабайт. + +У нас уже была программа на `ruby`, которая умела делать нужную обработку. + +Она успешно работала на файлах размером пару мегабайт, но для большого файла она работала слишком долго, и не было понятно, закончит ли она вообще работу за какое-то разумное время. + +Я решил исправить эту проблему, оптимизировав эту программу. + +## Формирование метрики +Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: программа должна потреблять < 70Мб памяти при обработке целевого файла data_large.tst в течение всего времени выполнения. + +## Гарантия корректности работы оптимизированной программы +Программа поставлялась с тестом. Выполнение этого теста в фидбек-лупе позволяет не допустить изменения логики программы при оптимизации. + +## Feedback-Loop +Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный `feedback-loop`, который позволил мне получать обратную связь по эффективности, сделанных измерений за время, которое бы не превышало 10 секунд. + +Вот как я построил `feedback_loop`: провел профилирование данной программы на объеме данных, обработка которых не превышала бы 10 сек. Далее по отчету профилировщика смотрю точки роста, правлю их в коде, тестируем, что ничего не поломалось и повторяем процесс заново. Поскольку с каждой итерацией время выполнения на конкретном объеме может уменьшаться, на каждом этапе корректируется(увеличиается) объем данных при профилировании + +Вначале взял файл с 30к строк, так как программа отрабатывала с этим объемом данных за 7.32 секунд. + +## Вникаем в детали системы, чтобы найти главные точки роста +Для того, чтобы найти "точки роста" для оптимизации я воспользовался несколькими инструментами: memory_profiler, +и ruby-prof в режимах flat, graph и callstack. +valgrind, к сожалению, не удалось применить, так как на процессоре m1 возникли некоторые сложности при сборке образа( + +Для начала, рерил попробовать проверить с помощью memory_profiler сколько аллоцируется в целом у нас памяти с 30к строк файла без переписывания на потоковый вход - memory_profiler показал 3.78 GB - это очень много +Соответственно, как и было написано в задании, я перевел программу на потоковый подход, чтобы файл читался и записывался построчно + +Вот какие проблемы удалось найти и решить + +### Ваша находка №1 +- Я сразу воспользовался отчетами нескольких профилировщиков. +- После увиденного в отчете, что программа потребляет огромное количество памяти при загрузке данных из файла, как и предложено было изначально в задании, переписал програму на построчный подход. Однако такой подход потребовал сильной переделки кода в программе, я попутно сразу переписал методы, где не производительно использовалась работа с массивами и циклами. +- Метрика изменилась максимально сильно! Сразу же после переписывания программы на потоковый и построчную реализацию результат стал невероятно быстрым и результаты были более чем удовлетворяющими) Для 30000 строк программа отработала за долисекунды, поэтому решил попробовать запустить программу для нужного нам файла data_large.txt, и результаты бенчмарка стали таковы: + MEMORY USAGE: 35 MB + Программа выполнилась за 8.99 секунд +Однако, как в пред. задании не получилось последовательно отслеживать на каждом шаге производительность программы при изменении конкретного метода, но удивительно, что с таким подходом изменился в корне результат. +- Отчёт профилировщика изменился так же сильно! Получилось так, что переписав программу, ничего дополнительно оптимизировать не пришлось, так как полученная метрика более чем устраивает, а остальная оптимизация сводится к тому, чтобы переделать методы IO: #foreach, write_to_file; split, ну и немного inject. +По резульатам написал тест memory_spec.rb, в котором происходит проверка на то, что программа отрабаывает не больше 10 сек, и на то, что программа не потребляет больше 45 Мб памяти при выполнении + +### Результаты + +Мне удалось обработать необходимый файл data_large.txt. Сколько изначально программа потребляла памяти - сказать затруднительно, но на 30000 строк потребяла 3.78 GB. С 3.78 GB потребление памяти удалось уменьшить до 30 Мб! При обработке всего файла занимает чуть больше, но не выходит за определенный нами в начале бюджет(< 70 Мб). +Конечный результат времени обработки файла data_large.txt при оптимизации по CPU получился - 24.783419 сек. +Конечный результат времени обработки файла data_large.txt при оптимизации по памяти получился - 8.99 секунд + +Удивительно, но оптимизация по памяти дала гораздо больший прирост по времени, чем по CPU. Т.е в 2,8 раза быстрее!) \ No newline at end of file diff --git a/memory_profiler.rb b/memory_profiler.rb new file mode 100644 index 00000000..4de263ee --- /dev/null +++ b/memory_profiler.rb @@ -0,0 +1,7 @@ +require_relative 'task-2' +require 'memory_profiler' + +report = MemoryProfiler.report do + work('data_large.txt') +end +report.pretty_print(scale_bytes: true) diff --git a/memory_spec.rb b/memory_spec.rb new file mode 100644 index 00000000..02808d06 --- /dev/null +++ b/memory_spec.rb @@ -0,0 +1,16 @@ +require 'rspec' +require 'rspec-benchmark' +require_relative 'task-2' + +RSpec.describe 'work' do + include RSpec::Benchmark::Matchers + + it 'execute less 10 sec' do + expect { work('data_large.txt') }.to perform_under(10).sec + end + + it 'memory is busy less 45 Mb' do + work('data_large.txt') + expect((`ps -o rss= -p #{Process.pid}`.to_i / 1024)).to be < 45 + end +end diff --git a/ruby_prof_reports/callstacks/callstack.html b/ruby_prof_reports/callstacks/callstack.html new file mode 100644 index 00000000..9a2b76a4 --- /dev/null +++ b/ruby_prof_reports/callstacks/callstack.html @@ -0,0 +1,1244 @@ + + + + + ruby-prof call tree + + + + + + + +
+
+ Call tree for application rubyprof_profiler.rb
Generated on 2024-06-23 16:48:40 +0300 + with options {}
+
+
+ Threshold: + + + + + +
+ + + + +
+ Thread: 260, Fiber: 240 (100.00% ~ 41.60780265502399) +
    + +
  • + +100.00% (100.00%) [global]# [1 calls, 1 total] +
      +
    • + +100.00% (100.00%) Object#work [1 calls, 1 total] +
        +
      • + +91.38% (91.38%) <Class::IO>#foreach [1 calls, 1 total] +
          +
        • + +55.29% (60.50%) Object#collect_stats_from_users [500000 calls, 500001 total] +
            +
          • + +33.74% (61.02%) Enumerable#inject [499999 calls, 500000 total] +
              +
            • + +32.81% (97.26%) Array#each [499999 calls, 500000 total] +
                +
              • + +6.13% (18.67%) Array#include? [2750932 calls, 2750940 total] +
              • +
              • + +2.00% (6.09%) Hash#[]= [8252796 calls, 8752820 total] +
              • +
              • + +1.81% (5.52%) Hash#[] [8252796 calls, 8752820 total] +
              • +
              • + +1.35% (4.10%) Integer#+ [8252796 calls, 8752820 total] +
              • +
              • + +1.08% (3.29%) Array#<< [5502064 calls, 8253020 total] +
              • + + + + +
              +
            • +
            +
          • +
          • + +10.93% (19.76%) Object#write_to_file [499999 calls, 500000 total] +
              +
            • + +2.87% (26.27%) <Module::JSON>#generate [499999 calls, 500001 total] +
                +
              • + +1.48% (51.62%) JSON::Ext::Generator::State#generate [499999 calls, 500001 total] +
              • + + +
              +
            • +
            • + +1.42% (13.01%) IO#write [999997 calls, 1000004 total] +
            • +
            • + +1.20% (10.99%) String#[] [1499997 calls, 1500000 total] +
            • + + + + + +
            +
          • +
          • + +1.53% (2.77%) Array#any? [499999 calls, 500000 total] +
              + +
            +
          • + + + + + + + + + + + +
          +
        • +
        • + +19.56% (21.41%) String#split [3250940 calls, 3250940 total] +
        • +
        • + +5.09% (5.57%) Object#parse_session [2750940 calls, 2750940 total] +
            +
          • + +1.90% (37.37%) Array#[] [13754700 calls, 19005640 total] +
          • +
          +
        • + + + + + + +
        +
      • +
      • + +8.61% (8.61%) Kernel#` [1 calls, 1 total] +
      • + + + + + + + + + + + + + + +
      +
    • + +
    +
  • + +
+
+ +
+
+ + diff --git a/ruby_prof_reports/calltrees/callgrind.out.73457 b/ruby_prof_reports/calltrees/callgrind.out.73457 new file mode 100644 index 00000000..2af428ea --- /dev/null +++ b/ruby_prof_reports/calltrees/callgrind.out.73457 @@ -0,0 +1,522 @@ +events: wall_time + +fl= +fn=String::% +0 18 + +fl= +fn=Integer::/ +0 9 + +fl= +fn=Kernel::` +0 12658226 + +fl= +fn=Process::pid^ +0 1 + +fl= +fn=IO::puts +0 40 +cfl= +cfn=IO::write +calls=2 0 +0 50 + +fl= +fn=Kernel::puts +0 13 +cfl= +cfn=IO::puts +calls=2 0 +0 90 + +fl= +fn=IO::close +0 166 + +fl= +fn=String::to_s +0 7 + +fl= +fn=String::slice +0 1 + +fl= +fn=Array::size +0 6 + +fl= +fn=Array::clear +0 39805 + +fl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +fn=Object::clear_data +109 197435 +cfl= +cfn=User::sessions +calls=500000 110 +110 41551 +cfl= +cfn=Array::clear +calls=500000 110 +110 39805 +cfl= +cfn=User::attributes= +calls=500000 111 +111 43823 + +fl= +fn=Integer::- +0 40191 + +fl= +fn=String::length +0 60060 + +fl= +fn=String::== +0 83047 + +fl= +fn=String::[] +0 1149260 + +fl= +fn=JSON/Ext/Generator/State::generate +0 662339 + +fl= +fn=JSON/Ext/Generator/State::initialize +0 36470 + +fl= +fn=Module::=== +0 39718 + +fl=/Users/aiufrolov/.asdf/installs/ruby/3.3.1/lib/ruby/gems/3.3.0/gems/json-2.7.2/lib/json/common.rb +fn=JSON::generate^ +300 223211 +cfl= +cfn=Module::=== +calls=500001 301 +301 39718 +cfl= +cfn=Class::new +calls=500001 304 +304 643105 +cfl= +cfn=JSON/Ext/Generator/State::generate +calls=500001 306 +306 662339 + +fl= +fn=Integer::> +0 39196 + +fl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +fn=Object::write_to_file +100 3656766 +cfl= +cfn=Integer::> +calls=500000 101 +101 39196 +cfl= +cfn=IO::write +calls=1000000 101 +101 1078564 +cfl= +cfn=User::attributes +calls=1000000 103 +103 76386 +cfl=/Users/aiufrolov/.asdf/installs/ruby/3.3.1/lib/ruby/gems/3.3.0/gems/json-2.7.2/lib/json/common.rb +cfn=JSON::generate^ +calls=500000 104 +104 1568354 +cfl= +cfn=String::[] +calls=1500000 105 +105 1149260 +cfl= +cfn=String::== +calls=1000000 105 +105 83047 +cfl= +cfn=String::length +calls=500000 105 +105 60060 +cfl= +cfn=Integer::- +calls=500000 105 +105 40191 + +fl= +fn=Array::reverse! +0 42791 + +fl= +fn=Array::sort! +0 242966 + +fl= +fn=Array::join +0 169180 + +fl= +fn=Array::sort +0 433695 + +fl= +fn=Array::all? +0 126926 +cfl= +cfn=String::=~ +calls=643788 89 +89 276031 + +fl= +fn=String::=~ +0 1017442 + +fl= +fn=Array::any? +0 240720 +cfl= +cfn=String::=~ +calls=1431949 87 +87 741411 + +fl= +fn=BasicObject::! +0 162080 + +fl= +fn=Array::include? +0 2603781 + +fl= +fn=String::upcase! +0 252762 + +fl= +fn=Integer::< +0 188595 + +fl= +fn=String::to_i +0 246197 + +fl= +fn=Hash::[]= +0 899158 + +fl= +fn=Integer::+ +0 644026 + +fl= +fn=Hash::[] +0 821752 + +fl= +fn=Array::each +0 12803321 +cfl= +cfn=Hash::[] +calls=8252820 72 +72 773790 +cfl= +cfn=Integer::+ +calls=8252820 72 +72 607370 +cfl= +cfn=Hash::[]= +calls=8252820 72 +72 847131 +cfl= +cfn=String::to_i +calls=2750940 73 +73 246175 +cfl= +cfn=Integer::< +calls=2750940 75 +75 188595 +cfl= +cfn=String::upcase! +calls=2750940 76 +76 252762 +cfl= +cfn=Array::<< +calls=5501880 76 +76 480907 +cfl= +cfn=Array::include? +calls=2750940 79 +79 2603781 +cfl= +cfn=BasicObject::! +calls=2750940 79 +79 162080 + +fl= +fn=Enumerable::inject +0 1033247 +cfl= +cfn=Array::each +calls=500000 0 +0 18965912 + +fl= +fn=Array::<< +0 693777 + +fl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +fn=Object::parse_session +24 1338090 +cfl= +cfn=Array::[] +calls=13754700 26 +26 795229 + +fl= +fn=User::sessions +0 296059 + +fl= +fn=User::attributes= +0 83408 + +fl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +fn=Object::parse_user +15 224831 +cfl= +cfn=Array::[] +calls=2000000 17 +17 146635 + +fl= +fn=User::attributes +0 111092 + +fl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +fn=Object::collect_stats_from_users +56 4859738 +cfl= +cfn=User::attributes +calls=500001 57 +57 34706 +cfl= +cfn=User::sessions +calls=500000 59 +59 38111 +cfl= +cfn=Enumerable::inject +calls=500000 59 +59 19999159 +cfl= +cfn=Hash::[] +calls=500000 85 +85 47962 +cfl= +cfn=Integer::+ +calls=500000 85 +85 36656 +cfl= +cfn=Hash::[]= +calls=500000 85 +85 52026 +cfl= +cfn=Array::any? +calls=500000 87 +87 982131 +cfl= +cfn=Array::all? +calls=500000 89 +89 402957 +cfl= +cfn=Array::sort +calls=500000 93 +93 433695 +cfl= +cfn=Array::join +calls=500000 93 +93 169180 +cfl= +cfn=Array::sort! +calls=500000 94 +94 242966 +cfl= +cfn=Array::reverse! +calls=500000 94 +94 42791 +cfl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +cfn=Object::write_to_file +calls=500000 96 +96 7751823 +cfl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +cfn=Object::clear_data +calls=500000 97 +97 322614 + +fl= +fn=Array::[] +0 1197494 + +fl= +fn=String::split +0 21592194 + +fl= +fn=String::chomp +0 326181 + +fl= +fn=IO::foreach^ +0 5317128 +cfl= +cfn=String::chomp +calls=3250940 121 +121 326181 +cfl= +cfn=String::split +calls=3250940 121 +121 21592194 +cfl= +cfn=Array::[] +calls=3250940 123 +123 255631 +cfl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +cfn=Object::collect_stats_from_users +calls=500000 127 +127 35416434 +cfl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +cfn=Object::parse_user +calls=500000 129 +129 371466 +cfl= +cfn=User::attributes= +calls=500000 129 +129 39585 +cfl= +cfn=User::sessions +calls=2750940 125 +125 216397 +cfl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +cfn=Object::parse_session +calls=2750940 125 +125 2133319 +cfl= +cfn=Array::<< +calls=2750940 125 +125 212871 + +fl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +fn=User::initialize +9 1 + +fl= +fn=Class::new +0 606639 +cfl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +cfn=User::initialize +calls=1 0 +0 1 +cfl= +cfn=JSON/Ext/Generator/State::initialize +calls=500001 0 +0 36470 + +fl= +fn=IO::write +0 1078643 + +fl= +fn=File::initialize +0 1645 + +fl= +fn=IO::open^ +0 6 +cfl= +cfn=File::initialize +calls=1 0 +0 1645 + +fl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +fn=Object::work +114 60 +cfl= +cfn=IO::open^ +calls=1 115 +115 1651 +cfl= +cfn=IO::write +calls=3 116 +116 28 +cfl= +cfn=Class::new +calls=1 118 +118 4 +cfl= +cfn=IO::foreach^ +calls=1 120 +120 65881204 +cfl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +cfn=Object::collect_stats_from_users +calls=1 133 +133 82 +cfl= +cfn=Array::size +calls=1 136 +136 6 +cfl=/Users/aiufrolov/.asdf/installs/ruby/3.3.1/lib/ruby/gems/3.3.0/gems/json-2.7.2/lib/json/common.rb +cfn=JSON::generate^ +calls=1 140 +140 20 +cfl= +cfn=String::slice +calls=1 140 +140 1 +cfl= +cfn=String::to_s +calls=1 140 +140 7 +cfl= +cfn=IO::close +calls=1 141 +141 166 +cfl= +cfn=Kernel::puts +calls=2 143 +143 102 +cfl= +cfn=Process::pid^ +calls=1 144 +144 1 +cfl= +cfn=Kernel::` +calls=1 144 +144 12658226 +cfl= +cfn=String::to_i +calls=1 144 +144 22 +cfl= +cfn=Integer::/ +calls=1 144 +144 9 +cfl= +cfn=String::% +calls=1 144 +144 18 + +fl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/rubyprof_profiler.rb +fn=[global]:: +25 49 +cfl=/Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb +cfn=Object::work +calls=1 25 +25 78541607 + diff --git a/ruby_prof_reports/flats/flat.txt b/ruby_prof_reports/flats/flat.txt new file mode 100644 index 00000000..3d8f1d33 --- /dev/null +++ b/ruby_prof_reports/flats/flat.txt @@ -0,0 +1,84 @@ +Measure Mode: wall_time +Thread ID: 260 +Fiber ID: 240 +Total: 41.607803 +Sort by: self_time + + %self total self wait child calls name location + 19.56 8.140 8.140 0.000 0.000 3250940 String#split + 18.43 13.652 7.669 0.000 5.983 500000 Array#each + 8.61 3.581 3.581 0.000 0.000 1 Kernel#` + 8.08 38.021 3.361 0.000 34.661 1 #foreach + 6.13 2.549 2.549 0.000 0.000 2750940 Array#include? + 5.71 23.005 2.376 0.000 20.629 500001 Object#collect_stats_from_users /Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb:56 + 4.74 4.546 1.970 0.000 2.576 500000 Object#write_to_file /Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb:100 + 3.19 2.117 1.326 0.000 0.791 2750940 Object#parse_session /Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb:24 + 2.85 1.186 1.186 0.000 0.000 19005640 Array#[] + 2.12 0.881 0.881 0.000 0.000 8752820 Hash#[]= + 1.92 0.799 0.799 0.000 0.000 8752820 Hash#[] + 1.59 0.662 0.662 0.000 0.000 8253020 Array#<< + 1.48 0.616 0.616 0.000 0.000 500001 JSON::Ext::Generator::State#generate + 1.43 0.596 0.596 0.000 0.000 8752820 Integer#+ + 1.42 0.592 0.592 0.000 0.000 1000004 IO#write + 1.39 0.577 0.577 0.000 0.000 2075737 String#=~ + 1.20 0.500 0.500 0.000 0.000 1500000 String#[] + 0.93 14.037 0.385 0.000 13.652 500000 Enumerable#inject + 0.78 0.324 0.324 0.000 0.000 3250940 String#chomp + 0.70 0.290 0.290 0.000 0.000 3750940 User#sessions + 0.69 0.322 0.287 0.000 0.035 500002 Class#new + 0.60 0.248 0.248 0.000 0.000 2750941 String#to_i + 0.58 0.243 0.243 0.000 0.000 2750940 String#upcase! + 0.58 0.239 0.239 0.000 0.000 500000 Array#sort + 0.56 0.638 0.234 0.000 0.404 500000 Array#any? + 0.53 0.366 0.221 0.000 0.144 500000 Object#parse_user /Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb:15 + 0.52 1.194 0.215 0.000 0.979 500001 #generate /Users/aiufrolov/.asdf/installs/ruby/3.3.1/lib/ruby/gems/3.3.0/gems/json-2.7.2/lib/json/common.rb:300 + 0.45 0.312 0.189 0.000 0.123 500000 Object#clear_data /Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb:109 + 0.45 0.188 0.188 0.000 0.000 2750940 Integer#< + 0.42 0.173 0.173 0.000 0.000 500000 Array#sort! + 0.39 0.161 0.161 0.000 0.000 2750940 BasicObject#! + 0.32 0.134 0.134 0.000 0.000 500000 Array#join + 0.31 0.301 0.128 0.000 0.174 500000 Array#all? + 0.26 0.107 0.107 0.000 0.000 1500001 User#attributes + 0.19 0.079 0.079 0.000 0.000 1000000 User#attributes= + 0.18 0.077 0.077 0.000 0.000 1000000 String#== + 0.14 0.059 0.059 0.000 0.000 500000 String#length + 0.10 0.041 0.041 0.000 0.000 500000 Array#reverse! + 0.10 0.041 0.041 0.000 0.000 500000 Integer#> + 0.10 0.041 0.041 0.000 0.000 500000 Array#clear + 0.10 0.041 0.041 0.000 0.000 500001 Module#=== + 0.10 0.040 0.040 0.000 0.000 500000 Integer#- + 0.08 0.035 0.035 0.000 0.000 500001 JSON::Ext::Generator::State#initialize + 0.01 0.004 0.004 0.000 0.000 1 File#initialize + 0.00 0.000 0.000 0.000 0.000 1 IO#close + 0.00 41.608 0.000 0.000 41.608 1 Object#work /Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb:114 + 0.00 0.000 0.000 0.000 0.000 2 IO#puts + 0.00 41.608 0.000 0.000 41.608 1 [global]# rubyprof_profiler.rb:7 + 0.00 0.000 0.000 0.000 0.000 2 Kernel#puts + 0.00 0.000 0.000 0.000 0.000 1 String#to_s + 0.00 0.000 0.000 0.000 0.000 1 Array#size + 0.00 0.000 0.000 0.000 0.000 1 Integer#/ + 0.00 0.000 0.000 0.000 0.000 1 String#% + 0.00 0.004 0.000 0.000 0.004 1 #open + 0.00 0.000 0.000 0.000 0.000 1 String#slice + 0.00 0.000 0.000 0.000 0.000 1 #disable :69 + 0.00 0.000 0.000 0.000 0.000 1 User#initialize /Users/aiufrolov/Desktop/optimization_rails/rails-optimization-task2/task-2.rb:9 + 0.00 0.000 0.000 0.000 0.000 1 #pid + +* recursively called methods + +Columns are: + + %self - The percentage of time spent in this method, derived from self_time/total_time. + total - The time spent in this method and its children. + self - The time spent in this method. + wait - The amount of time this method waited for other threads. + child - The time spent in this method's children. + calls - The number of times this method was called. + name - The name of the method. + location - The location of the method. + +The interpretation of method names is: + + * MyObject#test - An instance method "test" of the class "MyObject" + * #test - The <> characters indicate a method on a singleton class. + diff --git a/ruby_prof_reports/graphs/graph.html b/ruby_prof_reports/graphs/graph.html new file mode 100644 index 00000000..e5d4811a --- /dev/null +++ b/ruby_prof_reports/graphs/graph.html @@ -0,0 +1,4023 @@ + + + + + + +
+
+
+
Profile Report
+

Wall_time

+
+
+
Sunday, June 23 at 4:48:40 PM (MSK)
+ +
+
+
+
+ + + + + + + + + + + + + +
Thread IDFiber IDTotal
26024041.60780265502399
+ + + +

Thread 260, Fiber: 240

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
%Total%SelfTotalSelfWaitChildCallsNameLine
100.00%0.00%41.610.000.0041.611 + + [global]# + + 7
  41.610.000.0041.611/1Object#work8 +
  0.000.000.000.001/1<Module::GC>#disable7 +
  41.610.000.0041.611/1[global]#8
100.00%0.00%41.610.000.0041.611 + + Object#work + + 114
  38.023.360.0034.661/1<Class::IO>#foreach120 +
  3.583.580.000.001/1Kernel#`144 +
  0.000.000.000.001/1<Class::IO>#open115 +
  0.000.000.000.001/1IO#close141 +
  0.000.000.000.001/500001Object#collect_stats_from_users133 +
  0.000.000.000.002/2Kernel#puts143 +
  0.000.000.000.001/2750941String#to_i144 +
  0.000.000.000.003/1000004IO#write116 +
  0.000.000.000.001/500001<Module::JSON>#generate140 +
  0.000.000.000.001/1String#to_s140 +
  0.000.000.000.001/1Array#size136 +
  0.000.000.000.001/1Integer#/144 +
  0.000.000.000.001/1String#%144 +
  0.000.000.000.001/500002Class#new118 +
  0.000.000.000.001/1String#slice140 +
  0.000.000.000.001/1<Module::Process>#pid144 +
  38.023.360.0034.661/1Object#work120
91.38%8.08%38.023.360.0034.661 + + <Class::IO>#foreach + +
  23.002.380.0020.63500000/500001Object#collect_stats_from_users127 +
  8.148.140.000.003250940/3250940String#split121 +
  2.121.330.000.792750940/2750940Object#parse_session125 +
  0.370.220.000.14500000/500000Object#parse_user129 +
  0.320.320.000.003250940/3250940String#chomp121 +
  0.250.250.000.003250940/19005640Array#[]123 +
  0.210.210.000.002750940/8253020Array#<<125 +
  0.210.210.000.002750940/3750940User#sessions125 +
  0.040.040.000.00500000/1000000User#attributes=129 +
  0.000.000.000.001/500001Object#work133
  23.002.380.0020.63500000/500001<Class::IO>#foreach
55.29%5.71%23.002.380.0020.63500001 + + Object#collect_stats_from_users + + 56
  14.040.390.0013.65500000/500000Enumerable#inject59 +
  4.551.970.002.58500000/500000Object#write_to_file96 +
  0.640.230.000.40500000/500000Array#any?87 +
  0.310.190.000.12500000/500000Object#clear_data97 +
  0.300.130.000.17500000/500000Array#all?89 +
  0.240.240.000.00500000/500000Array#sort93 +
  0.170.170.000.00500000/500000Array#sort!94 +
  0.130.130.000.00500000/500000Array#join93 +
  0.050.050.000.00500000/8752820Hash#[]=85 +
  0.050.050.000.00500000/8752820Hash#[]85 +
  0.040.040.000.00500000/500000Array#reverse!94 +
  0.040.040.000.00500000/3750940User#sessions59 +
  0.040.040.000.00500000/8752820Integer#+85 +
  0.030.030.000.00500001/1500001User#attributes57 +
  14.040.390.0013.65500000/500000Object#collect_stats_from_users59
33.74%0.93%14.040.390.0013.65500000 + + Enumerable#inject + +
  13.657.670.005.98500000/500000Array#each +
  13.657.670.005.98500000/500000Enumerable#inject
32.81%18.43%13.657.670.005.98500000 + + Array#each + +
  2.552.550.000.002750940/2750940Array#include?79 +
  0.830.830.000.008252820/8752820Hash#[]=72 +
  0.750.750.000.008252820/8752820Hash#[]72 +
  0.560.560.000.008252820/8752820Integer#+72 +
  0.450.450.000.005502080/8253020Array#<<76 +
  0.250.250.000.002750940/2750941String#to_i73 +
  0.240.240.000.002750940/2750940String#upcase!76 +
  0.190.190.000.002750940/2750940Integer#<75 +
  0.160.160.000.002750940/2750940BasicObject#!79 +
  8.148.140.000.003250940/3250940<Class::IO>#foreach
19.56%19.56%8.148.140.000.003250940 + + String#split + +
  4.551.970.002.58500000/500000Object#collect_stats_from_users96
10.93%4.74%4.551.970.002.58500000 + + Object#write_to_file + + 100
  1.190.210.000.98500000/500001<Module::JSON>#generate104 +
  0.590.590.000.00999999/1000004IO#write106 +
  0.500.500.000.001500000/1500000String#[]105 +
  0.080.080.000.001000000/1000000String#==105 +
  0.070.070.000.001000000/1500001User#attributes103 +
  0.060.060.000.00500000/500000String#length105 +
  0.040.040.000.00500000/500000Integer#>101 +
  0.040.040.000.00500000/500000Integer#-105 +
  3.583.580.000.001/1Object#work144
8.61%8.61%3.583.580.000.001 + + Kernel#` + +
  2.552.550.000.002750940/2750940Array#each
6.13%6.13%2.552.550.000.002750940 + + Array#include? + +
  2.121.330.000.792750940/2750940<Class::IO>#foreach
5.09%3.19%2.121.330.000.792750940 + + Object#parse_session + + 24
  0.790.790.000.0013754700/19005640Array#[]26 +
  0.000.000.000.001/500001Object#work140
  1.190.210.000.98500000/500001Object#write_to_file104
2.87%0.52%1.190.210.000.98500001 + + <Module::JSON>#generate + + 300
  0.620.620.000.00500001/500001JSON::Ext::Generator::State#generate306 +
  0.320.290.000.04500001/500002Class#new304 +
  0.040.040.000.00500001/500001Module#===301 +
  0.140.140.000.002000000/19005640Object#parse_user17
  0.250.250.000.003250940/19005640<Class::IO>#foreach
  0.790.790.000.0013754700/19005640Object#parse_session26
2.85%2.85%1.191.190.000.0019005640 + + Array#[] + +
  0.050.050.000.00500000/8752820Object#collect_stats_from_users85
  0.830.830.000.008252820/8752820Array#each
2.12%2.12%0.880.880.000.008752820 + + Hash#[]= + +
  0.050.050.000.00500000/8752820Object#collect_stats_from_users85
  0.750.750.000.008252820/8752820Array#each
1.92%1.92%0.800.800.000.008752820 + + Hash#[] + +
  0.210.210.000.002750940/8253020<Class::IO>#foreach
  0.450.450.000.005502080/8253020Array#each
1.59%1.59%0.660.660.000.008253020 + + Array#<< + +
  0.640.230.000.40500000/500000Object#collect_stats_from_users87
1.53%0.56%0.640.230.000.40500000 + + Array#any? + +
  0.400.400.000.001431949/2075737String#=~87 +
  0.620.620.000.00500001/500001<Module::JSON>#generate306
1.48%1.48%0.620.620.000.00500001 + + JSON::Ext::Generator::State#generate + +
  0.040.040.000.00500000/8752820Object#collect_stats_from_users85
  0.560.560.000.008252820/8752820Array#each
1.43%1.43%0.600.600.000.008752820 + + Integer#+ + +
  0.000.000.000.003/1000004Object#work116
  0.000.000.000.002/1000004IO#puts
  0.590.590.000.00999999/1000004Object#write_to_file106
1.42%1.42%0.590.590.000.001000004 + + IO#write + +
  0.170.170.000.00643788/2075737Array#all?
  0.400.400.000.001431949/2075737Array#any?
1.39%1.39%0.580.580.000.002075737 + + String#=~ + +
  0.500.500.000.001500000/1500000Object#write_to_file105
1.20%1.20%0.500.500.000.001500000 + + String#[] + +
  0.370.220.000.14500000/500000<Class::IO>#foreach
0.88%0.53%0.370.220.000.14500000 + + Object#parse_user + + 15
  0.140.140.000.002000000/19005640Array#[]17 +
  0.320.320.000.003250940/3250940<Class::IO>#foreach
0.78%0.78%0.320.320.000.003250940 + + String#chomp + +
  0.000.000.000.001/500002Object#work118
  0.320.290.000.04500001/500002<Module::JSON>#generate304
0.77%0.69%0.320.290.000.04500002 + + Class#new + +
  0.040.040.000.00500001/500001JSON::Ext::Generator::State#initialize +
  0.000.000.000.001/1User#initialize +
  0.310.190.000.12500000/500000Object#collect_stats_from_users97
0.75%0.45%0.310.190.000.12500000 + + Object#clear_data + + 109
  0.040.040.000.00500000/3750940User#sessions110 +
  0.040.040.000.00500000/1000000User#attributes=111 +
  0.040.040.000.00500000/500000Array#clear110 +
  0.300.130.000.17500000/500000Object#collect_stats_from_users89
0.72%0.31%0.300.130.000.17500000 + + Array#all? + +
  0.170.170.000.00643788/2075737String#=~89 +
  0.040.040.000.00500000/3750940Object#collect_stats_from_users59
  0.040.040.000.00500000/3750940Object#clear_data110
  0.210.210.000.002750940/3750940<Class::IO>#foreach
0.70%0.70%0.290.290.000.003750940 + + User#sessions + +
  0.000.000.000.001/2750941Object#work144
  0.250.250.000.002750940/2750941Array#each
0.60%0.60%0.250.250.000.002750941 + + String#to_i + +
  0.240.240.000.002750940/2750940Array#each
0.58%0.58%0.240.240.000.002750940 + + String#upcase! + +
  0.240.240.000.00500000/500000Object#collect_stats_from_users93
0.58%0.58%0.240.240.000.00500000 + + Array#sort + +
  0.190.190.000.002750940/2750940Array#each
0.45%0.45%0.190.190.000.002750940 + + Integer#< + +
  0.170.170.000.00500000/500000Object#collect_stats_from_users94
0.42%0.42%0.170.170.000.00500000 + + Array#sort! + +
  0.160.160.000.002750940/2750940Array#each
0.39%0.39%0.160.160.000.002750940 + + BasicObject#! + +
  0.130.130.000.00500000/500000Object#collect_stats_from_users93
0.32%0.32%0.130.130.000.00500000 + + Array#join + +
  0.030.030.000.00500001/1500001Object#collect_stats_from_users57
  0.070.070.000.001000000/1500001Object#write_to_file103
0.26%0.26%0.110.110.000.001500001 + + User#attributes + +
  0.040.040.000.00500000/1000000<Class::IO>#foreach
  0.040.040.000.00500000/1000000Object#clear_data111
0.19%0.19%0.080.080.000.001000000 + + User#attributes= + +
  0.080.080.000.001000000/1000000Object#write_to_file105
0.18%0.18%0.080.080.000.001000000 + + String#== + +
  0.060.060.000.00500000/500000Object#write_to_file105
0.14%0.14%0.060.060.000.00500000 + + String#length + +
  0.040.040.000.00500000/500000Object#collect_stats_from_users94
0.10%0.10%0.040.040.000.00500000 + + Array#reverse! + +
  0.040.040.000.00500000/500000Object#write_to_file101
0.10%0.10%0.040.040.000.00500000 + + Integer#> + +
  0.040.040.000.00500001/500001<Module::JSON>#generate301
0.10%0.10%0.040.040.000.00500001 + + Module#=== + +
  0.040.040.000.00500000/500000Object#clear_data110
0.10%0.10%0.040.040.000.00500000 + + Array#clear + +
  0.040.040.000.00500000/500000Object#write_to_file105
0.10%0.10%0.040.040.000.00500000 + + Integer#- + +
  0.040.040.000.00500001/500001Class#new
0.08%0.08%0.040.040.000.00500001 + + JSON::Ext::Generator::State#initialize + +
  0.000.000.000.001/1Object#work115
0.01%0.00%0.000.000.000.001 + + <Class::IO>#open + +
  0.000.000.000.001/1File#initialize +
  0.000.000.000.001/1<Class::IO>#open
0.01%0.01%0.000.000.000.001 + + File#initialize + +
  0.000.000.000.001/1Object#work141
0.00%0.00%0.000.000.000.001 + + IO#close + +
  0.000.000.000.001/1[global]#7
0.00%0.00%0.000.000.000.001 + + <Module::GC>#disable + + 69
  0.000.000.000.001/1Object#work140
0.00%0.00%0.000.000.000.001 + + String#to_s + +
  0.000.000.000.001/1Object#work140
0.00%0.00%0.000.000.000.001 + + String#slice + +
  0.000.000.000.001/1Object#work144
0.00%0.00%0.000.000.000.001 + + String#% + +
  0.000.000.000.002/2Object#work143
0.00%0.00%0.000.000.000.002 + + Kernel#puts + +
  0.000.000.000.002/2IO#puts +
  0.000.000.000.001/1Object#work144
0.00%0.00%0.000.000.000.001 + + Integer#/ + +
  0.000.000.000.001/1Object#work136
0.00%0.00%0.000.000.000.001 + + Array#size + +
  0.000.000.000.001/1Object#work144
0.00%0.00%0.000.000.000.001 + + <Module::Process>#pid + +
  0.000.000.000.001/1Class#new
0.00%0.00%0.000.000.000.001 + + User#initialize + + 9
  0.000.000.000.002/2Kernel#puts
0.00%0.00%0.000.000.000.002 + + IO#puts + +
  0.000.000.000.002/1000004IO#write +
* indicates recursively called methods
+ +
+ + + + diff --git a/rubyprof_profiler.rb b/rubyprof_profiler.rb new file mode 100644 index 00000000..eb4670f8 --- /dev/null +++ b/rubyprof_profiler.rb @@ -0,0 +1,29 @@ +require 'ruby-prof' +require_relative 'task-2' + +RubyProf.measure_mode = RubyProf::ALLOCATIONS + +result = RubyProf::Profile.profile do + GC.disable + work('data_large.txt') +end + +# Профилирование по аллокациям +printer = RubyProf::FlatPrinter.new(result) +printer.print(File.open('ruby_prof_reports/flats/flat.txt', 'w+')) + +printer = RubyProf::GraphHtmlPrinter.new(result) +printer.print(File.open('ruby_prof_reports/graphs/graph.html', 'w+')) + +printer = RubyProf::CallStackPrinter.new(result) +printer.print(File.open('ruby_prof_reports/callstacks/callstack.html', 'w+')) + +# Профилирование по объёму памяти +RubyProf.measure_mode = RubyProf::MEMORY + +result = RubyProf::Profile.profile do + work('data_large.txt') +end + +printer = RubyProf::CallTreePrinter.new(result) +printer.print(path: 'ruby_prof_reports/calltrees', profile: 'profile') diff --git a/stackprof_profiler.rb b/stackprof_profiler.rb new file mode 100644 index 00000000..86e63d14 --- /dev/null +++ b/stackprof_profiler.rb @@ -0,0 +1,7 @@ +require 'stackprof' +require_relative 'task-2' + +StackProf.run(mode: :object, out: 'stackprof_reports/stackprof.dump', raw: true) do + GC.disable + work('data30000.txt') +end diff --git a/stackprof_reports/stackprof.dump b/stackprof_reports/stackprof.dump new file mode 100644 index 00000000..22652a7f Binary files /dev/null and b/stackprof_reports/stackprof.dump differ diff --git a/task-2.rb b/task-2.rb index 34e09a3c..92656524 100644 --- a/task-2.rb +++ b/task-2.rb @@ -1,12 +1,8 @@ -# Deoptimized version of homework task - require 'json' -require 'pry' -require 'date' require 'minitest/autorun' class User - attr_reader :attributes, :sessions + attr_accessor :attributes, :sessions def initialize(attributes:, sessions:) @attributes = attributes @@ -14,9 +10,8 @@ def initialize(attributes:, sessions:) end end -def parse_user(user) - fields = user.split(',') - parsed_result = { +def parse_user(fields) + { 'id' => fields[1], 'first_name' => fields[2], 'last_name' => fields[3], @@ -24,9 +19,8 @@ def parse_user(user) } end -def parse_session(session) - fields = session.split(',') - parsed_result = { +def parse_session(fields) + { 'user_id' => fields[1], 'session_id' => fields[2], 'browser' => fields[3], @@ -35,115 +29,125 @@ def parse_session(session) } end -def collect_stats_from_users(report, users_objects, &block) - users_objects.each do |user| - user_key = "#{user.attributes['first_name']}" + ' ' + "#{user.attributes['last_name']}" - report['usersStats'][user_key] ||= {} - report['usersStats'][user_key] = report['usersStats'][user_key].merge(block.call(user)) - end -end - -def work - file_lines = File.read('data.txt').split("\n") - - users = [] - sessions = [] - - file_lines.each do |line| - cols = line.split(',') - users = users + [parse_user(line)] if cols[0] == 'user' - sessions = sessions + [parse_session(line)] if cols[0] == 'session' +# Общая статистика по юзерам +$general_info = { + 'totalUsers' => 0, + 'uniqueBrowsersCount' => 0, + 'totalSessions' => 0, + 'allBrowsers' => [] +} + +# Отчёт в json +# - Сколько всего юзеров + +# - Сколько всего уникальных браузеров + +# - Сколько всего сессий + +# - Перечислить уникальные браузеры в алфавитном порядке через запятую и капсом + +# +# - По каждому пользователю +# - сколько всего сессий + +# - сколько всего времени + +# - самая длинная сессия + +# - браузеры через запятую + +# - Хоть раз использовал IE? + +# - Всегда использовал только Хром? + +# - даты сессий в порядке убывания через запятую + +def collect_stats_from_users(user, result_file) + return unless user.attributes + + stats = user.sessions.inject({ + # Собираем количество сессий по пользователям + 'sessionsCount' => 0, + # Собираем количество времени по пользователям + 'totalTime' => 0, + # Выбираем самую длинную сессию пользователя + 'longestSession' => 0, + # Браузеры пользователя через запятую + 'browsers' => [], + # Даты сессий через запятую в обратном порядке в формате iso8601 + 'dates' => [] + }) do |acc, session| + # Собираем количество сессий по пользователям + acc['sessionsCount'] += 1 + session_time = session['time'].to_i + acc['totalTime'] += session_time + acc['longestSession'] = session_time if acc['longestSession'] < session_time + acc['browsers'] << session['browser'].upcase! + acc['dates'] << session['date'] + + $general_info['allBrowsers'] << session['browser'] if !$general_info['allBrowsers'].include?(session['browser']) + acc end - # Отчёт в json - # - Сколько всего юзеров + - # - Сколько всего уникальных браузеров + - # - Сколько всего сессий + - # - Перечислить уникальные браузеры в алфавитном порядке через запятую и капсом + - # - # - По каждому пользователю - # - сколько всего сессий + - # - сколько всего времени + - # - самая длинная сессия + - # - браузеры через запятую + - # - Хоть раз использовал IE? + - # - Всегда использовал только Хром? + - # - даты сессий в порядке убывания через запятую + - - report = {} - - report[:totalUsers] = users.count - - # Подсчёт количества уникальных браузеров - uniqueBrowsers = [] - sessions.each do |session| - browser = session['browser'] - uniqueBrowsers += [browser] if uniqueBrowsers.all? { |b| b != browser } - end + # Хоть раз использовал IE? + stats['usedIE'] = stats['browsers'].any? { |b| b =~ /INTERNET EXPLORER/ } + # Всегда использовал только Chrome? + stats['alwaysUsedChrome'] = stats['browsers'].all? { |b| b =~ /CHROME/ } - report['uniqueBrowsersCount'] = uniqueBrowsers.count + stats['totalTime'] = "#{stats['totalTime']} min." + stats['longestSession'] = "#{stats['longestSession']} min." + stats['browsers'] = stats['browsers'].sort.join(', ') + stats['dates'].sort!.reverse! - report['totalSessions'] = sessions.count + write_to_file(user, stats, result_file) + clear_data(user) +end - report['allBrowsers'] = - sessions - .map { |s| s['browser'] } - .map { |b| b.upcase } - .sort - .uniq - .join(',') +def write_to_file(user, stats, result_file) + result_file.write(',') if $general_info['totalUsers'] > 1 - # Статистика по пользователям - users_objects = [] + user_data = { "#{user.attributes['first_name']} #{user.attributes['last_name']}" => stats } + json_data = JSON.generate(user_data) + json_data = json_data[1...json_data.length - 1] if json_data[0] == '{' && json_data[-1] == '}' + result_file.write(json_data) +end - 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) - users_objects = users_objects + [user_object] - end +def clear_data(user) + user.sessions = [] + user.attributes = nil +end - report['usersStats'] = {} +def work(file_name = 'data_large.txt') + reset_general_info + result_file = File.open('result.json', 'w') + result_file.write('{"usersStats":{') - # Собираем количество сессий по пользователям - collect_stats_from_users(report, users_objects) do |user| - { 'sessionsCount' => user.sessions.count } - end + user = User.new(attributes: nil, sessions: []) - # Собираем количество времени по пользователям - collect_stats_from_users(report, users_objects) do |user| - { 'totalTime' => user.sessions.map {|s| s['time']}.map {|t| t.to_i}.sum.to_s + ' min.' } - end + File.foreach(file_name) do |line| + cols = line.chomp.split(',') - # Выбираем самую длинную сессию пользователя - collect_stats_from_users(report, users_objects) do |user| - { 'longestSession' => user.sessions.map {|s| s['time']}.map {|t| t.to_i}.max.to_s + ' min.' } - end + case cols[0] + when 'session' + user.sessions << parse_session(cols) + $general_info['totalSessions'] += 1 + when 'user' + collect_stats_from_users(user, result_file) - # Браузеры пользователя через запятую - collect_stats_from_users(report, users_objects) do |user| - { 'browsers' => user.sessions.map {|s| s['browser']}.map {|b| b.upcase}.sort.join(', ') } + user.attributes = parse_user(cols) + $general_info['totalUsers'] += 1 + end end - # Хоть раз использовал IE? - collect_stats_from_users(report, users_objects) do |user| - { 'usedIE' => user.sessions.map{|s| s['browser']}.any? { |b| b.upcase =~ /INTERNET EXPLORER/ } } - end + collect_stats_from_users(user, result_file) - # Всегда использовал только Chrome? - collect_stats_from_users(report, users_objects) do |user| - { 'alwaysUsedChrome' => user.sessions.map{|s| s['browser']}.all? { |b| b.upcase =~ /CHROME/ } } - end + $general_info['uniqueBrowsersCount'] = $general_info['allBrowsers'].size + $general_info['allBrowsers'] = $general_info['allBrowsers'].sort!.join(',') - # Даты сессий через запятую в обратном порядке в формате iso8601 - collect_stats_from_users(report, users_objects) do |user| - { 'dates' => user.sessions.map{|s| s['date']}.map {|d| Date.parse(d)}.sort.reverse.map { |d| d.iso8601 } } - end + result_file.write('},') + result_file.write(JSON.generate($general_info).slice(1..-1).to_s) + result_file.close - File.write('result.json', "#{report.to_json}\n") + puts 'Finish work' puts "MEMORY USAGE: %d MB" % (`ps -o rss= -p #{Process.pid}`.to_i / 1024) end +def reset_general_info + $general_info['totalUsers'] = 0 + $general_info['uniqueBrowsersCount'] = 0 + $general_info['totalSessions'] = 0 + $general_info['allBrowsers'] = [] +end + class TestMe < Minitest::Test def setup File.write('result.json', '') diff --git a/valgrind/Dockerfile b/valgrind/Dockerfile new file mode 100644 index 00000000..e7f051ae --- /dev/null +++ b/valgrind/Dockerfile @@ -0,0 +1,16 @@ +FROM ruby:2.6-stretch + +RUN apt-get update && apt-get install g++ valgrind -y \ + massif-visualizer \ + --no-install-recommends \ + && apt-get purge --auto-remove -y curl \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /src/*.deb + +RUN groupadd -r massif && useradd -r -g massif massif \ + && mkdir -p /home/massif/test && chown -R massif:massif /home/massif +USER massif +WORKDIR /home/massif/test + +COPY Gemfile /home/massif/test +RUN bundle install \ No newline at end of file diff --git a/valgrind/Gemfile b/valgrind/Gemfile new file mode 100644 index 00000000..88d9c6cb --- /dev/null +++ b/valgrind/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' +gem 'oj' +gem 'progress_bar' \ No newline at end of file diff --git a/valgrind/build-docker.sh b/valgrind/build-docker.sh new file mode 100644 index 00000000..e0607c2d --- /dev/null +++ b/valgrind/build-docker.sh @@ -0,0 +1 @@ +docker build . \ No newline at end of file diff --git a/valgrind/profile.sh b/valgrind/profile.sh new file mode 100644 index 00000000..77248b8b --- /dev/null +++ b/valgrind/profile.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +docker run -it \ + -v $(pwd):/home/massif/test \ + -e DATA_FILE=small.txt \ + spajic/docker-valgrind-massif \ + valgrind --tool=massif ruby work.rb \ No newline at end of file diff --git a/valgrind/visualize.sh b/valgrind/visualize.sh new file mode 100644 index 00000000..2057e8f0 --- /dev/null +++ b/valgrind/visualize.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# +# NOTE: On macOS with XQuartz, you will need to allow network connections to X11 +# + +ip=$(ifconfig en0 | grep inet | awk '$1=="inet" {print $2}') && echo "IP: $ip" +xhost + ${ip} + +docker run -d -ti --rm \ + -e DISPLAY=${ip}:0 \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + -v $(pwd):/home/massif/test \ + spajic/docker-valgrind-massif \ + massif-visualizer \ No newline at end of file