Skip to content

Commit 6b6ca76

Browse files
NikitaNikita
authored andcommitted
[zero] create core classes and initial measure
1 parent 9c9410e commit 6b6ca76

File tree

12 files changed

+910
-173
lines changed

12 files changed

+910
-173
lines changed

.rspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--require spec_helper

Gemfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
source "https://rubygems.org"
4+
5+
6+
gem 'stackprof'
7+
gem 'ruby-prof'
8+
gem 'memory_profiler'
9+
gem 'rspec'
10+
gem 'byebug'

Gemfile.lock

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
GEM
2+
remote: https://rubygems.org/
3+
specs:
4+
byebug (11.1.3)
5+
diff-lcs (1.6.0)
6+
memory_profiler (1.1.0)
7+
rspec (3.13.0)
8+
rspec-core (~> 3.13.0)
9+
rspec-expectations (~> 3.13.0)
10+
rspec-mocks (~> 3.13.0)
11+
rspec-core (3.13.3)
12+
rspec-support (~> 3.13.0)
13+
rspec-expectations (3.13.3)
14+
diff-lcs (>= 1.2.0, < 2.0)
15+
rspec-support (~> 3.13.0)
16+
rspec-mocks (3.13.2)
17+
diff-lcs (>= 1.2.0, < 2.0)
18+
rspec-support (~> 3.13.0)
19+
rspec-support (3.13.2)
20+
ruby-prof (1.7.1)
21+
stackprof (0.2.27)
22+
23+
PLATFORMS
24+
ruby
25+
x86_64-linux
26+
27+
DEPENDENCIES
28+
byebug
29+
memory_profiler
30+
rspec
31+
ruby-prof
32+
stackprof
33+
34+
BUNDLED WITH
35+
2.5.18

main.rb

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
class Main
2+
PARTIAL_VOLUME_FILE_NAME = "dataN.txt"
3+
4+
def initialize(options: {})
5+
@source_file_name = options[:source_file_name] || 'data_large.txt'
6+
@count_lines = options[:count_lines]
7+
@is_print_memory_usage = options[:is_print_memory_usage] || false
8+
end
9+
10+
def call = work
11+
12+
private
13+
14+
attr_reader :source_file_name, :count_lines, :is_print_memory_usage
15+
16+
def work
17+
`head -n #{count_lines} #{source_file_name} > #{PARTIAL_VOLUME_FILE_NAME}` if count_lines
18+
file_lines = File.read(count_lines ? PARTIAL_VOLUME_FILE_NAME : source_file_name).split("\n")
19+
20+
users = []
21+
sessions = []
22+
23+
file_lines.each do |line|
24+
cols = line.split(',')
25+
users = users + [parse_user(line)] if cols[0] == 'user'
26+
sessions = sessions + [parse_session(line)] if cols[0] == 'session'
27+
end
28+
29+
# Отчёт в json
30+
# - Сколько всего юзеров +
31+
# - Сколько всего уникальных браузеров +
32+
# - Сколько всего сессий +
33+
# - Перечислить уникальные браузеры в алфавитном порядке через запятую и капсом +
34+
#
35+
# - По каждому пользователю
36+
# - сколько всего сессий +
37+
# - сколько всего времени +
38+
# - самая длинная сессия +
39+
# - браузеры через запятую +
40+
# - Хоть раз использовал IE? +
41+
# - Всегда использовал только Хром? +
42+
# - даты сессий в порядке убывания через запятую +
43+
44+
report = {}
45+
46+
report[:totalUsers] = users.count
47+
48+
# Подсчёт количества уникальных браузеров
49+
uniqueBrowsers = []
50+
sessions.each do |session|
51+
browser = session['browser']
52+
uniqueBrowsers += [browser] if uniqueBrowsers.all? { |b| b != browser }
53+
end
54+
55+
report['uniqueBrowsersCount'] = uniqueBrowsers.count
56+
57+
report['totalSessions'] = sessions.count
58+
59+
report['allBrowsers'] =
60+
sessions
61+
.map { |s| s['browser'] }
62+
.map { |b| b.upcase }
63+
.sort
64+
.uniq
65+
.join(',')
66+
67+
# Статистика по пользователям
68+
users_objects = []
69+
70+
users.each do |user|
71+
attributes = user
72+
user_sessions = sessions.select { |session| session['user_id'] == user['id'] }
73+
user_object = User.new(attributes: attributes, sessions: user_sessions)
74+
users_objects = users_objects + [user_object]
75+
end
76+
77+
report['usersStats'] = {}
78+
79+
# Собираем количество сессий по пользователям
80+
collect_stats_from_users(report, users_objects) do |user|
81+
{ 'sessionsCount' => user.sessions.count }
82+
end
83+
84+
# Собираем количество времени по пользователям
85+
collect_stats_from_users(report, users_objects) do |user|
86+
{ 'totalTime' => user.sessions.map {|s| s['time']}.map {|t| t.to_i}.sum.to_s + ' min.' }
87+
end
88+
89+
# Выбираем самую длинную сессию пользователя
90+
collect_stats_from_users(report, users_objects) do |user|
91+
{ 'longestSession' => user.sessions.map {|s| s['time']}.map {|t| t.to_i}.max.to_s + ' min.' }
92+
end
93+
94+
# Браузеры пользователя через запятую
95+
collect_stats_from_users(report, users_objects) do |user|
96+
{ 'browsers' => user.sessions.map {|s| s['browser']}.map {|b| b.upcase}.sort.join(', ') }
97+
end
98+
99+
# Хоть раз использовал IE?
100+
collect_stats_from_users(report, users_objects) do |user|
101+
{ 'usedIE' => user.sessions.map{|s| s['browser']}.any? { |b| b.upcase =~ /INTERNET EXPLORER/ } }
102+
end
103+
104+
# Всегда использовал только Chrome?
105+
collect_stats_from_users(report, users_objects) do |user|
106+
{ 'alwaysUsedChrome' => user.sessions.map{|s| s['browser']}.all? { |b| b.upcase =~ /CHROME/ } }
107+
end
108+
109+
# Даты сессий через запятую в обратном порядке в формате iso8601
110+
collect_stats_from_users(report, users_objects) do |user|
111+
{ 'dates' => user.sessions.map{|s| s['date']}.map {|d| Date.parse(d)}.sort.reverse.map { |d| d.iso8601 } }
112+
end
113+
114+
File.write('result.json', "#{report.to_json}\n")
115+
116+
puts "MEMORY USAGE: #{(`ps -o rss= -p #{Process.pid}`.to_i / 1024)} MB" if is_print_memory_usage
117+
end
118+
119+
def parse_user(user)
120+
fields = user.split(',')
121+
parsed_result = {
122+
'id' => fields[1],
123+
'first_name' => fields[2],
124+
'last_name' => fields[3],
125+
'age' => fields[4],
126+
}
127+
end
128+
129+
def parse_session(session)
130+
fields = session.split(',')
131+
parsed_result = {
132+
'user_id' => fields[1],
133+
'session_id' => fields[2],
134+
'browser' => fields[3],
135+
'time' => fields[4],
136+
'date' => fields[5],
137+
}
138+
end
139+
140+
def collect_stats_from_users(report, users_objects, &block)
141+
users_objects.each do |user|
142+
user_key = "#{user.attributes['first_name']}" + ' ' + "#{user.attributes['last_name']}"
143+
report['usersStats'][user_key] ||= {}
144+
report['usersStats'][user_key] = report['usersStats'][user_key].merge(block.call(user))
145+
end
146+
end
147+
end

