Class: Perfer::IterationJob
- Inherits:
-
Job
- Object
- Job
- Perfer::IterationJob
show all
- Defined in:
- lib/perfer/job/iteration_job.rb
Constant Summary
collapse
- CHANGE_ITERATIONS_MARGIN =
This factor ensure some margin, to avoid endlessly changing the number of iterations
0.1
- MAX_GROW_FACTOR =
10
- UNIQUE_NAME =
"a"
Instance Attribute Summary
Attributes inherited from Job
#metadata, #session, #title
Instance Method Summary
collapse
Methods inherited from Job
#load_metadata, #minimal_time, #number_of_measurements, #results, #verbose
Constructor Details
#initialize(session, title, code = nil, data = nil, &block) ⇒ IterationJob
Returns a new instance of IterationJob.
13
14
15
16
17
|
# File 'lib/perfer/job/iteration_job.rb', line 13
def initialize(session, title, code = nil, data = nil, &block)
super(session, title, &block)
load_metadata
compile_method(code, data) if code and !block
end
|
Instance Method Details
#compile_method(code, data) ⇒ Object
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
# File 'lib/perfer/job/iteration_job.rb', line 19
def compile_method(code, data)
@data = data || {}
if obj = @data.delete(:self)
klass = obj.singleton_class
meth = generate_method_name
else
klass = singleton_class
meth = :measure_call_times_code
end
if klass.method_defined?(meth)
raise Error, "method #{meth} already defined on #{klass} (#{obj})!"
end
begin
klass.class_eval <<-EOR
def #{meth}(__n#{@data.keys.map { |k| ", #{k}" }.join})
::Perfer.measure do
__i = 0
while __i < __n
#{"#{code}; " * repeat_eval}
__i += 1
end
end
end
EOR
rescue SyntaxError => e
raise Error, "There was an error while eval'ing the code: #{code.inspect}\n#{e}"
end
if obj
singleton_class.send(:define_method, :measure_call_times_code) do |*args|
obj.send(meth, *args)
end
end
end
|
#compute_new_iterations(iterations, time) ⇒ Object
84
85
86
|
# File 'lib/perfer/job/iteration_job.rb', line 84
def compute_new_iterations(iterations, time)
(minimal_time * iterations / time).ceil
end
|
#find_number_of_iterations_required(last_iterations = 1, last_time = 0) ⇒ Object
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
# File 'lib/perfer/job/iteration_job.rb', line 88
def find_number_of_iterations_required(last_iterations = 1, last_time = 0)
iterations = last_iterations
if last_time > 0
iterations = compute_new_iterations(last_iterations, last_time)
end
iterations = round_for_eval(iterations)
puts "Start search for iterations: start=#{iterations}" if verbose
loop do
puts "iterations: #{iterations}" if verbose
time = measure_call_times(iterations)[:real]
break if time > minimal_time
if time <= 0
iterations *= 2
next
end
new_iterations = compute_new_iterations(iterations, time)
if new_iterations <= iterations
puts "new_iterations <= iterations: #{new_iterations} <= #{iterations}" if verbose
new_iterations = (iterations*1.5).ceil
end
if new_iterations > MAX_GROW_FACTOR * iterations
new_iterations = MAX_GROW_FACTOR * iterations
end
iterations = round_for_eval(new_iterations)
end
puts "End search for iterations: iterations=#{iterations}" if verbose
iterations
end
|
#generate_method_name ⇒ Object
56
57
58
|
# File 'lib/perfer/job/iteration_job.rb', line 56
def generate_method_name
:"perfer_eval_#{UNIQUE_NAME.succ!}"
end
|
#mad(measurements) ⇒ Object
median absolute deviation / median
121
122
123
124
125
126
127
|
# File 'lib/perfer/job/iteration_job.rb', line 121
def mad(measurements)
stats = Statistics.new(measurements.map { |m| m[:real] })
mad = stats.median_absolute_deviation
mad /= stats.median
puts "mad: #{mad}" if verbose
mad
end
|
#measure_call_times(n) ⇒ Object
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
# File 'lib/perfer/job/iteration_job.rb', line 60
def measure_call_times(n)
GC.start
if !@block
if n % repeat_eval != 0
raise Error, "Implementation error: #{n} not multiple of #{repeat_eval}"
end
n /= repeat_eval
measure_call_times_code(n, *@data.values)
elsif @block.arity == 1
Perfer.measure { @block.call(n) }
else
Perfer.measure { n.times(&@block) }
end.tap { |m| p m if verbose }
end
|
#repeat_eval ⇒ Object
9
10
11
|
# File 'lib/perfer/job/iteration_job.rb', line 9
def repeat_eval
100
end
|
#round_for_eval(iterations) ⇒ Object
76
77
78
79
80
81
82
|
# File 'lib/perfer/job/iteration_job.rb', line 76
def round_for_eval(iterations)
if @block
iterations
else
((iterations + repeat_eval - 1) / repeat_eval) * repeat_eval
end
end
|
#run ⇒ Object
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
# File 'lib/perfer/job/iteration_job.rb', line 129
def run
super
measurements = []
measure_call_times(round_for_eval(1))
iterations = find_number_of_iterations_required
measurements_taken = 0
until measurements.size == number_of_measurements and
mad(measurements) < 0.01 * measurements_taken / number_of_measurements
time = measure_call_times(iterations)
measurements_taken += 1
if time[:real] < (1.0 - CHANGE_ITERATIONS_MARGIN) * minimal_time
puts "Restarting, #{time[:real]} < #{minimal_time}" if verbose
measurements.clear
measurements_taken = 0
iterations = find_number_of_iterations_required(iterations, time[:real])
else
measurements.shift if measurements.size == number_of_measurements
measurements << time
end
end
result = Result.new(@metadata)
result[:iterations] = iterations
result.data = measurements
@session.add_result(result)
end
|