jkr 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+
2
+ module Jkr
3
+ class PlanFinder
4
+ def initialize(jkr_env)
5
+ @jkr_env = jkr_env
6
+ end
7
+
8
+ def find_by_name(name, options = {})
9
+ options[:plan_search_path] ||= [@jkr_env.jkr_plan_dir]
10
+
11
+ options[:plan_search_path].each do |dir|
12
+ Dir.glob("#{dir}/*.plan").each do |path|
13
+ if File.basename(path, ".plan") == name
14
+ return path
15
+ end
16
+ end
17
+ end
18
+
19
+ nil
20
+ end
21
+
22
+ def find_by_result_id(ret_id)
23
+ ret_dir = Dir[sprintf("#{@jkr_env.jkr_result_dir}/%05d*", ret_id)].first
24
+
25
+ unless ret_dir
26
+ raise ArgumentError.new("Result not found: id=#{ret_id}")
27
+ end
28
+
29
+ plan_files = Dir["#{ret_dir}/*.plan"]
30
+
31
+ if plan_files.size < 1
32
+ raise RuntimeError.new("No plan file found: #{File.basename(ret_dir)}")
33
+ elsif plan_files.size > 1
34
+ raise RuntimeError.new("Multiple plan files found: #{File.basename(ret_dir)}")
35
+ end
36
+
37
+ plan_files.first
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,626 @@
1
+
2
+ require 'pathname'
3
+
4
+ def rel_path(basefile, path)
5
+ base = Pathname.new(File.dirname(basefile))
6
+ path = Pathname.new(path)
7
+ path.relative_path_from(base).to_s
8
+ end
9
+
10
+ def gnuplot_label_escape(str)
11
+ ret = str.gsub(/_/, "\\\\\\_").gsub(/\{/, "\\{").gsub(/\}/, "\\}")
12
+ ret
13
+ end
14
+
15
+ def plot_distribution(config)
16
+ dataset = config[:dataset]
17
+ xrange_max = config[:xrange_max]
18
+ xrange_min = config[:xrange_min] || 0
19
+ title = config[:title]
20
+ xlabel = config[:xlabel]
21
+ eps_file = config[:output]
22
+ datafile = if config[:datafile]
23
+ File.open(config[:datafile], "w")
24
+ else
25
+ Tempfile.new("dist")
26
+ end
27
+ if config[:gpfile]
28
+ gpfile = File.open(config[:gpfile], "w")
29
+ else
30
+ gpfile = Tempfile.new("plot_dist")
31
+ end
32
+ datafile_path = rel_path(gpfile.path, datafile)
33
+
34
+ stepnum = 500
35
+
36
+ plot_stmt = []
37
+ data_idx = 0
38
+ xrange_max_local = 0
39
+ histset = []
40
+ dataset.each do |tx_type,data|
41
+ next if data.empty?
42
+ hist = Array.new
43
+ avg = data.inject(&:+) / data.size.to_f
44
+ sterr = data.sterr
45
+ stdev = data.stdev
46
+ n90th_idx = (data.size * 0.9).to_i
47
+ n90percent = data.sort[n90th_idx]
48
+ xrange_max_local = [xrange_max_local, xrange_max || n90percent * 4].max
49
+ step = xrange_max_local / stepnum
50
+ maxidx = 0
51
+ data.each_with_index do |num, data_idx|
52
+ idx = (num / step).to_i
53
+ maxidx = [idx, maxidx].max
54
+ hist[idx] = (hist[idx] || 0) + 1
55
+ end
56
+
57
+ 0.upto(maxidx) do |idx|
58
+ unless hist[idx]
59
+ hist[idx] = 0
60
+ end
61
+ end
62
+
63
+ histset.push({:tx_type => tx_type, :step => step, :hist => hist, :max => hist.max})
64
+
65
+ histmax = hist.max.to_f
66
+ datafile.puts "##{tx_type}"
67
+ 0.upto(stepnum - 1) do |i|
68
+ x = (i + 0.5) * step
69
+ y = hist[i] || 0
70
+ if config[:normalize]
71
+ y = y / histmax
72
+ end
73
+ datafile.puts "#{x}\t#{y}"
74
+ end
75
+ datafile.puts
76
+ datafile.puts
77
+
78
+ # index data_idx+1
79
+ datafile.puts "#{avg}\t0"
80
+ datafile.puts "#{avg}\t#{hist.max * 1.2}"
81
+ datafile.puts
82
+ datafile.puts
83
+
84
+ # index data_idx+2
85
+ datafile.puts "#{n90percent}\t0"
86
+ datafile.puts "#{n90percent}\t#{hist.max * 1.2}"
87
+ datafile.puts
88
+ datafile.puts
89
+
90
+ # index data_idx+3
91
+ datafile.puts "#{avg-sterr * 3}\t0"
92
+ datafile.puts "#{avg-sterr * 3}\t#{hist.max * 1.2}"
93
+ datafile.puts
94
+ datafile.puts
95
+ # index data_idx+4
96
+ datafile.puts "#{avg+sterr * 3}\t0"
97
+ datafile.puts "#{avg+sterr * 3}\t#{hist.max * 1.2}"
98
+ datafile.puts
99
+
100
+ plot_stmt.push(sprintf("'%s' index %d:%d using 1:2 with linespoints title '%s'",
101
+ datafile_path, data_idx, data_idx, tx_type))
102
+ if config[:show_avgline]
103
+ plot_stmt.push(sprintf("'%s' index %d:%d using 1:2 with lines lc 1 lt 2 lw 3 title '%s(avg)'",
104
+ datafile_path, data_idx+1, data_idx+1, tx_type))
105
+ end
106
+ if config[:show_90pline]
107
+ plot_stmt.push(sprintf("'%s' index %d:%d using 1:2 with lines lc 2 lt 2 lw 3 title '%s(90%%-th)'",
108
+ datafile_path, data_idx+2, data_idx+2, tx_type))
109
+ end
110
+ if config[:show_confinterval]
111
+ plot_stmt.push(sprintf("'%s' index %d:%d using 1:2 with lines lc 3 lt 3 lw 3 title '%s(std err)'",
112
+ datafile_path, data_idx+3, data_idx+3, tx_type))
113
+ plot_stmt.push(sprintf("'%s' index %d:%d using 1:2 with lines lc 3 lt 3 lw 3 title '%s(std err)'",
114
+ datafile_path, data_idx+4, data_idx+4, tx_type))
115
+ end
116
+ data_idx += 5
117
+ end
118
+
119
+ datafile.fsync
120
+ plot_stmt = plot_stmt.flatten.join(", \\\n ")
121
+ plot_stmt = "plot #{plot_stmt}"
122
+
123
+ script = <<EOS
124
+ set term postscript enhanced color
125
+ set output "#{rel_path(gpfile.path, eps_file)}"
126
+ set size 0.9,0.6
127
+ set title "#{gnuplot_label_escape(title)}"
128
+ set ylabel "Frequency"
129
+ set xlabel "#{gnuplot_label_escape(xlabel)}"
130
+ set xrange [#{xrange_min}:#{xrange_max_local}]
131
+ #{config[:other_options]}
132
+ #{plot_stmt}
133
+ EOS
134
+ gpfile.puts script
135
+ gpfile.fsync
136
+ Dir.chdir(File.dirname(gpfile.path)) do
137
+ system_("gnuplot #{gpfile.path}")
138
+ end
139
+ gpfile.close
140
+ end
141
+
142
+ def plot_timeseries(config)
143
+ [:plot_data, :output, :title, :xlabel, :ylabel].each do |key|
144
+ unless config[key]
145
+ raise ArgumentError.new("key '#{key.to_s}' is required for time-series graph")
146
+ return
147
+ end
148
+ end
149
+
150
+ xrange = if config[:xrange]
151
+ "set xrange #{config[:xrange]}"
152
+ else
153
+ ""
154
+ end
155
+ yrange = if config[:yrange]
156
+ "set yrange #{config[:yrange]}"
157
+ else
158
+ ""
159
+ end
160
+
161
+ if config[:gpfile]
162
+ gpfile = File.open(config[:gpfile], "w")
163
+ else
164
+ gpfile = Tempfile.new("plot_timeseries")
165
+ end
166
+
167
+ plot_stmt = "plot " + config[:plot_data].map {|plot_datum|
168
+ [:datafile, :using, :with, :title].each do |key|
169
+ unless plot_datum[key]
170
+ raise ArgumentError.new("key '#{key.to_s}' is required for a plot_datum of time-series graph")
171
+ return
172
+ end
173
+ end
174
+ index = ""
175
+ if plot_datum[:index]
176
+ index = " index #{plot_datum[:index]} "
177
+ end
178
+ "'#{rel_path(gpfile.path, plot_datum[:datafile])}' #{index} using #{plot_datum[:using]} with #{plot_datum[:with]}"+
179
+ " title '#{gnuplot_label_escape(plot_datum[:title])}' " + plot_datum[:other_options].to_s
180
+ }.join(", \\\n ")
181
+
182
+ script = <<EOS
183
+ set term postscript enhanced color
184
+ set output "#{rel_path(gpfile.path, config[:output])}"
185
+ set size 0.9,0.6
186
+ set title "#{gnuplot_label_escape(config[:title])}"
187
+ set ylabel "#{config[:ylabel]}"
188
+ set xlabel "#{config[:xlabel]}"
189
+ set rmargin 3
190
+ set lmargin 10
191
+ #{xrange}
192
+ #{yrange}
193
+ set grid
194
+ #{config[:other_options]}
195
+ #{plot_stmt}
196
+ EOS
197
+ gpfile.puts script
198
+ gpfile.fsync
199
+ Dir.chdir(File.dirname(gpfile.path)) do
200
+ system_("gnuplot #{gpfile.path}")
201
+ end
202
+ gpfile.close
203
+ end
204
+
205
+ def plot_bar(config)
206
+ [:output, :title, :series_labels, :item_labels, :data].each do |key|
207
+ unless config[key]
208
+ raise StandardError.new("key '#{key.to_s}' is required for bar graph")
209
+ end
210
+ end
211
+
212
+ config[:size] ||= "0.9,0.6"
213
+
214
+ data = config[:data]
215
+
216
+ if ! data.all?{|series| series.all?{|datum| datum[:value].is_a? Numeric}}
217
+ raise StandardError.new("All datum must have :value key")
218
+ end
219
+
220
+ if config[:datafile]
221
+ datafile = File.open(config[:datafile], "w")
222
+ else
223
+ datafile = Tempfile.new("plot_bar")
224
+ end
225
+ if config[:gpfile]
226
+ gpfile = File.open(config[:gpfile], "w")
227
+ else
228
+ gpfile = Tempfile.new("plot_bar")
229
+ end
230
+ datafile_path = rel_path(gpfile.path, datafile.path)
231
+
232
+ config[:item_label_angle] ||= -30
233
+
234
+ plot_data = []
235
+ plot_stdev_data = []
236
+
237
+ if data.all?{|series| series.all?{|datum| datum[:stdev].is_a? Numeric}}
238
+ draw_stdev = true
239
+ else
240
+ draw_stdev = false
241
+ end
242
+
243
+ item_barwidth = 0.8 # 2.0 / 3.0
244
+ num_serieses = data.size
245
+ data_idx = 0
246
+ config[:data].each_with_index do |series, series_idx|
247
+ series.each_with_index do |datum, item_idx|
248
+ xpos = item_idx - item_barwidth / 2.0 + item_barwidth / num_serieses.to_f / 2.0 + item_barwidth / num_serieses.to_f * series_idx
249
+ barwidth = item_barwidth / num_serieses.to_f
250
+ datafile.puts([xpos, datum[:value], barwidth, (datum[:stdev] || "")].join("\t"))
251
+ end
252
+
253
+ plot_data.push({
254
+ :title => config[:series_labels][series_idx],
255
+ :using => "1:2:3",
256
+ :index => "#{data_idx}:#{data_idx}",
257
+ :with => "with boxes fs solid 0.7",
258
+ })
259
+
260
+ if series.every?{|datum| datum[:stdev]}
261
+ plot_data.push({
262
+ :title => nil,
263
+ :using => "1:2:4",
264
+ :index => "#{data_idx}:#{data_idx}",
265
+ :with => "with yerrorbars lc 1 lt 1 pt 0",
266
+ })
267
+ end
268
+
269
+ datafile.puts("\n\n")
270
+ data_idx += 1
271
+ end
272
+ datafile.fsync
273
+
274
+ ylabel = if config[:ylabel]
275
+ "set ylabel '#{config[:ylabel]}'"
276
+ else
277
+ ""
278
+ end
279
+
280
+ yrange = if config[:yrange]
281
+ "set yrange #{config[:yrange]}"
282
+ else
283
+ ""
284
+ end
285
+
286
+ plot_stmt = "plot " + plot_data.map do |plot_datum|
287
+ [:using].each do |key|
288
+ unless plot_datum[key]
289
+ raise StandardError.new("key '#{key.to_s}' is required for a plot_datum of bar graph")
290
+ return
291
+ end
292
+ end
293
+ index = ""
294
+ if plot_datum[:index]
295
+ index = " index #{plot_datum[:index]} "
296
+ end
297
+ if plot_datum[:title]
298
+ title = "title '#{gnuplot_label_escape(plot_datum[:title])}'"
299
+ else
300
+ title = "notitle"
301
+ end
302
+ "'#{rel_path(gpfile.path, datafile.path)}' #{index} using #{plot_datum[:using]}"+
303
+ " #{title} #{plot_datum[:with]} "
304
+ end.join(", \\\n ")
305
+
306
+ xpos = -1
307
+ xtics = config[:item_labels].map do |label|
308
+ xpos += 1
309
+ "\"#{label}\" #{xpos}"
310
+ end.join(",")
311
+ xtics = "(#{xtics})"
312
+ xrange = "[-0.5:#{config[:item_labels].size - 0.5}]"
313
+
314
+ script = <<EOS
315
+ set term postscript enhanced color
316
+ set output "#{rel_path(gpfile.path, config[:output])}"
317
+ set size #{config[:size]}
318
+ set title "#{gnuplot_label_escape(config[:title])}"
319
+ #{ylabel}
320
+ #{yrange}
321
+ set xrange #{xrange}
322
+ set xtic rotate by #{config[:item_label_angle]} scale 0
323
+ set xtics #{xtics}
324
+ #{config[:other_options]}
325
+ #{plot_stmt}
326
+ EOS
327
+ gpfile.puts script
328
+ gpfile.fsync
329
+ Dir.chdir(File.dirname(gpfile.path)) do
330
+ system_("gnuplot #{gpfile.path}")
331
+ end
332
+ gpfile.close
333
+ end
334
+
335
+ def plot_scatter(config)
336
+ [:plot_data, :output, :xlabel, :ylabel].each do |key|
337
+ unless config.keys.include?(key)
338
+ raise ArgumentError.new("key '#{key.to_s}' is required for scatter graph")
339
+ end
340
+ end
341
+
342
+ xrange = if config[:xrange]
343
+ "set xrange #{config[:xrange]}"
344
+ else
345
+ ""
346
+ end
347
+ yrange = if config[:yrange]
348
+ "set yrange #{config[:yrange]}"
349
+ else
350
+ ""
351
+ end
352
+ if config[:gpfile]
353
+ gpfile = File.open(config[:gpfile], "w")
354
+ else
355
+ gpfile = Tempfile.new("gnuplot")
356
+ end
357
+
358
+ plot_stmt = "plot " + config[:plot_data].map {|plot_datum|
359
+ unless plot_datum[:expression] ||
360
+ [:datafile, :using].every?{|key| plot_datum[key]}
361
+ raise ArgumentError.new("key ('datafile', 'using', 'with') or 'expression' is required for a plot_datum")
362
+ end
363
+
364
+ plot_target = nil
365
+ if plot_datum[:expression]
366
+ plot_target = plot_datum[:expression]
367
+ else
368
+ index = ""
369
+ if plot_datum[:index]
370
+ index = " index #{plot_datum[:index]} "
371
+ end
372
+
373
+ plot_target = "'#{rel_path(gpfile.path, plot_datum[:datafile])}' #{index} using #{plot_datum[:using]}"
374
+ end
375
+
376
+ unless plot_datum[:title]
377
+ title = "notitle"
378
+ else
379
+ title = "title '#{gnuplot_label_escape(plot_datum[:title])}'"
380
+ end
381
+
382
+ if plot_datum[:with]
383
+ with = "with #{plot_datum[:with]}"
384
+ else
385
+ with = ""
386
+ end
387
+ "#{plot_target} #{with} #{title} " + plot_datum[:other_options].to_s
388
+ }.join(", \\\n ")
389
+
390
+ title_stmt = "unset title"
391
+ if config[:title]
392
+ title_stmt = "set title \"#{gnuplot_label_escape(config[:title])}\""
393
+ end
394
+
395
+ case config[:output]
396
+ when /\.svg\Z/
397
+ terminal = 'svg'
398
+ else
399
+ terminal = 'postscript enhanced color'
400
+ end
401
+
402
+ script = <<EOS
403
+ set term #{terminal}
404
+ set output "#{rel_path(gpfile.path, config[:output])}"
405
+ #{if config[:size]; then "set size " + config[:size]; else; ""; end}
406
+ #{title_stmt}
407
+ set ylabel "#{gnuplot_label_escape(config[:ylabel])}"
408
+ set xlabel "#{gnuplot_label_escape(config[:xlabel])}"
409
+ set rmargin 10
410
+ set lmargin 10
411
+ #{xrange}
412
+ #{yrange}
413
+ set grid
414
+ #{config[:other_options]}
415
+ #{plot_stmt}
416
+ EOS
417
+ gpfile.puts script
418
+ gpfile.fsync
419
+ Dir.chdir(File.dirname(gpfile.path)) do
420
+ system_("gnuplot #{gpfile.path}")
421
+ end
422
+ gpfile.close
423
+ end
424
+
425
+ def plot_mpstat_data(mpstat_data, option)
426
+ option[:dir] ||= "mpstat"
427
+ option[:start_time] ||= mpstat_data.first[:time]
428
+ option[:end_time] ||= mpstat_data[-1][:time]
429
+
430
+ if ! Dir.exists?(option[:dir])
431
+ FileUtils.mkdir_p(option[:dir])
432
+ end
433
+
434
+ datafile = File.open(File.join(option[:dir],
435
+ "mpstat.tsv"),
436
+ "w")
437
+
438
+ start_time = option[:start_time]
439
+ end_time = option[:end_time]
440
+
441
+ data_idx = 0
442
+ nr_cpu = mpstat_data.first[:data].size - 1
443
+ (nr_cpu + 1).times do |cpu_idx|
444
+ scale = 1.0
445
+ if cpu_idx < nr_cpu
446
+ label = cpu_idx.to_s
447
+ else
448
+ label = "ALL"
449
+ scale = nr_cpu.to_f
450
+ end
451
+
452
+ datafile.puts("# cpu#{label}")
453
+ mpstat_data.each do |rec|
454
+ t = rec[:time] - start_time
455
+
456
+ if cpu_idx < nr_cpu
457
+ data = rec[:data].find{|x| x[:cpu] == cpu_idx}
458
+ else
459
+ data = rec[:data].find{|x| x[:cpu] == "all"}
460
+ end
461
+ datafile.puts([t,
462
+ data[:usr] * scale,
463
+ data[:nice] * scale,
464
+ data[:sys] * scale,
465
+ data[:iowait] * scale,
466
+ data[:irq] * scale,
467
+ data[:soft] * scale,
468
+ data[:steal] * scale,
469
+ data[:idle] * scale].map(&:to_s).join("\t"))
470
+ end
471
+ datafile.puts("\n\n")
472
+ datafile.fsync
473
+
474
+ plot_scatter(:output => File.join(option[:dir],
475
+ "cpu#{label}.eps"),
476
+ :gpfile => File.join(option[:dir],
477
+ "gp_cpu#{label}.gp"),
478
+ :title => nil,
479
+ :xlabel => "elapsed time [sec]",
480
+ :ylabel => "CPU usage [%]",
481
+ :xrange => "[0:#{end_time - start_time}]",
482
+ :yrange => "[0:#{105 * scale}]",
483
+ :plot_data => [{
484
+ :title => "%usr",
485
+ :using => "1:2",
486
+ :index => "#{data_idx}:#{data_idx}",
487
+ :with => "filledcurve x1",
488
+ :datafile => datafile.path
489
+ },
490
+ {
491
+ :title => "%nice",
492
+ :using => "1:($2+$3)",
493
+ :index => "#{data_idx}:#{data_idx}",
494
+ :with => "filledcurve x1",
495
+ :datafile => datafile.path
496
+ },
497
+ {
498
+ :title => "%sys",
499
+ :using => "1:($2+$3+$4)",
500
+ :index => "#{data_idx}:#{data_idx}",
501
+ :with => "filledcurve x1",
502
+ :datafile => datafile.path
503
+ },
504
+ {
505
+ :title => "%iowait",
506
+ :using => "1:($2+$3+$4+$5)",
507
+ :index => "#{data_idx}:#{data_idx}",
508
+ :with => "filledcurve x1",
509
+ :datafile => datafile.path
510
+ },
511
+ {
512
+ :title => "%irq",
513
+ :using => "1:($2+$3+$4+$5+$6)",
514
+ :index => "#{data_idx}:#{data_idx}",
515
+ :with => "filledcurve x1",
516
+ :datafile => datafile.path
517
+ },
518
+ {
519
+ :title => "%soft",
520
+ :using => "1:($2+$3+$4+$5+$6+$7)",
521
+ :index => "#{data_idx}:#{data_idx}",
522
+ :with => "filledcurve x1",
523
+ :datafile => datafile.path
524
+ },
525
+ {
526
+ :title => "%steal",
527
+ :using => "1:($2+$3+$4+$5+$6+$7+$8)",
528
+ :index => "#{data_idx}:#{data_idx}",
529
+ :with => "filledcurve x1",
530
+ :datafile => datafile.path
531
+ }].reverse,
532
+ :size => "0.9,0.9",
533
+ :other_options => <<EOS
534
+ set rmargin 3
535
+ set lmargin 5
536
+ set key below
537
+ EOS
538
+ )
539
+
540
+ data_idx += 1
541
+ end
542
+ end
543
+
544
+ def plot_io_pgr_data(pgr_data, option)
545
+ option[:dir] ||= "io_pgr"
546
+ option[:start_time] ||= pgr_data.first["time"]
547
+ option[:end_time] ||= pgr_data[-1]["time"]
548
+
549
+ if ! Dir.exists?(option[:dir])
550
+ FileUtils.mkdir_p(option[:dir])
551
+ end
552
+
553
+ start_time = option[:start_time]
554
+ end_time = option[:end_time]
555
+
556
+ datafile = File.open(File.join(option[:dir],
557
+ "io_pgr.tsv"),
558
+ "w")
559
+ devices = pgr_data.first["ioinfo"]["devices"]
560
+
561
+ idx = 0
562
+ devices.each do |device|
563
+ do_read = false
564
+ do_write = false
565
+ unless pgr_data.all?{|rec| rec["ioinfo"][device]['r/s'] < 0.1}
566
+ do_read = true
567
+ end
568
+ unless pgr_data.all?{|rec| rec["ioinfo"][device]['w/s'] < 0.1}
569
+ do_read = true
570
+ end
571
+
572
+ if do_read == false && do_write == false
573
+ $stderr.puts("#{device} performs no I/O")
574
+ next
575
+ end
576
+
577
+ datafile.puts("# #{device}")
578
+ pgr_data.each do |rec|
579
+ datafile.puts([rec["time"] - start_time,
580
+ rec["ioinfo"][device]['r/s'],
581
+ rec["ioinfo"][device]['w/s'],
582
+ ].map(&:to_s).join("\t"))
583
+ end
584
+ datafile.puts("\n\n")
585
+ datafile.fsync
586
+
587
+ plot_data = []
588
+ if do_read
589
+ plot_data.push({
590
+ :title => "read",
591
+ :datafile => datafile.path,
592
+ :using => "1:2",
593
+ :index => "#{idx}:#{idx}",
594
+ :with => "lines"
595
+ })
596
+ end
597
+ if do_write
598
+ plot_data.push({
599
+ :title => "write",
600
+ :datafile => datafile.path,
601
+ :using => "1:3",
602
+ :index => "#{idx}:#{idx}",
603
+ :with => "lines"
604
+ })
605
+ end
606
+
607
+ idx += 1
608
+
609
+ plot_scatter(:output => File.join(option[:dir],
610
+ device + ".eps"),
611
+ :gpfile => File.join(option[:dir],
612
+ "gp_" + device + ".gp"),
613
+ :xlabel => "elapsed time [sec]",
614
+ :ylabel => "IOPS",
615
+ :xrange => "[0:#{end_time - start_time}]",
616
+ :yrange => "[0:]",
617
+ :title => "IO performance",
618
+ :plot_data => plot_data,
619
+ :other_options => <<EOS
620
+ set rmargin 3
621
+ set lmargin 10
622
+ set key below
623
+ EOS
624
+ )
625
+ end
626
+ end