| 96 | | @time += benchmark do |
|---|
| 97 | | Content.create! :title => "record #{@id} / #{i}", :description => DrbSmokeTest::random_document |
|---|
| 98 | | end |
|---|
| 99 | | #sleep 0.1 |
|---|
| 100 | | if i % NUM_RECORDS_PER_LOGENTRY == 0 |
|---|
| 101 | | # write stats |
|---|
| 102 | | puts "#{@id}: #{i} records indexed, last #{NUM_RECORDS_PER_LOGENTRY} in #{@time}" |
|---|
| 103 | | Stats.create! :process_id => @id, :kind => 'write', :info => i, |
|---|
| 104 | | :processing_time => @time * TIME_FACTOR, # average processing time per record in this batch |
|---|
| 105 | | :open_connections => Monitor::count_connections |
|---|
| 106 | | @time = 0 |
|---|
| 107 | | end |
|---|
| 108 | | end |
|---|
| 109 | | Stats.create! :process_id => @id, :kind => 'finished' |
|---|
| 110 | | puts "#{@i} finished" |
|---|
| 111 | | end |
|---|
| 112 | | end |
|---|
| 113 | | |
|---|
| 114 | | class Searcher < TestBase |
|---|
| 115 | | def run |
|---|
| 116 | | while Monitor::running? |
|---|
| | 126 | log create_record(i) |
|---|
| | 127 | if i % log_interval == 0 |
|---|
| | 128 | # log progress |
|---|
| | 129 | puts "#{@id}: #{i} records indexed" |
|---|
| | 130 | end |
|---|
| | 131 | end |
|---|
| | 132 | end |
|---|
| | 133 | |
|---|
| | 134 | def create_record(i) |
|---|
| | 135 | time = benchmark do |
|---|
| | 136 | Content.create! :title => "record #{@id} / #{i}", :description => DrbSmokeTest::random_document |
|---|
| | 137 | end |
|---|
| | 138 | [ time ] |
|---|
| | 139 | end |
|---|
| | 140 | end |
|---|
| | 141 | |
|---|
| | 142 | class Searcher < WorkerBase |
|---|
| | 143 | def self.prefix; 'searcher' end |
|---|
| | 144 | def do_run |
|---|
| | 145 | while Monitor::writers_running? |
|---|
| 170 | | end |
|---|
| 171 | | end |
|---|
| | 194 | puts "doing the math now..." |
|---|
| | 195 | DrbSmokeTest::Stats.new(DrbSmokeTest::Writer::prefix).run |
|---|
| | 196 | DrbSmokeTest::Stats.new(DrbSmokeTest::Searcher::prefix).run |
|---|
| | 197 | end |
|---|
| | 198 | end |
|---|
| | 199 | |
|---|
| | 200 | module Statistics |
|---|
| | 201 | def odd?(value) |
|---|
| | 202 | value % 2 == 1 |
|---|
| | 203 | end |
|---|
| | 204 | |
|---|
| | 205 | def median(population) |
|---|
| | 206 | if odd?(population.size) |
|---|
| | 207 | population[population.size/2] |
|---|
| | 208 | else |
|---|
| | 209 | mean [ population[population.size/2-1], population[population.size/2] ] |
|---|
| | 210 | end |
|---|
| | 211 | end |
|---|
| | 212 | |
|---|
| | 213 | def mean(population) |
|---|
| | 214 | sum = population.inject(0) { |sum, v| sum + v } |
|---|
| | 215 | sum / population.size.to_f |
|---|
| | 216 | end |
|---|
| | 217 | |
|---|
| | 218 | # variance and standard_deviation methods from |
|---|
| | 219 | # http://warrenseen.com/blog/2006/03/13/how-to-calculate-standard-deviation/ |
|---|
| | 220 | def variance(population) |
|---|
| | 221 | n = 0 |
|---|
| | 222 | mean = 0.0 |
|---|
| | 223 | s = 0.0 |
|---|
| | 224 | population.each { |x| |
|---|
| | 225 | n = n + 1 |
|---|
| | 226 | delta = x - mean |
|---|
| | 227 | mean = mean + (delta / n) |
|---|
| | 228 | s = s + delta * (x - mean) |
|---|
| | 229 | } |
|---|
| | 230 | # if you want to calculate std deviation |
|---|
| | 231 | # of a sample change this to "s / (n-1)" |
|---|
| | 232 | return s / n |
|---|
| | 233 | end |
|---|
| | 234 | |
|---|
| | 235 | # calculate the standard deviation of a population |
|---|
| | 236 | # accepts: an array, the population |
|---|
| | 237 | # returns: the standard deviation |
|---|
| | 238 | def standard_deviation(population) |
|---|
| | 239 | Math.sqrt(variance(population)) |
|---|
| | 240 | rescue |
|---|
| | 241 | puts "pop: #{population.inspect}" |
|---|
| | 242 | end |
|---|
| | 243 | end |
|---|
| | 244 | |
|---|
| | 245 | class Stats |
|---|
| | 246 | include Statistics |
|---|
| | 247 | |
|---|
| | 248 | def initialize(prefix) |
|---|
| | 249 | @prefix = prefix |
|---|
| | 250 | @stats = [] |
|---|
| | 251 | end |
|---|
| | 252 | |
|---|
| | 253 | def collect_stats |
|---|
| | 254 | Dir["#{@prefix}_*.log"].each do |logfile| |
|---|
| | 255 | puts logfile |
|---|
| | 256 | File.open(logfile) do |f| |
|---|
| | 257 | while line = f.gets |
|---|
| | 258 | row = line.split(',') |
|---|
| | 259 | row[row.size-1] = row.last.to_i |
|---|
| | 260 | @stats << row |
|---|
| | 261 | end |
|---|
| | 262 | end |
|---|
| | 263 | end |
|---|
| | 264 | puts "#{@stats.size} lines read, now sorting..." |
|---|
| | 265 | @stats.sort! { |row1, row2| row1.last <=> row2.last } |
|---|
| | 266 | end |
|---|
| | 267 | |
|---|
| | 268 | def with_segments(segment_count) |
|---|
| | 269 | t0 = @stats.first.last.to_i |
|---|
| | 270 | t1 = @stats.last.last.to_i |
|---|
| | 271 | timespan = t1 - t0 |
|---|
| | 272 | puts "test run took: #{timespan/1000} seconds" |
|---|
| | 273 | # we want to draw 1000 points, determine which timespan one point covers |
|---|
| | 274 | segment_length = timespan / segment_count |
|---|
| | 275 | t = 0 |
|---|
| | 276 | i = 0 |
|---|
| | 277 | while t <= t1 |
|---|
| | 278 | t += segment_length |
|---|
| | 279 | segment_stats = [] |
|---|
| | 280 | while @stats.any? && @stats.first.last.to_i < t |
|---|
| | 281 | segment_stats << @stats.shift |
|---|
| | 282 | end |
|---|
| | 283 | yield segment_stats unless segment_stats.empty? |
|---|
| | 284 | end |
|---|
| | 285 | end |
|---|
| | 286 | |
|---|
| | 287 | def run |
|---|
| | 288 | collect_stats |
|---|
| | 289 | segments = [] |
|---|
| | 290 | with_segments(500) do |segment_stats| |
|---|
| | 291 | segments << process_segment(segment_stats) |
|---|
| | 292 | end |
|---|
| | 293 | |
|---|
| | 294 | chart("#{@prefix} mean", "#{@prefix.downcase}_mean") do |g| |
|---|
| | 295 | g.data :mean, segments.map{ |row| row[0] } |
|---|
| | 296 | g.data :stddev, segments.map{ |row| row[1] } |
|---|
| | 297 | end |
|---|
| | 298 | chart("#{@prefix} median", "#{@prefix.downcase}_median") do |g| |
|---|
| | 299 | g.data :median, segments.map{ |row| row[2] } |
|---|
| | 300 | end |
|---|
| | 301 | end |
|---|
| | 302 | |
|---|
| | 303 | def process_segment(segment) |
|---|
| | 304 | times = segment.map{|row|row.first.to_i * 1000} |
|---|
| | 305 | [mean(times), standard_deviation(times), median(times), segment.size] |
|---|
| | 306 | end |
|---|
| | 307 | |
|---|
| | 308 | def chart(title, fname) |
|---|
| | 309 | g = Gruff::Line.new do |g| |
|---|
| | 310 | g.title = title |
|---|
| | 311 | g.theme = { |
|---|
| | 312 | :background_colors => ["#e6e6e6", "#e6e6e6"], |
|---|
| | 313 | :colors => ["#ff43a7", '#666666', 'black', 'white', 'grey'], |
|---|
| | 314 | :marker_color => "white" |
|---|
| | 315 | } |
|---|
| | 316 | end |
|---|
| | 317 | yield g |
|---|
| | 318 | g.write "#{fname}.png" |
|---|
| | 319 | end |
|---|
| | 320 | end |
|---|
| | 321 | |
|---|