From 6b6ca767352711d4ce1b8c8b743c33d324b0c142 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 16 Mar 2025 01:30:34 +0300 Subject: [PATCH 1/4] [zero] create core classes and initial measure --- .rspec | 1 + Gemfile | 10 + Gemfile.lock | 35 +++ main.rb | 147 ++++++++++++ measurer.rb | 10 + profiler.rb | 49 ++++ profiles/memory_profiler | 506 +++++++++++++++++++++++++++++++++++++++ profiles/stackprof.dump | Bin 0 -> 5039 bytes spec/main_spec.rb | 33 +++ spec/spec_helper.rb | 105 ++++++++ task-2.rb | 179 +------------- user.rb | 8 + 12 files changed, 910 insertions(+), 173 deletions(-) create mode 100644 .rspec create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 main.rb create mode 100644 measurer.rb create mode 100644 profiler.rb create mode 100644 profiles/memory_profiler create mode 100644 profiles/stackprof.dump create mode 100644 spec/main_spec.rb create mode 100644 spec/spec_helper.rb create mode 100644 user.rb diff --git a/.rspec b/.rspec new file mode 100644 index 00000000..c99d2e73 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..ba961d0a --- /dev/null +++ b/Gemfile @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + + +gem 'stackprof' +gem 'ruby-prof' +gem 'memory_profiler' +gem 'rspec' +gem 'byebug' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..293b5584 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,35 @@ +GEM + remote: https://rubygems.org/ + specs: + byebug (11.1.3) + diff-lcs (1.6.0) + memory_profiler (1.1.0) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.3) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) + ruby-prof (1.7.1) + stackprof (0.2.27) + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + byebug + memory_profiler + rspec + ruby-prof + stackprof + +BUNDLED WITH + 2.5.18 diff --git a/main.rb b/main.rb new file mode 100644 index 00000000..f9bd3354 --- /dev/null +++ b/main.rb @@ -0,0 +1,147 @@ +class Main + PARTIAL_VOLUME_FILE_NAME = "dataN.txt" + + def initialize(options: {}) + @source_file_name = options[:source_file_name] || 'data_large.txt' + @count_lines = options[:count_lines] + @is_print_memory_usage = options[:is_print_memory_usage] || false + end + + def call = work + + private + + attr_reader :source_file_name, :count_lines, :is_print_memory_usage + + def work + `head -n #{count_lines} #{source_file_name} > #{PARTIAL_VOLUME_FILE_NAME}` if count_lines + file_lines = File.read(count_lines ? PARTIAL_VOLUME_FILE_NAME : source_file_name).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' + 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 + + report['uniqueBrowsersCount'] = uniqueBrowsers.count + + report['totalSessions'] = sessions.count + + report['allBrowsers'] = + sessions + .map { |s| s['browser'] } + .map { |b| b.upcase } + .sort + .uniq + .join(',') + + # Статистика по пользователям + users_objects = [] + + 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 + + report['usersStats'] = {} + + # Собираем количество сессий по пользователям + collect_stats_from_users(report, users_objects) do |user| + { 'sessionsCount' => user.sessions.count } + end + + # Собираем количество времени по пользователям + 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 + + # Выбираем самую длинную сессию пользователя + 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 + + # Браузеры пользователя через запятую + collect_stats_from_users(report, users_objects) do |user| + { 'browsers' => user.sessions.map {|s| s['browser']}.map {|b| b.upcase}.sort.join(', ') } + 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 + + # Всегда использовал только Chrome? + collect_stats_from_users(report, users_objects) do |user| + { 'alwaysUsedChrome' => user.sessions.map{|s| s['browser']}.all? { |b| b.upcase =~ /CHROME/ } } + end + + # Даты сессий через запятую в обратном порядке в формате 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 + + File.write('result.json', "#{report.to_json}\n") + + puts "MEMORY USAGE: #{(`ps -o rss= -p #{Process.pid}`.to_i / 1024)} MB" if is_print_memory_usage + end + + def parse_user(user) + fields = user.split(',') + parsed_result = { + 'id' => fields[1], + 'first_name' => fields[2], + 'last_name' => fields[3], + 'age' => fields[4], + } + end + + def parse_session(session) + fields = session.split(',') + parsed_result = { + 'user_id' => fields[1], + 'session_id' => fields[2], + 'browser' => fields[3], + 'time' => fields[4], + 'date' => fields[5], + } + 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 +end diff --git a/measurer.rb b/measurer.rb new file mode 100644 index 00000000..8de867b0 --- /dev/null +++ b/measurer.rb @@ -0,0 +1,10 @@ +require_relative 'main' + +class Measurer + def call + Main.new(options: { count_lines: 15_000, is_print_memory_usage: true }).call + end +end + +# Zero iteration: +# 15_000 lines: 103 MB diff --git a/profiler.rb b/profiler.rb new file mode 100644 index 00000000..0099c34c --- /dev/null +++ b/profiler.rb @@ -0,0 +1,49 @@ +require 'memory_profiler' +require 'stackprof' +require 'ruby-prof' + +require_relative 'main' + +class Profiler + def initialize(count_lines:) + @count_lines = count_lines + end + + def call + profile_by_memory_profiler + profile_memory_by_ruby_prof + profile_allocations_by_ruby_prof + profile_allocations_by_stack_prof + end + + private + + attr_reader :count_lines + + def profile_by_memory_profiler + report = MemoryProfiler.report { action } + report.pretty_print(to_file: './profiles/memory_profiler') + end + + def profile_memory_by_ruby_prof + RubyProf.measure_mode = RubyProf::MEMORY + result = RubyProf.profile { action } + printer = RubyProf::CallTreePrinter.new(result) + printer.print(:path => ".", :profile => "profiles/ruby_prof_memory_profile") + end + + def profile_allocations_by_ruby_prof + RubyProf.measure_mode = RubyProf::ALLOCATIONS + result = RubyProf.profile { action } + printer = RubyProf::CallTreePrinter.new(result) + printer.print(:path => ".", :profile => "profiles/ruby_prof_allocations_profile") + end + + def profile_allocations_by_stack_prof + StackProf.run(mode: :object, out: 'profiles/stackprof.dump') do + action + end + end + + def action = Main.new(options: { count_lines: }).call +end diff --git a/profiles/memory_profiler b/profiles/memory_profiler new file mode 100644 index 00000000..b22436b2 --- /dev/null +++ b/profiles/memory_profiler @@ -0,0 +1,506 @@ +Total allocated: 3791101592 bytes (1471886 objects) +Total retained: 4249 bytes (9 objects) + +allocated memory by gem +----------------------------------- +3791101592 other + +allocated memory by file +----------------------------------- +3791101032 /home/nikita/rails_optimization/task_2/main.rb + 560 /home/nikita/rails_optimization/task_2/profiler.rb + +allocated memory by location +----------------------------------- +2585606480 /home/nikita/rails_optimization/task_2/main.rb:26 + 933571968 /home/nikita/rails_optimization/task_2/main.rb:72 + 85937936 /home/nikita/rails_optimization/task_2/main.rb:25 + 84737936 /home/nikita/rails_optimization/task_2/main.rb:74 + 29275553 /home/nikita/rails_optimization/task_2/main.rb:111 + 14467200 /home/nikita/rails_optimization/task_2/main.rb:24 + 12446720 /home/nikita/rails_optimization/task_2/main.rb:130 + 6559560 /home/nikita/rails_optimization/task_2/main.rb:142 + 5143600 /home/nikita/rails_optimization/task_2/main.rb:144 + 4351680 /home/nikita/rails_optimization/task_2/main.rb:96 + 4065280 /home/nikita/rails_optimization/task_2/main.rb:131 + 3882366 /home/nikita/rails_optimization/task_2/main.rb:18 + 3623624 /home/nikita/rails_optimization/task_2/main.rb:101 + 2731036 /home/nikita/rails_optimization/task_2/main.rb:114 + 2197240 /home/nikita/rails_optimization/task_2/main.rb:106 + 2063760 /home/nikita/rails_optimization/task_2/main.rb:86 + 2063760 /home/nikita/rails_optimization/task_2/main.rb:91 + 2020480 /home/nikita/rails_optimization/task_2/main.rb:120 + 1653120 /home/nikita/rails_optimization/task_2/main.rb:73 + 1470504 /home/nikita/rails_optimization/task_2/main.rb:62 + 937080 /home/nikita/rails_optimization/task_2/main.rb:143 + 734720 /home/nikita/rails_optimization/task_2/main.rb:121 + 734720 /home/nikita/rails_optimization/task_2/main.rb:81 + 229536 /home/nikita/rails_optimization/task_2/main.rb:77 + 203344 /home/nikita/rails_optimization/task_2/main.rb:63 + 203304 /home/nikita/rails_optimization/task_2/main.rb:61 + 183152 /home/nikita/rails_optimization/task_2/main.rb:52 + 2645 /home/nikita/rails_optimization/task_2/main.rb:65 + 1640 /home/nikita/rails_optimization/task_2/main.rb:64 + 728 /home/nikita/rails_optimization/task_2/main.rb:17 + 560 /home/nikita/rails_optimization/task_2/profiler.rb:48 + 160 /home/nikita/rails_optimization/task_2/main.rb:44 + 40 /home/nikita/rails_optimization/task_2/main.rb:20 + 40 /home/nikita/rails_optimization/task_2/main.rb:21 + 40 /home/nikita/rails_optimization/task_2/main.rb:49 + 40 /home/nikita/rails_optimization/task_2/main.rb:5 + 40 /home/nikita/rails_optimization/task_2/main.rb:68 + +allocated memory by class +----------------------------------- +3706736736 Array + 47273903 String + 21586016 Hash + 13478424 MatchData + 1829376 Date + 183680 User + 8688 File + 3609 Regexp + 560 Proc + 248 IO + 120 JSON::Ext::Generator::State + 80 Main + 80 Process::Status + 72 Thread::Mutex + +allocated objects by gem +----------------------------------- + 1471886 other + +allocated objects by file +----------------------------------- + 1471882 /home/nikita/rails_optimization/task_2/main.rb + 4 /home/nikita/rails_optimization/task_2/profiler.rb + +allocated objects by location +----------------------------------- + 387380 /home/nikita/rails_optimization/task_2/main.rb:111 + 235408 /home/nikita/rails_optimization/task_2/main.rb:24 + 203264 /home/nikita/rails_optimization/task_2/main.rb:130 + 160720 /home/nikita/rails_optimization/task_2/main.rb:142 + 80816 /home/nikita/rails_optimization/task_2/main.rb:26 + 57062 /home/nikita/rails_optimization/task_2/main.rb:96 + 39184 /home/nikita/rails_optimization/task_2/main.rb:25 + 38868 /home/nikita/rails_optimization/task_2/main.rb:101 + 32151 /home/nikita/rails_optimization/task_2/main.rb:144 + 32144 /home/nikita/rails_optimization/task_2/main.rb:120 + 30005 /home/nikita/rails_optimization/task_2/main.rb:18 + 27552 /home/nikita/rails_optimization/task_2/main.rb:86 + 27552 /home/nikita/rails_optimization/task_2/main.rb:91 + 25409 /home/nikita/rails_optimization/task_2/main.rb:62 + 25408 /home/nikita/rails_optimization/task_2/main.rb:131 + 22611 /home/nikita/rails_optimization/task_2/main.rb:106 + 13776 /home/nikita/rails_optimization/task_2/main.rb:73 + 9184 /home/nikita/rails_optimization/task_2/main.rb:143 + 9184 /home/nikita/rails_optimization/task_2/main.rb:74 + 4592 /home/nikita/rails_optimization/task_2/main.rb:121 + 4592 /home/nikita/rails_optimization/task_2/main.rb:72 + 4592 /home/nikita/rails_optimization/task_2/main.rb:81 + 400 /home/nikita/rails_optimization/task_2/main.rb:52 + 8 /home/nikita/rails_optimization/task_2/main.rb:114 + 7 /home/nikita/rails_optimization/task_2/main.rb:17 + 4 /home/nikita/rails_optimization/task_2/profiler.rb:48 + 2 /home/nikita/rails_optimization/task_2/main.rb:63 + 2 /home/nikita/rails_optimization/task_2/main.rb:65 + 1 /home/nikita/rails_optimization/task_2/main.rb:20 + 1 /home/nikita/rails_optimization/task_2/main.rb:21 + 1 /home/nikita/rails_optimization/task_2/main.rb:44 + 1 /home/nikita/rails_optimization/task_2/main.rb:49 + 1 /home/nikita/rails_optimization/task_2/main.rb:5 + 1 /home/nikita/rails_optimization/task_2/main.rb:61 + 1 /home/nikita/rails_optimization/task_2/main.rb:64 + 1 /home/nikita/rails_optimization/task_2/main.rb:68 + 1 /home/nikita/rails_optimization/task_2/main.rb:77 + +allocated objects by class +----------------------------------- + 1006313 String + 206678 Array + 133479 Hash + 95399 MatchData + 25408 Date + 4592 User + 7 Proc + 3 Regexp + 2 File + 1 IO + 1 JSON::Ext::Generator::State + 1 Main + 1 Process::Status + 1 Thread::Mutex + +retained memory by gem +----------------------------------- + 4249 other + +retained memory by file +----------------------------------- + 4249 /home/nikita/rails_optimization/task_2/main.rb + +retained memory by location +----------------------------------- + 4169 /home/nikita/rails_optimization/task_2/main.rb:111 + 80 /home/nikita/rails_optimization/task_2/main.rb:17 + +retained memory by class +----------------------------------- + 3609 Regexp + 560 String + 80 Process::Status + +retained objects by gem +----------------------------------- + 9 other + +retained objects by file +----------------------------------- + 9 /home/nikita/rails_optimization/task_2/main.rb + +retained objects by location +----------------------------------- + 8 /home/nikita/rails_optimization/task_2/main.rb:111 + 1 /home/nikita/rails_optimization/task_2/main.rb:17 + +retained objects by class +----------------------------------- + 5 String + 3 Regexp + 1 Process::Status + + +Allocated String Report +----------------------------------- + 133776 " " + 101632 /home/nikita/rails_optimization/task_2/main.rb:111 + 32144 /home/nikita/rails_optimization/task_2/main.rb:142 + + 80816 "session" + 30000 /home/nikita/rails_optimization/task_2/main.rb:26 + 25408 /home/nikita/rails_optimization/task_2/main.rb:130 + 25408 /home/nikita/rails_optimization/task_2/main.rb:24 + + 60001 "," + 30000 /home/nikita/rails_optimization/task_2/main.rb:24 + 25408 /home/nikita/rails_optimization/task_2/main.rb:130 + 4592 /home/nikita/rails_optimization/task_2/main.rb:120 + 1 /home/nikita/rails_optimization/task_2/main.rb:65 + + 39184 "user" + 30000 /home/nikita/rails_optimization/task_2/main.rb:25 + 4592 /home/nikita/rails_optimization/task_2/main.rb:120 + 4592 /home/nikita/rails_optimization/task_2/main.rb:24 + + 9702 "0" + 4849 /home/nikita/rails_optimization/task_2/main.rb:24 + 4808 /home/nikita/rails_optimization/task_2/main.rb:130 + 41 /home/nikita/rails_optimization/task_2/main.rb:120 + 2 /home/nikita/rails_optimization/task_2/main.rb:86 + 2 /home/nikita/rails_optimization/task_2/main.rb:91 + + 9414 "2017" + 9400 /home/nikita/rails_optimization/task_2/main.rb:111 + 7 /home/nikita/rails_optimization/task_2/main.rb:24 + 6 /home/nikita/rails_optimization/task_2/main.rb:130 + 1 /home/nikita/rails_optimization/task_2/main.rb:120 + + 9194 "2018" + 9172 /home/nikita/rails_optimization/task_2/main.rb:111 + 11 /home/nikita/rails_optimization/task_2/main.rb:24 + 10 /home/nikita/rails_optimization/task_2/main.rb:130 + 1 /home/nikita/rails_optimization/task_2/main.rb:120 + + 9184 " min." + 4592 /home/nikita/rails_optimization/task_2/main.rb:86 + 4592 /home/nikita/rails_optimization/task_2/main.rb:91 + + 8770 "1" + 4379 /home/nikita/rails_optimization/task_2/main.rb:24 + 4332 /home/nikita/rails_optimization/task_2/main.rb:130 + 47 /home/nikita/rails_optimization/task_2/main.rb:120 + 6 /home/nikita/rails_optimization/task_2/main.rb:86 + 6 /home/nikita/rails_optimization/task_2/main.rb:91 + + 7946 "2" + 3965 /home/nikita/rails_optimization/task_2/main.rb:24 + 3908 /home/nikita/rails_optimization/task_2/main.rb:130 + 57 /home/nikita/rails_optimization/task_2/main.rb:120 + 8 /home/nikita/rails_optimization/task_2/main.rb:86 + 8 /home/nikita/rails_optimization/task_2/main.rb:91 + + 6993 "3" + 3496 /home/nikita/rails_optimization/task_2/main.rb:24 + 3447 /home/nikita/rails_optimization/task_2/main.rb:130 + 49 /home/nikita/rails_optimization/task_2/main.rb:120 + 1 /home/nikita/rails_optimization/task_2/main.rb:91 + + 6014 "4" + 3006 /home/nikita/rails_optimization/task_2/main.rb:24 + 2955 /home/nikita/rails_optimization/task_2/main.rb:130 + 51 /home/nikita/rails_optimization/task_2/main.rb:120 + 1 /home/nikita/rails_optimization/task_2/main.rb:86 + 1 /home/nikita/rails_optimization/task_2/main.rb:91 + + 5762 "2016" + 5754 /home/nikita/rails_optimization/task_2/main.rb:111 + 4 /home/nikita/rails_optimization/task_2/main.rb:24 + 3 /home/nikita/rails_optimization/task_2/main.rb:130 + 1 /home/nikita/rails_optimization/task_2/main.rb:120 + + 5113 "5" + 2556 /home/nikita/rails_optimization/task_2/main.rb:24 + 2513 /home/nikita/rails_optimization/task_2/main.rb:130 + 43 /home/nikita/rails_optimization/task_2/main.rb:120 + 1 /home/nikita/rails_optimization/task_2/main.rb:86 + + 4592 ", " + 4592 /home/nikita/rails_optimization/task_2/main.rb:96 + + 4216 "6" + 2105 /home/nikita/rails_optimization/task_2/main.rb:24 + 2061 /home/nikita/rails_optimization/task_2/main.rb:130 + 44 /home/nikita/rails_optimization/task_2/main.rb:120 + 3 /home/nikita/rails_optimization/task_2/main.rb:86 + 3 /home/nikita/rails_optimization/task_2/main.rb:91 + + 3741 "12" + 3208 /home/nikita/rails_optimization/task_2/main.rb:111 + 258 /home/nikita/rails_optimization/task_2/main.rb:24 + 205 /home/nikita/rails_optimization/task_2/main.rb:130 + 53 /home/nikita/rails_optimization/task_2/main.rb:120 + 9 /home/nikita/rails_optimization/task_2/main.rb:86 + 8 /home/nikita/rails_optimization/task_2/main.rb:91 + + 3656 "11" + 3068 /home/nikita/rails_optimization/task_2/main.rb:111 + 282 /home/nikita/rails_optimization/task_2/main.rb:24 + 245 /home/nikita/rails_optimization/task_2/main.rb:130 + 37 /home/nikita/rails_optimization/task_2/main.rb:120 + 12 /home/nikita/rails_optimization/task_2/main.rb:86 + 12 /home/nikita/rails_optimization/task_2/main.rb:91 + + 3655 "10" + 3135 /home/nikita/rails_optimization/task_2/main.rb:111 + 256 /home/nikita/rails_optimization/task_2/main.rb:24 + 209 /home/nikita/rails_optimization/task_2/main.rb:130 + 47 /home/nikita/rails_optimization/task_2/main.rb:120 + 4 /home/nikita/rails_optimization/task_2/main.rb:86 + 4 /home/nikita/rails_optimization/task_2/main.rb:91 + + 3422 "7" + 1711 /home/nikita/rails_optimization/task_2/main.rb:24 + 1663 /home/nikita/rails_optimization/task_2/main.rb:130 + 48 /home/nikita/rails_optimization/task_2/main.rb:120 + + 3283 "08" + 3283 /home/nikita/rails_optimization/task_2/main.rb:111 + + 3276 "07" + 3276 /home/nikita/rails_optimization/task_2/main.rb:111 + + 3226 "01" + 3226 /home/nikita/rails_optimization/task_2/main.rb:111 + + 3129 "06" + 3129 /home/nikita/rails_optimization/task_2/main.rb:111 + + 3113 "09" + 3113 /home/nikita/rails_optimization/task_2/main.rb:111 + + 2736 "05" + 2736 /home/nikita/rails_optimization/task_2/main.rb:111 + + 2535 "02" + 2535 /home/nikita/rails_optimization/task_2/main.rb:111 + + 2474 "8" + 1229 /home/nikita/rails_optimization/task_2/main.rb:24 + 1189 /home/nikita/rails_optimization/task_2/main.rb:130 + 40 /home/nikita/rails_optimization/task_2/main.rb:120 + 8 /home/nikita/rails_optimization/task_2/main.rb:86 + 8 /home/nikita/rails_optimization/task_2/main.rb:91 + + 2386 "03" + 2386 /home/nikita/rails_optimization/task_2/main.rb:111 + + 2378 "04" + 2378 /home/nikita/rails_optimization/task_2/main.rb:111 + + 1473 "9" + 730 /home/nikita/rails_optimization/task_2/main.rb:24 + 683 /home/nikita/rails_optimization/task_2/main.rb:130 + 47 /home/nikita/rails_optimization/task_2/main.rb:120 + 7 /home/nikita/rails_optimization/task_2/main.rb:91 + 6 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1413 "14" + 819 /home/nikita/rails_optimization/task_2/main.rb:111 + 287 /home/nikita/rails_optimization/task_2/main.rb:24 + 242 /home/nikita/rails_optimization/task_2/main.rb:130 + 45 /home/nikita/rails_optimization/task_2/main.rb:120 + 11 /home/nikita/rails_optimization/task_2/main.rb:91 + 9 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1411 "20" + 898 /home/nikita/rails_optimization/task_2/main.rb:111 + 253 /home/nikita/rails_optimization/task_2/main.rb:24 + 199 /home/nikita/rails_optimization/task_2/main.rb:130 + 54 /home/nikita/rails_optimization/task_2/main.rb:120 + 5 /home/nikita/rails_optimization/task_2/main.rb:91 + 2 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1411 "27" + 816 /home/nikita/rails_optimization/task_2/main.rb:111 + 293 /home/nikita/rails_optimization/task_2/main.rb:24 + 241 /home/nikita/rails_optimization/task_2/main.rb:130 + 52 /home/nikita/rails_optimization/task_2/main.rb:120 + 6 /home/nikita/rails_optimization/task_2/main.rb:91 + 3 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1406 "29" + 814 /home/nikita/rails_optimization/task_2/main.rb:111 + 281 /home/nikita/rails_optimization/task_2/main.rb:24 + 241 /home/nikita/rails_optimization/task_2/main.rb:130 + 40 /home/nikita/rails_optimization/task_2/main.rb:120 + 17 /home/nikita/rails_optimization/task_2/main.rb:91 + 13 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1401 "24" + 892 /home/nikita/rails_optimization/task_2/main.rb:111 + 250 /home/nikita/rails_optimization/task_2/main.rb:24 + 202 /home/nikita/rails_optimization/task_2/main.rb:130 + 48 /home/nikita/rails_optimization/task_2/main.rb:120 + 6 /home/nikita/rails_optimization/task_2/main.rb:91 + 3 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1398 "25" + 858 /home/nikita/rails_optimization/task_2/main.rb:111 + 265 /home/nikita/rails_optimization/task_2/main.rb:24 + 228 /home/nikita/rails_optimization/task_2/main.rb:130 + 37 /home/nikita/rails_optimization/task_2/main.rb:120 + 6 /home/nikita/rails_optimization/task_2/main.rb:91 + 4 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1385 "19" + 847 /home/nikita/rails_optimization/task_2/main.rb:111 + 266 /home/nikita/rails_optimization/task_2/main.rb:24 + 221 /home/nikita/rails_optimization/task_2/main.rb:130 + 45 /home/nikita/rails_optimization/task_2/main.rb:120 + 3 /home/nikita/rails_optimization/task_2/main.rb:86 + 3 /home/nikita/rails_optimization/task_2/main.rb:91 + + 1373 "21" + 822 /home/nikita/rails_optimization/task_2/main.rb:111 + 270 /home/nikita/rails_optimization/task_2/main.rb:24 + 226 /home/nikita/rails_optimization/task_2/main.rb:130 + 44 /home/nikita/rails_optimization/task_2/main.rb:120 + 6 /home/nikita/rails_optimization/task_2/main.rb:91 + 5 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1369 "26" + 823 /home/nikita/rails_optimization/task_2/main.rb:111 + 268 /home/nikita/rails_optimization/task_2/main.rb:24 + 224 /home/nikita/rails_optimization/task_2/main.rb:130 + 44 /home/nikita/rails_optimization/task_2/main.rb:120 + 6 /home/nikita/rails_optimization/task_2/main.rb:86 + 4 /home/nikita/rails_optimization/task_2/main.rb:91 + + 1363 "18" + 813 /home/nikita/rails_optimization/task_2/main.rb:111 + 271 /home/nikita/rails_optimization/task_2/main.rb:24 + 229 /home/nikita/rails_optimization/task_2/main.rb:130 + 42 /home/nikita/rails_optimization/task_2/main.rb:120 + 4 /home/nikita/rails_optimization/task_2/main.rb:86 + 4 /home/nikita/rails_optimization/task_2/main.rb:91 + + 1356 "28" + 833 /home/nikita/rails_optimization/task_2/main.rb:111 + 257 /home/nikita/rails_optimization/task_2/main.rb:24 + 208 /home/nikita/rails_optimization/task_2/main.rb:130 + 49 /home/nikita/rails_optimization/task_2/main.rb:120 + 7 /home/nikita/rails_optimization/task_2/main.rb:91 + 2 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1345 "16" + 817 /home/nikita/rails_optimization/task_2/main.rb:111 + 261 /home/nikita/rails_optimization/task_2/main.rb:24 + 198 /home/nikita/rails_optimization/task_2/main.rb:130 + 63 /home/nikita/rails_optimization/task_2/main.rb:120 + 3 /home/nikita/rails_optimization/task_2/main.rb:86 + 3 /home/nikita/rails_optimization/task_2/main.rb:91 + + 1343 "22" + 853 /home/nikita/rails_optimization/task_2/main.rb:111 + 239 /home/nikita/rails_optimization/task_2/main.rb:24 + 193 /home/nikita/rails_optimization/task_2/main.rb:130 + 46 /home/nikita/rails_optimization/task_2/main.rb:120 + 8 /home/nikita/rails_optimization/task_2/main.rb:91 + 4 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1337 "17" + 801 /home/nikita/rails_optimization/task_2/main.rb:111 + 267 /home/nikita/rails_optimization/task_2/main.rb:24 + 228 /home/nikita/rails_optimization/task_2/main.rb:130 + 39 /home/nikita/rails_optimization/task_2/main.rb:120 + 1 /home/nikita/rails_optimization/task_2/main.rb:86 + 1 /home/nikita/rails_optimization/task_2/main.rb:91 + + 1317 "13" + 774 /home/nikita/rails_optimization/task_2/main.rb:111 + 266 /home/nikita/rails_optimization/task_2/main.rb:24 + 218 /home/nikita/rails_optimization/task_2/main.rb:130 + 48 /home/nikita/rails_optimization/task_2/main.rb:120 + 7 /home/nikita/rails_optimization/task_2/main.rb:91 + 4 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1316 "30" + 749 /home/nikita/rails_optimization/task_2/main.rb:111 + 276 /home/nikita/rails_optimization/task_2/main.rb:24 + 232 /home/nikita/rails_optimization/task_2/main.rb:130 + 44 /home/nikita/rails_optimization/task_2/main.rb:120 + 8 /home/nikita/rails_optimization/task_2/main.rb:91 + 7 /home/nikita/rails_optimization/task_2/main.rb:86 + + 1314 "23" + 778 /home/nikita/rails_optimization/task_2/main.rb:111 + 264 /home/nikita/rails_optimization/task_2/main.rb:24 + 214 /home/nikita/rails_optimization/task_2/main.rb:130 + 50 /home/nikita/rails_optimization/task_2/main.rb:120 + 4 /home/nikita/rails_optimization/task_2/main.rb:86 + 4 /home/nikita/rails_optimization/task_2/main.rb:91 + + 1308 "15" + 817 /home/nikita/rails_optimization/task_2/main.rb:111 + 241 /home/nikita/rails_optimization/task_2/main.rb:24 + 193 /home/nikita/rails_optimization/task_2/main.rb:130 + 48 /home/nikita/rails_optimization/task_2/main.rb:120 + 5 /home/nikita/rails_optimization/task_2/main.rb:86 + 4 /home/nikita/rails_optimization/task_2/main.rb:91 + + 1090 "2019" + 1082 /home/nikita/rails_optimization/task_2/main.rb:111 + 4 /home/nikita/rails_optimization/task_2/main.rb:24 + 3 /home/nikita/rails_optimization/task_2/main.rb:130 + 1 /home/nikita/rails_optimization/task_2/main.rb:120 + + +Retained String Report +----------------------------------- + 1 "('?[-+]?(?EfP|M5agvIv=Z_IRE}t*{z-7o zx$|?^Hf`yHHTT?m?m6H2y=T3lWxm5`pJuM$jy={gkbA&rtvOX@v^nKTR`HAuZhOp~ zHZ7ho+J#xGTTJlyGmXtg=Xj+QY{ZPaYg`CcRdzaR%rmQ|XU@lrwlUYNF)_c-+S#&F zpZI(K+bH*Z31A z^5s2KOqCwU*Zu1`x12B%M@}297PlE6G`c;*CPae$HpC|CdVpO5?J zcnd5q|Lc!$!!rIfSe8NwVm`qK&nC%Mj?6vhCGJAHFCSv|wU#Nl*)4@;hB}G z5*`*hi|vIB&lkE$5+H~b?k45Z53~!?>u;R^uSlMa^GQAssE+aea2fK=w*iXoQ>X=@ zQ4vndR6+Yg~CkiNR1m|p0VNJYwh=N*3 zkMwOC@|=3_c9)%RaF_8yP)h7~gcecm{LC<1CbVG1C(aP7^|8(jO1vc49sL86o~-wp|UZ)l0&<2E)a z&-5As-1I2A=|im<503tpz7bAhN26!Cb{0Fg(h*mBO*dxf)Es_g}MZi*Hib_|ugbdeyxz^HV z?rf?V={X$cLnbto+8^1Z{jgc!T9aQ9x_RHL zDwksgB~E(SGKI+6Y$o!U{!SLg0R8PO3<7A#^1o^Hh?aD7<8km-mfPIpFnm2h57O?> zI1{0xqxRMczDt0{1m#r$-({sbJHj4Bst)sr?w5&?a;^3`1}){^u(?K;4R| z@)34=o`z-pCwGF`srMxsqvAh!J`M*<8zfcyM=MaheL;qNCp=6vM%>;XMpID0`wIm{J~DYl?7t& zU!Dj&9RiDindAlt^f@BJ3G9b8?1j&=od|Ne9Wwmn?F@>m<~&j698sK}9D$$x+TrTp)V@*fBG zs9t1M-~il-VxdHM3_>g`V?G71dx!s6JRX&N8p$ABy zWVMn@WdEs~u3)8xU~bSDz5|YdY6gK26W=p_1Aws7lez$o(H9rU-B03LCILW|4M5s2 zq3(=uLR&zoN^>9}_fT(=KtwR{$|j{(wOViC^v9&rK-`M!+=1eMT;*#tAW!}VTRHHP(jn} z{RoKu+p@Kiz92QLFJc|km*vso0)(-ZYv0E(-Vi#Ezd8%8?R!XqDkyO662{bTMA{-A ze7v=lcZABw;0`}RO3KA!_fv4-6Jn9P$4oJiJ$r8d`i_y%q|1r3#@ulw=#c&B^2G&^ zDjyu_@)7tEHxB>m@SGC*EC@Oj5^&RtBp)HQm`!``s|xQO0lcePlCC^iq$us1y8lV# z=7n>pMS0Mgv{;s^MeJEei}L)*tB2u-a*ir9QWJ-F5$;k|%71h$zaXDddKI6!K|zgQ z+kqaJN76Sw0NRf?#UyOMhp{5V_B)>aMVAQqKN>GEwR{%)DlwJ8|!O_|(Z0hH+%pGmNh>o4KavxXSW4^Cq0CFbql+ hi2bC%U+qEogF2Q|0!i$E$6?0h_-}}SD+P9?{tNBjCtm;n literal 0 HcmV?d00001 diff --git a/spec/main_spec.rb b/spec/main_spec.rb new file mode 100644 index 00000000..46e6232d --- /dev/null +++ b/spec/main_spec.rb @@ -0,0 +1,33 @@ +require_relative "spec_helper" + +describe Main do + before do + File.write('result.json', '') + File.write('data.txt', +'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 +') + end + + it "записывает в файл корректный результат" do + Main.new(options: { source_file_name: "data.txt" }).call + expected_result = '{"totalUsers":3,"uniqueBrowsersCount":14,"totalSessions":15,"allBrowsers":"CHROME 13,CHROME 20,CHROME 35,CHROME 6,FIREFOX 12,FIREFOX 32,FIREFOX 47,INTERNET EXPLORER 10,INTERNET EXPLORER 28,INTERNET EXPLORER 35,SAFARI 17,SAFARI 29,SAFARI 39,SAFARI 49","usersStats":{"Leida Cira":{"sessionsCount":6,"totalTime":"455 min.","longestSession":"118 min.","browsers":"FIREFOX 12, INTERNET EXPLORER 28, INTERNET EXPLORER 28, INTERNET EXPLORER 35, SAFARI 29, SAFARI 39","usedIE":true,"alwaysUsedChrome":false,"dates":["2017-09-27","2017-03-28","2017-02-27","2016-10-23","2016-09-15","2016-09-01"]},"Palmer Katrina":{"sessionsCount":5,"totalTime":"218 min.","longestSession":"116 min.","browsers":"CHROME 13, CHROME 6, FIREFOX 32, INTERNET EXPLORER 10, SAFARI 17","usedIE":true,"alwaysUsedChrome":false,"dates":["2017-04-29","2016-12-28","2016-12-20","2016-11-11","2016-10-21"]},"Gregory Santos":{"sessionsCount":4,"totalTime":"192 min.","longestSession":"85 min.","browsers":"CHROME 20, CHROME 35, FIREFOX 47, SAFARI 49","usedIE":false,"alwaysUsedChrome":false,"dates":["2018-09-21","2018-02-02","2017-05-22","2016-11-25"]}}}' + "\n" + expect(File.read('result.json')).to eq(expected_result) + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..8dad3d3b --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,105 @@ +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration + +require 'json' +require 'byebug' +require 'date' +require_relative "../user" +require_relative "../main" + +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ + config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + config.warnings = true + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end diff --git a/task-2.rb b/task-2.rb index 34e09a3c..dff0b927 100644 --- a/task-2.rb +++ b/task-2.rb @@ -1,177 +1,10 @@ # Deoptimized version of homework task require 'json' -require 'pry' +require 'byebug' require 'date' -require 'minitest/autorun' - -class User - attr_reader :attributes, :sessions - - def initialize(attributes:, sessions:) - @attributes = attributes - @sessions = sessions - end -end - -def parse_user(user) - fields = user.split(',') - parsed_result = { - 'id' => fields[1], - 'first_name' => fields[2], - 'last_name' => fields[3], - 'age' => fields[4], - } -end - -def parse_session(session) - fields = session.split(',') - parsed_result = { - 'user_id' => fields[1], - 'session_id' => fields[2], - 'browser' => fields[3], - 'time' => fields[4], - 'date' => fields[5], - } -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' - 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 - - report['uniqueBrowsersCount'] = uniqueBrowsers.count - - report['totalSessions'] = sessions.count - - report['allBrowsers'] = - sessions - .map { |s| s['browser'] } - .map { |b| b.upcase } - .sort - .uniq - .join(',') - - # Статистика по пользователям - users_objects = [] - - 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 - - report['usersStats'] = {} - - # Собираем количество сессий по пользователям - collect_stats_from_users(report, users_objects) do |user| - { 'sessionsCount' => user.sessions.count } - end - - # Собираем количество времени по пользователям - 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 - - # Выбираем самую длинную сессию пользователя - 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 - - # Браузеры пользователя через запятую - collect_stats_from_users(report, users_objects) do |user| - { 'browsers' => user.sessions.map {|s| s['browser']}.map {|b| b.upcase}.sort.join(', ') } - 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 - - # Всегда использовал только Chrome? - collect_stats_from_users(report, users_objects) do |user| - { 'alwaysUsedChrome' => user.sessions.map{|s| s['browser']}.all? { |b| b.upcase =~ /CHROME/ } } - end - - # Даты сессий через запятую в обратном порядке в формате 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 - - File.write('result.json', "#{report.to_json}\n") - puts "MEMORY USAGE: %d MB" % (`ps -o rss= -p #{Process.pid}`.to_i / 1024) -end - -class TestMe < Minitest::Test - def setup - File.write('result.json', '') - File.write('data.txt', -'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 -') - end - - def test_result - work - expected_result = JSON.parse('{"totalUsers":3,"uniqueBrowsersCount":14,"totalSessions":15,"allBrowsers":"CHROME 13,CHROME 20,CHROME 35,CHROME 6,FIREFOX 12,FIREFOX 32,FIREFOX 47,INTERNET EXPLORER 10,INTERNET EXPLORER 28,INTERNET EXPLORER 35,SAFARI 17,SAFARI 29,SAFARI 39,SAFARI 49","usersStats":{"Leida Cira":{"sessionsCount":6,"totalTime":"455 min.","longestSession":"118 min.","browsers":"FIREFOX 12, INTERNET EXPLORER 28, INTERNET EXPLORER 28, INTERNET EXPLORER 35, SAFARI 29, SAFARI 39","usedIE":true,"alwaysUsedChrome":false,"dates":["2017-09-27","2017-03-28","2017-02-27","2016-10-23","2016-09-15","2016-09-01"]},"Palmer Katrina":{"sessionsCount":5,"totalTime":"218 min.","longestSession":"116 min.","browsers":"CHROME 13, CHROME 6, FIREFOX 32, INTERNET EXPLORER 10, SAFARI 17","usedIE":true,"alwaysUsedChrome":false,"dates":["2017-04-29","2016-12-28","2016-12-20","2016-11-11","2016-10-21"]},"Gregory Santos":{"sessionsCount":4,"totalTime":"192 min.","longestSession":"85 min.","browsers":"CHROME 20, CHROME 35, FIREFOX 47, SAFARI 49","usedIE":false,"alwaysUsedChrome":false,"dates":["2018-09-21","2018-02-02","2017-05-22","2016-11-25"]}}}') - assert_equal expected_result, JSON.parse(File.read('result.json')) - end -end +require 'benchmark' +require_relative 'user' +require_relative 'measurer' +require_relative 'profiler' +require_relative 'main' diff --git a/user.rb b/user.rb new file mode 100644 index 00000000..c0a90d89 --- /dev/null +++ b/user.rb @@ -0,0 +1,8 @@ +class User + attr_reader :attributes, :sessions + + def initialize(attributes:, sessions:) + @attributes = attributes + @sessions = sessions + end +end From 420ecf8cc4ceeb3cf50e4312f113ec443129cf6a Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 16 Mar 2025 11:24:53 +0300 Subject: [PATCH 2/4] [Zero Iteration] add control measuring thread in measurer --- main.rb | 2 -- measurer.rb | 24 ++++++++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/main.rb b/main.rb index f9bd3354..c74e162e 100644 --- a/main.rb +++ b/main.rb @@ -112,8 +112,6 @@ def work end File.write('result.json', "#{report.to_json}\n") - - puts "MEMORY USAGE: #{(`ps -o rss= -p #{Process.pid}`.to_i / 1024)} MB" if is_print_memory_usage end def parse_user(user) diff --git a/measurer.rb b/measurer.rb index 8de867b0..beae5245 100644 --- a/measurer.rb +++ b/measurer.rb @@ -1,8 +1,28 @@ require_relative 'main' class Measurer - def call - Main.new(options: { count_lines: 15_000, is_print_memory_usage: true }).call + MEMORY_USAGE_LIMIT_MB = 70 + CHECK_USAGE_INTERVAL_SEC = 1 + + def initialize(count_lines) + @count_lines = count_lines + end + + def call = [memory_measurer_thread, work_thread].map(&:join) + + private + + attr_reader :count_lines + + def work_thread = Thread.new { Main.new(options: { count_lines: }).call } + + def memory_measurer_thread = Thread.new do + sleep CHECK_USAGE_INTERVAL_SEC + current_memory_usage_mb = `ps -o rss= -p #{Process.pid}`.to_i / 1024 + if current_memory_usage_mb > MEMORY_USAGE_LIMIT_MB + puts "MEMORY USAGE: #{current_memory_usage_mb} MB, BUT LIMIT: #{MEMORY_USAGE_LIMIT_MB}" + exit + end end end From aeb309256c293ae583fafe91b7dee9d0b424a586 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 16 Mar 2025 11:31:53 +0300 Subject: [PATCH 3/4] [First] optimizing users and sessions collecting --- main.rb | 4 ++-- measurer.rb | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/main.rb b/main.rb index c74e162e..104ed6f1 100644 --- a/main.rb +++ b/main.rb @@ -22,8 +22,8 @@ def work 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' + users << parse_user(line) if cols[0] == 'user' + sessions << parse_session(line) if cols[0] == 'session' end # Отчёт в json diff --git a/measurer.rb b/measurer.rb index beae5245..36a2384d 100644 --- a/measurer.rb +++ b/measurer.rb @@ -4,7 +4,7 @@ class Measurer MEMORY_USAGE_LIMIT_MB = 70 CHECK_USAGE_INTERVAL_SEC = 1 - def initialize(count_lines) + def initialize(count_lines: nil) @count_lines = count_lines end @@ -20,11 +20,13 @@ def memory_measurer_thread = Thread.new do sleep CHECK_USAGE_INTERVAL_SEC current_memory_usage_mb = `ps -o rss= -p #{Process.pid}`.to_i / 1024 if current_memory_usage_mb > MEMORY_USAGE_LIMIT_MB - puts "MEMORY USAGE: #{current_memory_usage_mb} MB, BUT LIMIT: #{MEMORY_USAGE_LIMIT_MB}" + puts "MEMORY USAGE: #{current_memory_usage_mb} MB, BUT LIMIT: #{MEMORY_USAGE_LIMIT_MB}. TERMINATING." exit + else + puts "MEMORY USAGE: #{current_memory_usage_mb} MB" end end end # Zero iteration: -# 15_000 lines: 103 MB +# 15_000 lines: 57 MB (optimizing users and sessions collecting) From 69fa2257a3990d5832d1c6d99c653ed26fccd3f7 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 16 Mar 2025 16:39:46 +0300 Subject: [PATCH 4/4] [Second] report generation on fly --- Gemfile | 1 + Gemfile.lock | 9 + README.md | 8 +- case-study-template.md | 38 ++- main.rb | 195 +++++++--------- measurer.rb | 20 +- profiles/memory_profiler | 489 +++++---------------------------------- profiles/stackprof.dump | Bin 5039 -> 3176 bytes spec/main_spec.rb | 2 +- spec/performance_spec.rb | 8 + 10 files changed, 196 insertions(+), 574 deletions(-) create mode 100644 spec/performance_spec.rb diff --git a/Gemfile b/Gemfile index ba961d0a..2be733cc 100644 --- a/Gemfile +++ b/Gemfile @@ -8,3 +8,4 @@ gem 'ruby-prof' gem 'memory_profiler' gem 'rspec' gem 'byebug' +gem 'rspec-benchmark' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 293b5584..c73aac59 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,9 @@ GEM remote: https://rubygems.org/ specs: + benchmark-malloc (0.2.0) + benchmark-perf (0.6.0) + benchmark-trend (0.4.0) byebug (11.1.3) diff-lcs (1.6.0) memory_profiler (1.1.0) @@ -8,6 +11,11 @@ GEM 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.3) rspec-support (~> 3.13.0) rspec-expectations (3.13.3) @@ -28,6 +36,7 @@ DEPENDENCIES byebug memory_profiler rspec + rspec-benchmark ruby-prof stackprof diff --git a/README.md b/README.md index 934cd8a2..56923c7a 100644 --- a/README.md +++ b/README.md @@ -46,10 +46,10 @@ puts "MEMORY USAGE: %d MB" % (`ps -o rss= -p #{Process.pid}`.to_i / 1024)" - файл `case-study.md` с описанием проделанной оптимизации; ## Checklist -- [ ] Потренироваться с `memory_profiler` -- [ ] Потренироваться с `ruby-prof` в режиме `CallTree` c визуализацией в `QCachegrind`; -- [ ] Потренироваться с `stackprof` + `CLI` и `Speedscope` -- [ ] Потренироваться со вторым тредом для мониторинга памяти +- [x] Потренироваться с `memory_profiler` +- [x] Потренироваться с `ruby-prof` в режиме `CallTree` c визуализацией в `QCachegrind`; +- [x] Потренироваться с `stackprof` + `CLI` и `Speedscope` +- [x] Потренироваться со вторым тредом для мониторинга памяти ## Формат шагов case-study Каждый шаг оптимизации в `case-study` должен содержать четыре составляющих: diff --git a/case-study-template.md b/case-study-template.md index c3279664..08487d99 100644 --- a/case-study-template.md +++ b/case-study-template.md @@ -12,44 +12,38 @@ Я решил исправить эту проблему, оптимизировав эту программу. ## Формирование метрики -Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: *тут ваша метрика* +Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: количество потребляемой памяти процессом ## Гарантия корректности работы оптимизированной программы Программа поставлялась с тестом. Выполнение этого теста в фидбек-лупе позволяет не допустить изменения логики программы при оптимизации. ## Feedback-Loop -Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный `feedback-loop`, который позволил мне получать обратную связь по эффективности сделанных изменений за *время, которое у вас получилось* +Для того, чтобы иметь возможность быстро проверять гипотезы я выстроил эффективный `feedback-loop`, который позволил мне получать обратную связь по эффективности сделанных изменений за несколько секунд (запуск measurer -> запуск profiler -> анализ) -Вот как я построил `feedback_loop`: *как вы построили feedback_loop* +Вот как я построил `feedback_loop`: + - Вынес вычисление метрики в Measurer, в котором запускаются 2 потока - один выполняет бизнес логику, а второй выступает предохранителем при использовании процессом памяти - больше заданного лимита + - Вынес профилирование в Profiler ## Вникаем в детали системы, чтобы найти главные точки роста -Для того, чтобы найти "точки роста" для оптимизации я воспользовался *инструментами, которыми вы воспользовались* +Для того, чтобы найти "точки роста" для оптимизации я воспользовался memory_profiler, ruby-prof, stackprof Вот какие проблемы удалось найти и решить ### Ваша находка №1 -- какой отчёт показал главную точку роста -- как вы решили её оптимизировать -- как изменилась метрика -- как изменился отчёт профилировщика +- Одну из главных точек роста я выявил в отчёте memory_profile - модификация массива не in_place +- Убрал лишние аллокации +- Метрика изменилась, но не сильно с 80 МБ до 57 для 15_000 строк +- Количество общих аллокаций по Array стало меньше, но не сильно ### Ваша находка №2 -- какой отчёт показал главную точку роста -- как вы решили её оптимизировать -- как изменилась метрика -- как изменился отчёт профилировщика - -### Ваша находка №X -- какой отчёт показал главную точку роста -- как вы решили её оптимизировать -- как изменилась метрика -- как изменился отчёт профилировщика +- Понял (по подсказке + отчёту memory_profiler), что для значительного буста по памяти необходимо составлять отчёт на лету, без хранения сессий и пользователей, то есть в каждый момент времени будет обрабатываться только один пользователь. +- Добавил составление отчёта на лету при чтении каждой строки +- Метрика изменилась с 57 для 15_000 МБ до 38 для полного объёма +- Значительно уменьшилось количество общий аллокаций по Array и другим классам ## Результаты В результате проделанной оптимизации наконец удалось обработать файл с данными. -Удалось улучшить метрику системы с *того, что у вас было в начале, до того, что получилось в конце* и уложиться в заданный бюджет. - -*Какими ещё результами можете поделиться* +Удалось улучшить метрику системы с 15_000 lines: 57 MB, до полной обработки: 38 MB и уложиться в заданный бюджет. ## Защита от регрессии производительности -Для защиты от потери достигнутого прогресса при дальнейших изменениях программы *о performance-тестах, которые вы написали* +Для защиты от потери достигнутого прогресса при дальнейших изменениях программы были добавлены тесты, проверяющие корректность бизнес-логики и укладывание в заданные лимиты \ No newline at end of file diff --git a/main.rb b/main.rb index 104ed6f1..a9f5c3ac 100644 --- a/main.rb +++ b/main.rb @@ -4,142 +4,111 @@ class Main def initialize(options: {}) @source_file_name = options[:source_file_name] || 'data_large.txt' @count_lines = options[:count_lines] - @is_print_memory_usage = options[:is_print_memory_usage] || false + @current_user = nil + @current_stat = nil end def call = work private - attr_reader :source_file_name, :count_lines, :is_print_memory_usage + attr_reader :source_file_name, :count_lines def work `head -n #{count_lines} #{source_file_name} > #{PARTIAL_VOLUME_FILE_NAME}` if count_lines - file_lines = File.read(count_lines ? PARTIAL_VOLUME_FILE_NAME : source_file_name).split("\n") - - users = [] - sessions = [] - - file_lines.each do |line| - cols = line.split(',') - users << parse_user(line) if cols[0] == 'user' - sessions << parse_session(line) if cols[0] == 'session' - 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 - - report['uniqueBrowsersCount'] = uniqueBrowsers.count - - report['totalSessions'] = sessions.count - - report['allBrowsers'] = - sessions - .map { |s| s['browser'] } - .map { |b| b.upcase } - .sort - .uniq - .join(',') - - # Статистика по пользователям - users_objects = [] - - 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 - - report['usersStats'] = {} - - # Собираем количество сессий по пользователям - collect_stats_from_users(report, users_objects) do |user| - { 'sessionsCount' => user.sessions.count } - end - - # Собираем количество времени по пользователям - 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 - - # Выбираем самую длинную сессию пользователя - 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 - - # Браузеры пользователя через запятую - collect_stats_from_users(report, users_objects) do |user| - { 'browsers' => user.sessions.map {|s| s['browser']}.map {|b| b.upcase}.sort.join(', ') } - 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 - - # Всегда использовал только Chrome? - collect_stats_from_users(report, users_objects) do |user| - { 'alwaysUsedChrome' => user.sessions.map{|s| s['browser']}.all? { |b| b.upcase =~ /CHROME/ } } - end - - # Даты сессий через запятую в обратном порядке в формате 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 } } + sessions_count = 0 + users_count = 0 + uniq_browsers = Set.new + + File.open('result.json', 'w') do |result_file| + result_file.write('{"usersStats":{') + + File.foreach(count_lines ? PARTIAL_VOLUME_FILE_NAME : source_file_name).each do |line| + fields = line.split(',') + case fields[0] + when 'session' + session = parse_session(fields) + sessions_count += 1 + uniq_browsers.add(session['browser']) + @current_stat = build_current_stat(@current_user, session) + when 'user' + write_user_stat(result_file) + @current_user = parse_user(fields) + @current_stat = build_initial_stat + users_count += 1 + end + end + + write_user_stat(result_file, with_delimeter: false) + + result_file.write("},\"totalUsers\":#{users_count},\"uniqueBrowsersCount\":#{uniq_browsers.count},\"totalSessions\":#{sessions_count},\"allBrowsers\":\"#{uniq_browsers.sort.join(',')}\"}\n") end - - File.write('result.json', "#{report.to_json}\n") end - def parse_user(user) - fields = user.split(',') - parsed_result = { + def write_user_stat(file, with_delimeter: true) + return unless @current_user + + file.write("\"#{@current_user['key']}\":#{build_final_stat.to_json}") + file.write(",") if with_delimeter + + @current_user = nil + end + + def build_final_stat + { + 'sessionsCount' => @current_stat['sessionsCount'], + 'totalTime' => "#{@current_stat['totalTime']} min.", + 'longestSession' => "#{@current_stat['longestSession']} min.", + 'browsers' => @current_stat['browsers'].sort.join(', '), + 'usedIE' => @current_stat['usedIE'], + 'alwaysUsedChrome' => @current_stat['alwaysUsedChrome'], + 'dates' => @current_stat['dates'].sort.reverse + } + end + + def build_current_stat(user, session) + { + 'sessionsCount' => @current_stat['sessionsCount'] + 1, + 'totalTime' => @current_stat['totalTime'] + session['time'].to_i, + 'longestSession' => @current_stat['longestSession'] > session['time'] ? @current_stat['longestSession'] : session['time'], + 'browsers' => @current_stat['browsers'] << session['browser'], + 'usedIE' => @current_stat['usedIE'] ? true : !!(session['browser'] =~ /INTERNET EXPLORER/), + 'alwaysUsedChrome' => @current_stat['alwaysUsedChrome'] ? !!(session['browser'].upcase =~ /CHROME/) : false, + 'dates' => @current_stat['dates'] << Date.strptime(session['date'], '%Y-%m-%d').iso8601 + } + end + + def build_initial_stat + { + 'sessionsCount' => 0, + 'totalTime' => 0, + 'longestSession' => 0, + 'browsers' => [], + 'usedIE' => false, + 'alwaysUsedChrome' => true, + 'dates' => [] + } + end + + def parse_user(fields) + { 'id' => fields[1], 'first_name' => fields[2], 'last_name' => fields[3], 'age' => fields[4], + 'key' => "#{fields[2]} #{fields[3]}" } 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], - 'time' => fields[4], + 'browser' => fields[3].upcase, + 'time' => fields[4].to_i, 'date' => fields[5], } 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 end diff --git a/measurer.rb b/measurer.rb index 36a2384d..514d6053 100644 --- a/measurer.rb +++ b/measurer.rb @@ -14,19 +14,23 @@ def call = [memory_measurer_thread, work_thread].map(&:join) attr_reader :count_lines - def work_thread = Thread.new { Main.new(options: { count_lines: }).call } + def work_thread = @work_thread ||= Thread.new { Main.new(options: { count_lines: }).call } def memory_measurer_thread = Thread.new do - sleep CHECK_USAGE_INTERVAL_SEC - current_memory_usage_mb = `ps -o rss= -p #{Process.pid}`.to_i / 1024 - if current_memory_usage_mb > MEMORY_USAGE_LIMIT_MB - puts "MEMORY USAGE: #{current_memory_usage_mb} MB, BUT LIMIT: #{MEMORY_USAGE_LIMIT_MB}. TERMINATING." - exit - else - puts "MEMORY USAGE: #{current_memory_usage_mb} MB" + while work_thread.alive? + sleep CHECK_USAGE_INTERVAL_SEC + current_memory_usage_mb = `ps -o rss= -p #{Process.pid}`.to_i / 1024 + if current_memory_usage_mb > MEMORY_USAGE_LIMIT_MB + puts "MEMORY USAGE: #{current_memory_usage_mb} MB, BUT LIMIT: #{MEMORY_USAGE_LIMIT_MB}. TERMINATING." + exit + else + puts "MEMORY USAGE: #{current_memory_usage_mb} MB" + end end end end # Zero iteration: # 15_000 lines: 57 MB (optimizing users and sessions collecting) +# First iteration: +# full: 38 MB (10 sec) (optimizing file stream processing) \ No newline at end of file diff --git a/profiles/memory_profiler b/profiles/memory_profiler index b22436b2..7e7cfdce 100644 --- a/profiles/memory_profiler +++ b/profiles/memory_profiler @@ -1,506 +1,143 @@ -Total allocated: 3791101592 bytes (1471886 objects) -Total retained: 4249 bytes (9 objects) +Total allocated: 19320 bytes (29 objects) +Total retained: 80 bytes (1 objects) allocated memory by gem ----------------------------------- -3791101592 other + 19160 other + 160 set allocated memory by file ----------------------------------- -3791101032 /home/nikita/rails_optimization/task_2/main.rb + 18600 /home/nikita/rails_optimization/task_2/main.rb 560 /home/nikita/rails_optimization/task_2/profiler.rb + 160 /usr/share/rvm/rubies/ruby-3.3.0/lib/ruby/3.3.0/set.rb allocated memory by location ----------------------------------- -2585606480 /home/nikita/rails_optimization/task_2/main.rb:26 - 933571968 /home/nikita/rails_optimization/task_2/main.rb:72 - 85937936 /home/nikita/rails_optimization/task_2/main.rb:25 - 84737936 /home/nikita/rails_optimization/task_2/main.rb:74 - 29275553 /home/nikita/rails_optimization/task_2/main.rb:111 - 14467200 /home/nikita/rails_optimization/task_2/main.rb:24 - 12446720 /home/nikita/rails_optimization/task_2/main.rb:130 - 6559560 /home/nikita/rails_optimization/task_2/main.rb:142 - 5143600 /home/nikita/rails_optimization/task_2/main.rb:144 - 4351680 /home/nikita/rails_optimization/task_2/main.rb:96 - 4065280 /home/nikita/rails_optimization/task_2/main.rb:131 - 3882366 /home/nikita/rails_optimization/task_2/main.rb:18 - 3623624 /home/nikita/rails_optimization/task_2/main.rb:101 - 2731036 /home/nikita/rails_optimization/task_2/main.rb:114 - 2197240 /home/nikita/rails_optimization/task_2/main.rb:106 - 2063760 /home/nikita/rails_optimization/task_2/main.rb:86 - 2063760 /home/nikita/rails_optimization/task_2/main.rb:91 - 2020480 /home/nikita/rails_optimization/task_2/main.rb:120 - 1653120 /home/nikita/rails_optimization/task_2/main.rb:73 - 1470504 /home/nikita/rails_optimization/task_2/main.rb:62 - 937080 /home/nikita/rails_optimization/task_2/main.rb:143 - 734720 /home/nikita/rails_optimization/task_2/main.rb:121 - 734720 /home/nikita/rails_optimization/task_2/main.rb:81 - 229536 /home/nikita/rails_optimization/task_2/main.rb:77 - 203344 /home/nikita/rails_optimization/task_2/main.rb:63 - 203304 /home/nikita/rails_optimization/task_2/main.rb:61 - 183152 /home/nikita/rails_optimization/task_2/main.rb:52 - 2645 /home/nikita/rails_optimization/task_2/main.rb:65 - 1640 /home/nikita/rails_optimization/task_2/main.rb:64 - 728 /home/nikita/rails_optimization/task_2/main.rb:17 + 8680 /home/nikita/rails_optimization/task_2/main.rb:28 + 8560 /home/nikita/rails_optimization/task_2/main.rb:25 + 728 /home/nikita/rails_optimization/task_2/main.rb:18 560 /home/nikita/rails_optimization/task_2/profiler.rb:48 - 160 /home/nikita/rails_optimization/task_2/main.rb:44 - 40 /home/nikita/rails_optimization/task_2/main.rb:20 - 40 /home/nikita/rails_optimization/task_2/main.rb:21 - 40 /home/nikita/rails_optimization/task_2/main.rb:49 + 280 /home/nikita/rails_optimization/task_2/main.rb:46 + 160 /home/nikita/rails_optimization/task_2/main.rb:20 + 160 /usr/share/rvm/rubies/ruby-3.3.0/lib/ruby/3.3.0/set.rb:244 + 112 /home/nikita/rails_optimization/task_2/main.rb:26 + 40 /home/nikita/rails_optimization/task_2/main.rb:23 40 /home/nikita/rails_optimization/task_2/main.rb:5 - 40 /home/nikita/rails_optimization/task_2/main.rb:68 allocated memory by class ----------------------------------- -3706736736 Array - 47273903 String - 21586016 Hash - 13478424 MatchData - 1829376 Date - 183680 User - 8688 File - 3609 Regexp - 560 Proc + 16880 File + 960 Hash + 720 String 248 IO - 120 JSON::Ext::Generator::State + 160 Enumerator + 80 Array 80 Main 80 Process::Status 72 Thread::Mutex + 40 Set allocated objects by gem ----------------------------------- - 1471886 other + 28 other + 1 set allocated objects by file ----------------------------------- - 1471882 /home/nikita/rails_optimization/task_2/main.rb + 24 /home/nikita/rails_optimization/task_2/main.rb 4 /home/nikita/rails_optimization/task_2/profiler.rb + 1 /usr/share/rvm/rubies/ruby-3.3.0/lib/ruby/3.3.0/set.rb allocated objects by location ----------------------------------- - 387380 /home/nikita/rails_optimization/task_2/main.rb:111 - 235408 /home/nikita/rails_optimization/task_2/main.rb:24 - 203264 /home/nikita/rails_optimization/task_2/main.rb:130 - 160720 /home/nikita/rails_optimization/task_2/main.rb:142 - 80816 /home/nikita/rails_optimization/task_2/main.rb:26 - 57062 /home/nikita/rails_optimization/task_2/main.rb:96 - 39184 /home/nikita/rails_optimization/task_2/main.rb:25 - 38868 /home/nikita/rails_optimization/task_2/main.rb:101 - 32151 /home/nikita/rails_optimization/task_2/main.rb:144 - 32144 /home/nikita/rails_optimization/task_2/main.rb:120 - 30005 /home/nikita/rails_optimization/task_2/main.rb:18 - 27552 /home/nikita/rails_optimization/task_2/main.rb:86 - 27552 /home/nikita/rails_optimization/task_2/main.rb:91 - 25409 /home/nikita/rails_optimization/task_2/main.rb:62 - 25408 /home/nikita/rails_optimization/task_2/main.rb:131 - 22611 /home/nikita/rails_optimization/task_2/main.rb:106 - 13776 /home/nikita/rails_optimization/task_2/main.rb:73 - 9184 /home/nikita/rails_optimization/task_2/main.rb:143 - 9184 /home/nikita/rails_optimization/task_2/main.rb:74 - 4592 /home/nikita/rails_optimization/task_2/main.rb:121 - 4592 /home/nikita/rails_optimization/task_2/main.rb:72 - 4592 /home/nikita/rails_optimization/task_2/main.rb:81 - 400 /home/nikita/rails_optimization/task_2/main.rb:52 - 8 /home/nikita/rails_optimization/task_2/main.rb:114 - 7 /home/nikita/rails_optimization/task_2/main.rb:17 + 7 /home/nikita/rails_optimization/task_2/main.rb:18 + 4 /home/nikita/rails_optimization/task_2/main.rb:25 + 4 /home/nikita/rails_optimization/task_2/main.rb:28 + 4 /home/nikita/rails_optimization/task_2/main.rb:46 4 /home/nikita/rails_optimization/task_2/profiler.rb:48 - 2 /home/nikita/rails_optimization/task_2/main.rb:63 - 2 /home/nikita/rails_optimization/task_2/main.rb:65 + 2 /home/nikita/rails_optimization/task_2/main.rb:26 1 /home/nikita/rails_optimization/task_2/main.rb:20 - 1 /home/nikita/rails_optimization/task_2/main.rb:21 - 1 /home/nikita/rails_optimization/task_2/main.rb:44 - 1 /home/nikita/rails_optimization/task_2/main.rb:49 + 1 /home/nikita/rails_optimization/task_2/main.rb:23 1 /home/nikita/rails_optimization/task_2/main.rb:5 - 1 /home/nikita/rails_optimization/task_2/main.rb:61 - 1 /home/nikita/rails_optimization/task_2/main.rb:64 - 1 /home/nikita/rails_optimization/task_2/main.rb:68 - 1 /home/nikita/rails_optimization/task_2/main.rb:77 + 1 /usr/share/rvm/rubies/ruby-3.3.0/lib/ruby/3.3.0/set.rb:244 allocated objects by class ----------------------------------- - 1006313 String - 206678 Array - 133479 Hash - 95399 MatchData - 25408 Date - 4592 User - 7 Proc - 3 Regexp + 13 String + 6 Hash + 2 Array 2 File + 1 Enumerator 1 IO - 1 JSON::Ext::Generator::State 1 Main 1 Process::Status + 1 Set 1 Thread::Mutex retained memory by gem ----------------------------------- - 4249 other + 80 other retained memory by file ----------------------------------- - 4249 /home/nikita/rails_optimization/task_2/main.rb + 80 /home/nikita/rails_optimization/task_2/main.rb retained memory by location ----------------------------------- - 4169 /home/nikita/rails_optimization/task_2/main.rb:111 - 80 /home/nikita/rails_optimization/task_2/main.rb:17 + 80 /home/nikita/rails_optimization/task_2/main.rb:18 retained memory by class ----------------------------------- - 3609 Regexp - 560 String 80 Process::Status retained objects by gem ----------------------------------- - 9 other + 1 other retained objects by file ----------------------------------- - 9 /home/nikita/rails_optimization/task_2/main.rb + 1 /home/nikita/rails_optimization/task_2/main.rb retained objects by location ----------------------------------- - 8 /home/nikita/rails_optimization/task_2/main.rb:111 - 1 /home/nikita/rails_optimization/task_2/main.rb:17 + 1 /home/nikita/rails_optimization/task_2/main.rb:18 retained objects by class ----------------------------------- - 5 String - 3 Regexp 1 Process::Status Allocated String Report ----------------------------------- - 133776 " " - 101632 /home/nikita/rails_optimization/task_2/main.rb:111 - 32144 /home/nikita/rails_optimization/task_2/main.rb:142 + 2 "" + 1 /home/nikita/rails_optimization/task_2/main.rb:18 + 1 /home/nikita/rails_optimization/task_2/main.rb:46 - 80816 "session" - 30000 /home/nikita/rails_optimization/task_2/main.rb:26 - 25408 /home/nikita/rails_optimization/task_2/main.rb:130 - 25408 /home/nikita/rails_optimization/task_2/main.rb:24 + 2 "head -n 50000 data_large.txt > dataN.txt" + 2 /home/nikita/rails_optimization/task_2/main.rb:18 - 60001 "," - 30000 /home/nikita/rails_optimization/task_2/main.rb:24 - 25408 /home/nikita/rails_optimization/task_2/main.rb:130 - 4592 /home/nikita/rails_optimization/task_2/main.rb:120 - 1 /home/nikita/rails_optimization/task_2/main.rb:65 + 2 "result.json" + 2 /home/nikita/rails_optimization/task_2/main.rb:25 - 39184 "user" - 30000 /home/nikita/rails_optimization/task_2/main.rb:25 - 4592 /home/nikita/rails_optimization/task_2/main.rb:120 - 4592 /home/nikita/rails_optimization/task_2/main.rb:24 + 1 "," + 1 /home/nikita/rails_optimization/task_2/main.rb:46 - 9702 "0" - 4849 /home/nikita/rails_optimization/task_2/main.rb:24 - 4808 /home/nikita/rails_optimization/task_2/main.rb:130 - 41 /home/nikita/rails_optimization/task_2/main.rb:120 - 2 /home/nikita/rails_optimization/task_2/main.rb:86 - 2 /home/nikita/rails_optimization/task_2/main.rb:91 + 1 "50000" + 1 /home/nikita/rails_optimization/task_2/main.rb:18 - 9414 "2017" - 9400 /home/nikita/rails_optimization/task_2/main.rb:111 - 7 /home/nikita/rails_optimization/task_2/main.rb:24 - 6 /home/nikita/rails_optimization/task_2/main.rb:130 - 1 /home/nikita/rails_optimization/task_2/main.rb:120 + 1 "dataN.txt" + 1 /home/nikita/rails_optimization/task_2/main.rb:28 - 9194 "2018" - 9172 /home/nikita/rails_optimization/task_2/main.rb:111 - 11 /home/nikita/rails_optimization/task_2/main.rb:24 - 10 /home/nikita/rails_optimization/task_2/main.rb:130 - 1 /home/nikita/rails_optimization/task_2/main.rb:120 - - 9184 " min." - 4592 /home/nikita/rails_optimization/task_2/main.rb:86 - 4592 /home/nikita/rails_optimization/task_2/main.rb:91 - - 8770 "1" - 4379 /home/nikita/rails_optimization/task_2/main.rb:24 - 4332 /home/nikita/rails_optimization/task_2/main.rb:130 - 47 /home/nikita/rails_optimization/task_2/main.rb:120 - 6 /home/nikita/rails_optimization/task_2/main.rb:86 - 6 /home/nikita/rails_optimization/task_2/main.rb:91 - - 7946 "2" - 3965 /home/nikita/rails_optimization/task_2/main.rb:24 - 3908 /home/nikita/rails_optimization/task_2/main.rb:130 - 57 /home/nikita/rails_optimization/task_2/main.rb:120 - 8 /home/nikita/rails_optimization/task_2/main.rb:86 - 8 /home/nikita/rails_optimization/task_2/main.rb:91 - - 6993 "3" - 3496 /home/nikita/rails_optimization/task_2/main.rb:24 - 3447 /home/nikita/rails_optimization/task_2/main.rb:130 - 49 /home/nikita/rails_optimization/task_2/main.rb:120 - 1 /home/nikita/rails_optimization/task_2/main.rb:91 - - 6014 "4" - 3006 /home/nikita/rails_optimization/task_2/main.rb:24 - 2955 /home/nikita/rails_optimization/task_2/main.rb:130 - 51 /home/nikita/rails_optimization/task_2/main.rb:120 - 1 /home/nikita/rails_optimization/task_2/main.rb:86 - 1 /home/nikita/rails_optimization/task_2/main.rb:91 - - 5762 "2016" - 5754 /home/nikita/rails_optimization/task_2/main.rb:111 - 4 /home/nikita/rails_optimization/task_2/main.rb:24 - 3 /home/nikita/rails_optimization/task_2/main.rb:130 - 1 /home/nikita/rails_optimization/task_2/main.rb:120 - - 5113 "5" - 2556 /home/nikita/rails_optimization/task_2/main.rb:24 - 2513 /home/nikita/rails_optimization/task_2/main.rb:130 - 43 /home/nikita/rails_optimization/task_2/main.rb:120 - 1 /home/nikita/rails_optimization/task_2/main.rb:86 - - 4592 ", " - 4592 /home/nikita/rails_optimization/task_2/main.rb:96 - - 4216 "6" - 2105 /home/nikita/rails_optimization/task_2/main.rb:24 - 2061 /home/nikita/rails_optimization/task_2/main.rb:130 - 44 /home/nikita/rails_optimization/task_2/main.rb:120 - 3 /home/nikita/rails_optimization/task_2/main.rb:86 - 3 /home/nikita/rails_optimization/task_2/main.rb:91 - - 3741 "12" - 3208 /home/nikita/rails_optimization/task_2/main.rb:111 - 258 /home/nikita/rails_optimization/task_2/main.rb:24 - 205 /home/nikita/rails_optimization/task_2/main.rb:130 - 53 /home/nikita/rails_optimization/task_2/main.rb:120 - 9 /home/nikita/rails_optimization/task_2/main.rb:86 - 8 /home/nikita/rails_optimization/task_2/main.rb:91 - - 3656 "11" - 3068 /home/nikita/rails_optimization/task_2/main.rb:111 - 282 /home/nikita/rails_optimization/task_2/main.rb:24 - 245 /home/nikita/rails_optimization/task_2/main.rb:130 - 37 /home/nikita/rails_optimization/task_2/main.rb:120 - 12 /home/nikita/rails_optimization/task_2/main.rb:86 - 12 /home/nikita/rails_optimization/task_2/main.rb:91 - - 3655 "10" - 3135 /home/nikita/rails_optimization/task_2/main.rb:111 - 256 /home/nikita/rails_optimization/task_2/main.rb:24 - 209 /home/nikita/rails_optimization/task_2/main.rb:130 - 47 /home/nikita/rails_optimization/task_2/main.rb:120 - 4 /home/nikita/rails_optimization/task_2/main.rb:86 - 4 /home/nikita/rails_optimization/task_2/main.rb:91 - - 3422 "7" - 1711 /home/nikita/rails_optimization/task_2/main.rb:24 - 1663 /home/nikita/rails_optimization/task_2/main.rb:130 - 48 /home/nikita/rails_optimization/task_2/main.rb:120 - - 3283 "08" - 3283 /home/nikita/rails_optimization/task_2/main.rb:111 - - 3276 "07" - 3276 /home/nikita/rails_optimization/task_2/main.rb:111 - - 3226 "01" - 3226 /home/nikita/rails_optimization/task_2/main.rb:111 - - 3129 "06" - 3129 /home/nikita/rails_optimization/task_2/main.rb:111 - - 3113 "09" - 3113 /home/nikita/rails_optimization/task_2/main.rb:111 - - 2736 "05" - 2736 /home/nikita/rails_optimization/task_2/main.rb:111 - - 2535 "02" - 2535 /home/nikita/rails_optimization/task_2/main.rb:111 - - 2474 "8" - 1229 /home/nikita/rails_optimization/task_2/main.rb:24 - 1189 /home/nikita/rails_optimization/task_2/main.rb:130 - 40 /home/nikita/rails_optimization/task_2/main.rb:120 - 8 /home/nikita/rails_optimization/task_2/main.rb:86 - 8 /home/nikita/rails_optimization/task_2/main.rb:91 - - 2386 "03" - 2386 /home/nikita/rails_optimization/task_2/main.rb:111 - - 2378 "04" - 2378 /home/nikita/rails_optimization/task_2/main.rb:111 - - 1473 "9" - 730 /home/nikita/rails_optimization/task_2/main.rb:24 - 683 /home/nikita/rails_optimization/task_2/main.rb:130 - 47 /home/nikita/rails_optimization/task_2/main.rb:120 - 7 /home/nikita/rails_optimization/task_2/main.rb:91 - 6 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1413 "14" - 819 /home/nikita/rails_optimization/task_2/main.rb:111 - 287 /home/nikita/rails_optimization/task_2/main.rb:24 - 242 /home/nikita/rails_optimization/task_2/main.rb:130 - 45 /home/nikita/rails_optimization/task_2/main.rb:120 - 11 /home/nikita/rails_optimization/task_2/main.rb:91 - 9 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1411 "20" - 898 /home/nikita/rails_optimization/task_2/main.rb:111 - 253 /home/nikita/rails_optimization/task_2/main.rb:24 - 199 /home/nikita/rails_optimization/task_2/main.rb:130 - 54 /home/nikita/rails_optimization/task_2/main.rb:120 - 5 /home/nikita/rails_optimization/task_2/main.rb:91 - 2 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1411 "27" - 816 /home/nikita/rails_optimization/task_2/main.rb:111 - 293 /home/nikita/rails_optimization/task_2/main.rb:24 - 241 /home/nikita/rails_optimization/task_2/main.rb:130 - 52 /home/nikita/rails_optimization/task_2/main.rb:120 - 6 /home/nikita/rails_optimization/task_2/main.rb:91 - 3 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1406 "29" - 814 /home/nikita/rails_optimization/task_2/main.rb:111 - 281 /home/nikita/rails_optimization/task_2/main.rb:24 - 241 /home/nikita/rails_optimization/task_2/main.rb:130 - 40 /home/nikita/rails_optimization/task_2/main.rb:120 - 17 /home/nikita/rails_optimization/task_2/main.rb:91 - 13 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1401 "24" - 892 /home/nikita/rails_optimization/task_2/main.rb:111 - 250 /home/nikita/rails_optimization/task_2/main.rb:24 - 202 /home/nikita/rails_optimization/task_2/main.rb:130 - 48 /home/nikita/rails_optimization/task_2/main.rb:120 - 6 /home/nikita/rails_optimization/task_2/main.rb:91 - 3 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1398 "25" - 858 /home/nikita/rails_optimization/task_2/main.rb:111 - 265 /home/nikita/rails_optimization/task_2/main.rb:24 - 228 /home/nikita/rails_optimization/task_2/main.rb:130 - 37 /home/nikita/rails_optimization/task_2/main.rb:120 - 6 /home/nikita/rails_optimization/task_2/main.rb:91 - 4 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1385 "19" - 847 /home/nikita/rails_optimization/task_2/main.rb:111 - 266 /home/nikita/rails_optimization/task_2/main.rb:24 - 221 /home/nikita/rails_optimization/task_2/main.rb:130 - 45 /home/nikita/rails_optimization/task_2/main.rb:120 - 3 /home/nikita/rails_optimization/task_2/main.rb:86 - 3 /home/nikita/rails_optimization/task_2/main.rb:91 - - 1373 "21" - 822 /home/nikita/rails_optimization/task_2/main.rb:111 - 270 /home/nikita/rails_optimization/task_2/main.rb:24 - 226 /home/nikita/rails_optimization/task_2/main.rb:130 - 44 /home/nikita/rails_optimization/task_2/main.rb:120 - 6 /home/nikita/rails_optimization/task_2/main.rb:91 - 5 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1369 "26" - 823 /home/nikita/rails_optimization/task_2/main.rb:111 - 268 /home/nikita/rails_optimization/task_2/main.rb:24 - 224 /home/nikita/rails_optimization/task_2/main.rb:130 - 44 /home/nikita/rails_optimization/task_2/main.rb:120 - 6 /home/nikita/rails_optimization/task_2/main.rb:86 - 4 /home/nikita/rails_optimization/task_2/main.rb:91 - - 1363 "18" - 813 /home/nikita/rails_optimization/task_2/main.rb:111 - 271 /home/nikita/rails_optimization/task_2/main.rb:24 - 229 /home/nikita/rails_optimization/task_2/main.rb:130 - 42 /home/nikita/rails_optimization/task_2/main.rb:120 - 4 /home/nikita/rails_optimization/task_2/main.rb:86 - 4 /home/nikita/rails_optimization/task_2/main.rb:91 - - 1356 "28" - 833 /home/nikita/rails_optimization/task_2/main.rb:111 - 257 /home/nikita/rails_optimization/task_2/main.rb:24 - 208 /home/nikita/rails_optimization/task_2/main.rb:130 - 49 /home/nikita/rails_optimization/task_2/main.rb:120 - 7 /home/nikita/rails_optimization/task_2/main.rb:91 - 2 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1345 "16" - 817 /home/nikita/rails_optimization/task_2/main.rb:111 - 261 /home/nikita/rails_optimization/task_2/main.rb:24 - 198 /home/nikita/rails_optimization/task_2/main.rb:130 - 63 /home/nikita/rails_optimization/task_2/main.rb:120 - 3 /home/nikita/rails_optimization/task_2/main.rb:86 - 3 /home/nikita/rails_optimization/task_2/main.rb:91 - - 1343 "22" - 853 /home/nikita/rails_optimization/task_2/main.rb:111 - 239 /home/nikita/rails_optimization/task_2/main.rb:24 - 193 /home/nikita/rails_optimization/task_2/main.rb:130 - 46 /home/nikita/rails_optimization/task_2/main.rb:120 - 8 /home/nikita/rails_optimization/task_2/main.rb:91 - 4 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1337 "17" - 801 /home/nikita/rails_optimization/task_2/main.rb:111 - 267 /home/nikita/rails_optimization/task_2/main.rb:24 - 228 /home/nikita/rails_optimization/task_2/main.rb:130 - 39 /home/nikita/rails_optimization/task_2/main.rb:120 - 1 /home/nikita/rails_optimization/task_2/main.rb:86 - 1 /home/nikita/rails_optimization/task_2/main.rb:91 - - 1317 "13" - 774 /home/nikita/rails_optimization/task_2/main.rb:111 - 266 /home/nikita/rails_optimization/task_2/main.rb:24 - 218 /home/nikita/rails_optimization/task_2/main.rb:130 - 48 /home/nikita/rails_optimization/task_2/main.rb:120 - 7 /home/nikita/rails_optimization/task_2/main.rb:91 - 4 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1316 "30" - 749 /home/nikita/rails_optimization/task_2/main.rb:111 - 276 /home/nikita/rails_optimization/task_2/main.rb:24 - 232 /home/nikita/rails_optimization/task_2/main.rb:130 - 44 /home/nikita/rails_optimization/task_2/main.rb:120 - 8 /home/nikita/rails_optimization/task_2/main.rb:91 - 7 /home/nikita/rails_optimization/task_2/main.rb:86 - - 1314 "23" - 778 /home/nikita/rails_optimization/task_2/main.rb:111 - 264 /home/nikita/rails_optimization/task_2/main.rb:24 - 214 /home/nikita/rails_optimization/task_2/main.rb:130 - 50 /home/nikita/rails_optimization/task_2/main.rb:120 - 4 /home/nikita/rails_optimization/task_2/main.rb:86 - 4 /home/nikita/rails_optimization/task_2/main.rb:91 - - 1308 "15" - 817 /home/nikita/rails_optimization/task_2/main.rb:111 - 241 /home/nikita/rails_optimization/task_2/main.rb:24 - 193 /home/nikita/rails_optimization/task_2/main.rb:130 - 48 /home/nikita/rails_optimization/task_2/main.rb:120 - 5 /home/nikita/rails_optimization/task_2/main.rb:86 - 4 /home/nikita/rails_optimization/task_2/main.rb:91 - - 1090 "2019" - 1082 /home/nikita/rails_optimization/task_2/main.rb:111 - 4 /home/nikita/rails_optimization/task_2/main.rb:24 - 3 /home/nikita/rails_optimization/task_2/main.rb:130 - 1 /home/nikita/rails_optimization/task_2/main.rb:120 - - -Retained String Report ------------------------------------ - 1 "('?[-+]?(?d$odbTc!21OdAvJ;>0-Fd>?%k$?oGMZ-i$B)Vv-Yu?OUy1J^Z zsvbTaiE-r$*|^1(zeE!kE{vZ<6BoKM#*HLy)Sc0V3B-(;5bwROK0Pf&WL2-K-aGf4 zbML$FJ&+v~^}?#~d~ltm>}+Gdp6j}cLLYP6=Y$pLMQ{S)t(rEddcillJzMxNqmM6J zt>`VKPjtce#p2MHVpjy_q8XTjls>lPnO)%z>h_-O*S9(+u3R1z^qhm=s*?+zy9BoI zYNiz+13gD?&FW*?irW>M104uV%`?IFTW&9aE?hEco)(yXr?p?}h07aWTh-O0r}dl- zj(`LDhQJL>J0>(^;D*7^e#4!oFw8K7yK z|CV%(QdNtohCy4VZMzoJ@muYSEk7`=PK$mT$pL|CF zduJQ72O6`Q4Q&lyweyJP6B?D4)CMEkq=-=4xmeq4Kl~aIdxVJ{ad8V>2x3|-aiQl4 zpLQHEJa(T9EeV_OOV6iA7)p;qhUt^(nB(^x7=B1x~LYkkW(qr!-Nhd_E+Q_O8^y^{_Ue_&ogZWjVxZ&VurdR1M0P(cp{N z2f2s}yt*|e?ZdJ|y(2J#zK=#MkP)|(q0qG3q~6GgRAPjT&X~JmOkr9pE!;pYTdWF+ zExN)%TO@p87@mqgg2&R0Q3a#Fa@DIY`$ct6IL6Sxsn!bX5C z)mr&?Cm_!y6VCDVSL1RkA*lvt%ZuC`ZH&<}?rugLdtt{o`bx16JxWd)|Fb;B1wpNX zyF=3HiKiqN);kmvT8=kebjii}ZSCug3xhbYujv9k%QADC@hv{qcVTYi3(wKB`}x2o0gu=D9HpWB$^etkqD3`uAOO6#T;g+J?jCOx?Vw#! ze(x(h0$~#ld)IhK4eh&kc*qlef0HKg73D8Rps#1v;^A8QTbe-J3a|;el_3LLBuChW z&kUr1vbqIFJMfuB_7CGTkKgC;S>!v8y#EYo3GWH4r5tLZJ4_-R<b)AR^hD!z~ z%fw1snkQ@LJ)lcTG%Jh2lAQCbab&dQdURSO^QiXoh*su;kQIK^>34-^2Ci3IKbb%y z3&LVbDu`oDg==w>)-1QrXHaEfP|M5agvIv=Z_IRE}t*{z-7o zx$|?^Hf`yHHTT?m?m6H2y=T3lWxm5`pJuM$jy={gkbA&rtvOX@v^nKTR`HAuZhOp~ zHZ7ho+J#xGTTJlyGmXtg=Xj+QY{ZPaYg`CcRdzaR%rmQ|XU@lrwlUYNF)_c-+S#&F zpZI(K+bH*Z31A z^5s2KOqCwU*Zu1`x12B%M@}297PlE6G`c;*CPae$HpC|CdVpO5?J zcnd5q|Lc!$!!rIfSe8NwVm`qK&nC%Mj?6vhCGJAHFCSv|wU#Nl*)4@;hB}G z5*`*hi|vIB&lkE$5+H~b?k45Z53~!?>u;R^uSlMa^GQAssE+aea2fK=w*iXoQ>X=@ zQ4vndR6+Yg~CkiNR1m|p0VNJYwh=N*3 zkMwOC@|=3_c9)%RaF_8yP)h7~gcecm{LC<1CbVG1C(aP7^|8(jO1vc49sL86o~-wp|UZ)l0&<2E)a z&-5As-1I2A=|im<503tpz7bAhN26!Cb{0Fg(h*mBO*dxf)Es_g}MZi*Hib_|ugbdeyxz^HV z?rf?V={X$cLnbto+8^1Z{jgc!T9aQ9x_RHL zDwksgB~E(SGKI+6Y$o!U{!SLg0R8PO3<7A#^1o^Hh?aD7<8km-mfPIpFnm2h57O?> zI1{0xqxRMczDt0{1m#r$-({sbJHj4Bst)sr?w5&?a;^3`1}){^u(?K;4R| z@)34=o`z-pCwGF`srMxsqvAh!J`M*<8zfcyM=MaheL;qNCp=6vM%>;XMpID0`wIm{J~DYl?7t& zU!Dj&9RiDindAlt^f@BJ3G9b8?1j&=od|Ne9Wwmn?F@>m<~&j698sK}9D$$x+TrTp)V@*fBG zs9t1M-~il-VxdHM3_>g`V?G71dx!s6JRX&N8p$ABy zWVMn@WdEs~u3)8xU~bSDz5|YdY6gK26W=p_1Aws7lez$o(H9rU-B03LCILW|4M5s2 zq3(=uLR&zoN^>9}_fT(=KtwR{$|j{(wOViC^v9&rK-`M!+=1eMT;*#tAW!}VTRHHP(jn} z{RoKu+p@Kiz92QLFJc|km*vso0)(-ZYv0E(-Vi#Ezd8%8?R!XqDkyO662{bTMA{-A ze7v=lcZABw;0`}RO3KA!_fv4-6Jn9P$4oJiJ$r8d`i_y%q|1r3#@ulw=#c&B^2G&^ zDjyu_@)7tEHxB>m@SGC*EC@Oj5^&RtBp)HQm`!``s|xQO0lcePlCC^iq$us1y8lV# z=7n>pMS0Mgv{;s^MeJEei}L)*tB2u-a*ir9QWJ-F5$;k|%71h$zaXDddKI6!K|zgQ z+kqaJN76Sw0NRf?#UyOMhp{5V_B)>aMVAQqKN>GEwR{%)DlwJ8|!O_|(Z0hH+%pGmNh>o4KavxXSW4^Cq0CFbql+ hi2bC%U+qEogF2Q|0!i$E$6?0h_-}}SD+P9?{tNBjCtm;n diff --git a/spec/main_spec.rb b/spec/main_spec.rb index 46e6232d..92c6f6c3 100644 --- a/spec/main_spec.rb +++ b/spec/main_spec.rb @@ -27,7 +27,7 @@ it "записывает в файл корректный результат" do Main.new(options: { source_file_name: "data.txt" }).call - expected_result = '{"totalUsers":3,"uniqueBrowsersCount":14,"totalSessions":15,"allBrowsers":"CHROME 13,CHROME 20,CHROME 35,CHROME 6,FIREFOX 12,FIREFOX 32,FIREFOX 47,INTERNET EXPLORER 10,INTERNET EXPLORER 28,INTERNET EXPLORER 35,SAFARI 17,SAFARI 29,SAFARI 39,SAFARI 49","usersStats":{"Leida Cira":{"sessionsCount":6,"totalTime":"455 min.","longestSession":"118 min.","browsers":"FIREFOX 12, INTERNET EXPLORER 28, INTERNET EXPLORER 28, INTERNET EXPLORER 35, SAFARI 29, SAFARI 39","usedIE":true,"alwaysUsedChrome":false,"dates":["2017-09-27","2017-03-28","2017-02-27","2016-10-23","2016-09-15","2016-09-01"]},"Palmer Katrina":{"sessionsCount":5,"totalTime":"218 min.","longestSession":"116 min.","browsers":"CHROME 13, CHROME 6, FIREFOX 32, INTERNET EXPLORER 10, SAFARI 17","usedIE":true,"alwaysUsedChrome":false,"dates":["2017-04-29","2016-12-28","2016-12-20","2016-11-11","2016-10-21"]},"Gregory Santos":{"sessionsCount":4,"totalTime":"192 min.","longestSession":"85 min.","browsers":"CHROME 20, CHROME 35, FIREFOX 47, SAFARI 49","usedIE":false,"alwaysUsedChrome":false,"dates":["2018-09-21","2018-02-02","2017-05-22","2016-11-25"]}}}' + "\n" + expected_result = '{"usersStats":{"Leida Cira":{"sessionsCount":6,"totalTime":"455 min.","longestSession":"118 min.","browsers":"FIREFOX 12, INTERNET EXPLORER 28, INTERNET EXPLORER 28, INTERNET EXPLORER 35, SAFARI 29, SAFARI 39","usedIE":true,"alwaysUsedChrome":false,"dates":["2017-09-27","2017-03-28","2017-02-27","2016-10-23","2016-09-15","2016-09-01"]},"Palmer Katrina":{"sessionsCount":5,"totalTime":"218 min.","longestSession":"116 min.","browsers":"CHROME 13, CHROME 6, FIREFOX 32, INTERNET EXPLORER 10, SAFARI 17","usedIE":true,"alwaysUsedChrome":false,"dates":["2017-04-29","2016-12-28","2016-12-20","2016-11-11","2016-10-21"]},"Gregory Santos":{"sessionsCount":4,"totalTime":"192 min.","longestSession":"85 min.","browsers":"CHROME 20, CHROME 35, FIREFOX 47, SAFARI 49","usedIE":false,"alwaysUsedChrome":false,"dates":["2018-09-21","2018-02-02","2017-05-22","2016-11-25"]}},"totalUsers":3,"uniqueBrowsersCount":14,"totalSessions":15,"allBrowsers":"CHROME 13,CHROME 20,CHROME 35,CHROME 6,FIREFOX 12,FIREFOX 32,FIREFOX 47,INTERNET EXPLORER 10,INTERNET EXPLORER 28,INTERNET EXPLORER 35,SAFARI 17,SAFARI 29,SAFARI 39,SAFARI 49"}' + "\n" expect(File.read('result.json')).to eq(expected_result) end end diff --git a/spec/performance_spec.rb b/spec/performance_spec.rb new file mode 100644 index 00000000..36d7d44e --- /dev/null +++ b/spec/performance_spec.rb @@ -0,0 +1,8 @@ +require_relative "spec_helper" + +describe Main do + it "использование памяти не превышает заданный лимит" do + described_class.new.call + expect((`ps -o rss= -p #{Process.pid}`.to_i / 1024)).to be < 70 + end +end