measurer.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
require_relative 'main'
2+
3+
class Measurer
4+
def call
5+
Main.new(options: { count_lines: 15_000, is_print_memory_usage: true }).call
6+
end
7+
end
8+
9+
# Zero iteration:
10+
# 15_000 lines: 103 MB

profiler.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
require 'memory_profiler'
2+
require 'stackprof'
3+
require 'ruby-prof'
4+
5+
require_relative 'main'
6+
7+
class Profiler
8+
def initialize(count_lines:)
9+
@count_lines = count_lines
10+
end
11+
12+
def call
13+
profile_by_memory_profiler
14+
profile_memory_by_ruby_prof
15+
profile_allocations_by_ruby_prof
16+
profile_allocations_by_stack_prof
17+
end
18+
19+
private
20+
21+
attr_reader :count_lines
22+
23+
def profile_by_memory_profiler
24+
report = MemoryProfiler.report { action }
25+
report.pretty_print(to_file: './profiles/memory_profiler')
26+
end
27+
28+
def profile_memory_by_ruby_prof
29+
RubyProf.measure_mode = RubyProf::MEMORY
30+
result = RubyProf.profile { action }
31+
printer = RubyProf::CallTreePrinter.new(result)
32+
printer.print(:path => ".", :profile => "profiles/ruby_prof_memory_profile")
33+
end
34+
35+
def profile_allocations_by_ruby_prof
36+
RubyProf.measure_mode = RubyProf::ALLOCATIONS
37+
result = RubyProf.profile { action }
38+
printer = RubyProf::CallTreePrinter.new(result)
39+
printer.print(:path => ".", :profile => "profiles/ruby_prof_allocations_profile")
40+
end
41+
42+
def profile_allocations_by_stack_prof
43+
StackProf.run(mode: :object, out: 'profiles/stackprof.dump') do
44+
action
45+
end
46+
end
47+
48+
def action = Main.new(options: { count_lines: }).call
49+
end

0 commit comments

Comments
 (0)