Skip to content

Commit db476ec

Browse files
committed
thursday
1 parent 824eaab commit db476ec

File tree

1 file changed

+109
-87
lines changed

1 file changed

+109
-87
lines changed

src/trials.jl

Lines changed: 109 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -297,18 +297,19 @@ function withtypename(f, io, t)
297297
end
298298
end
299299

300-
function bindata(sorteddata, nbins, min, max)
301-
Δ = (max - min) / nbins
302-
bins = zeros(nbins)
303-
lastpos = 0
304-
for i 1:nbins
305-
pos = searchsortedlast(sorteddata, min + i * Δ)
306-
bins[i] = pos - lastpos
307-
lastpos = pos
300+
function bindata(data, fences::AbstractRange)
301+
@assert step(fences) > 0
302+
bins = zeros(Int, length(fences))
303+
for t in data
304+
i = searchsortedlast(fences, t)
305+
# Any data to the left of the leftmost divider is ignored:
306+
iszero(i) && continue
307+
bins[i] += 1
308308
end
309309
bins
310310
end
311-
bindata(sorteddata, nbins) = bindata(sorteddata, nbins, first(sorteddata), last(sorteddata))
311+
312+
# @test bindata([1.1, 3.1, 99, -99], 1:3) == [1,0,2]
312313

313314
function asciihist(bins, height=1)
314315
histbars = ['', '', '', '', '', '', '', '']
@@ -347,11 +348,13 @@ Base.show(io::IO, t::TrialJudgement) = _show(io, t)
347348

348349
function Base.show(io::IO, ::MIME"text/plain", t::Trial)
349350
pad = get(io, :pad, "")
351+
350352
boxcolor = :light_black
351353
boxspace = " "
352354
modulestr = "" # "BenchmarkTools."
353355
avgcolor = :green
354356
medcolor = :blue
357+
captioncolor = :light_black
355358
showpercentile = 99 # used both for display time, and to set right cutoff of histogram
356359

357360
allocsstr = if allocs(t) == 0
@@ -406,90 +409,74 @@ function Base.show(io::IO, ::MIME"text/plain", t::Trial)
406409

407410
printstyled(io, pad, "", boxspace; color=boxcolor)
408411
printstyled(io, "min "; color=:default)
409-
printstyled(io, prettytime(minimum(t.times)); color=:default, bold=true)
412+
mintime = minimum(t.times)
413+
printstyled(io, prettytime(mintime); color=:default, bold=true)
410414
print(io, ", ")
411415
printstyled(io, "median "; color=medcolor)
412-
printstyled(io, prettytime(median(t.times)); color=medcolor, bold=true)
416+
medtime = median(t.times)
417+
printstyled(io, prettytime(medtime); color=medcolor, bold=true)
413418
# printstyled(io, " (½)"; color=medcolor)
414419
print(io, ", ")
415420
printstyled(io, "mean "; color=avgcolor)
416-
printstyled(io, prettytime(mean(t.times)); color=avgcolor, bold=true)
421+
avgtime = mean(t.times)
422+
printstyled(io, prettytime(avgtime); color=avgcolor, bold=true)
417423
# printstyled(io, " (*)"; color=avgcolor)
418424
print(io, ", ")
419425
print(io, showpercentile, "ᵗʰ ")
420-
printstyled(prettytime(quantile(t.times, showpercentile/100)); bold=true)
426+
quantime = quantile(t.times, showpercentile/100)
427+
printstyled(prettytime(quantime); bold=true)
421428
println(io)
422429

423430
printstyled(io, pad, "", boxspace; color=boxcolor)
424431
println(io, allocsstr)
425432

426433
if !all(iszero, t.gctimes)
427434
# Mean GC time is just that; then we take the percentage of the mean time
428-
avggctime = prettytime(mean(t.gctimes))
429-
avegcpercent = prettypercent(mean(t.gctimes) / mean(t.times))
435+
printstyled(io, pad, "", boxspace; color=boxcolor)
436+
_avgc = mean(t.gctimes)
437+
print(io, "GC time: mean ", prettytime(_avgc))
438+
printstyled(io, " (", prettypercent(_avgc / avgtime), ")"; color=avgcolor)
430439

431440
# Maximum GC time is _not_ taken as the GC time of the slowst run, maximum(t).
432441
# The percentage shown is of the same max-GC run, again not the percentage of longest time.
433442
# Of course, very often the slowest run is due to GC, and these distinctions won't matter.
434443
_t, _i = findmax(t.gctimes)
435-
maxgctime = prettytime(_t)
436-
maxgcpercent = prettypercent(_t / t.times[_i])
437-
438-
printstyled(io, pad, "", boxspace; color=boxcolor)
439-
print(io, "GC time: mean ", avggctime)
440-
printstyled(io, " (", avegcpercent, ")"; color=avgcolor)
441-
println(io, ", max ", maxgctime, " (", maxgcpercent, ")")
444+
println(io, ", max ", prettytime(_t), " (", prettypercent(_t / t.times[_i]), ")")
442445
end
443446

444447
# Histogram
445448

446-
# Axis ends at this quantile, same as displayed time, ideally:
447-
histquantile = showpercentile/100
449+
logbins = get(io, :logbins, nothing) == true
450+
caption = logbins ? ("log(counts) from " * samplesstr) : samplesstr
451+
448452
# The height and width of the printed histogram in characters:
449-
histheight = 2
450-
histwidth = max(min(90, displaysize(io)[2]), length(samplesstr) + 24) - 5 - length(boxspace)
453+
histheight = 2
454+
_nonhistwidth = 5 + length(boxspace)
455+
_minhistwidth = 18 + length(caption)
456+
histwidth = max(_minhistwidth, min(90, displaysize(io)[2]) - _nonhistwidth)
451457
# This should fit it within your terminal, but stops growing at 90 columns. Below about
452458
# 55 columns it will stop shrinking, by which point the first line has already wrapped.
453459

454-
times = sort(t.times)
455-
456-
# This needs sorted times:
457-
histtimes = times[1:round(Int, histquantile*end)]
458-
histmin = get(io, :histmin, low_edge(histtimes))
459-
histmax = get(io, :histmax, high_edge(histtimes))
460-
461-
logbins = get(io, :logbins, nothing)
462-
bins = bindata(histtimes, histwidth - 1, histmin, histmax)
463-
append!(bins, [1, floor((1-histquantile) * length(times))])
464-
if logbins === true
465-
bins, logbins = log.(1 .+ bins), true
466-
else
467-
logbins = false
460+
histmin = get(io, :histmin, low_edge(t.times, mintime, avgtime))
461+
histmax = get(io, :histmax, high_edge(t.times, avgtime, quantime))
462+
463+
# Here nextfloat() ensures both endpoints included, will only matter for
464+
# artificial cases such as: Trial(Parameters(), [3,4,5], [0,0,0], 0, 0)
465+
fences = range(histmin, nextfloat(histmax), length=histwidth)
466+
bins = bindata(t.times, fences)
467+
# Last bin is everything right of last fence, introduce a gap for printing:
468+
_lastbin = pop!(bins)
469+
push!(bins, 0, _lastbin)
470+
if logbins
471+
bins = log.(1 .+ bins)
468472
end
469473
hist = asciihist(bins, histheight)
470-
hist[:,end-1] .= ' '
471-
maxbin = maximum(bins)
472-
473-
# delta1 = (histmax - histmin) / (histwidth - 1)
474-
# if delta1 > 0
475-
# medpos = 1 + round(Int, (histtimes[length(times) ÷ 2] - histmin) / delta1)
476-
# avgpos = 1 + round(Int, (mean(times) - histmin) / delta1)
477-
# # TODO replace with searchsortedfirst & range?
478-
# q25pos = 1 + round(Int, (histtimes[max(1,length(times) ÷ 4)] - histmin) / delta1)
479-
# q75pos = 1 + round(Int, (histtimes[3*length(times) ÷ 4] - histmin) / delta1)
480-
# else
481-
# medpos, avgpos = 1, 1
482-
# q25pos, q75pos = 1, 1
483-
# end
484-
_centres = range(histmin, histmax, length=length(bins)-2)
485-
fences = _centres .+ step(_centres)/2
486-
avgpos = searchsortedfirst(fences, mean(times))
487-
medpos = searchsortedfirst(fences, median(times))
488-
# @show medpos medpos2
489-
q25pos = searchsortedfirst(fences, quantile(times, 0.25))
490-
q75pos = searchsortedfirst(fences, quantile(times, 0.75))
491-
# @show q25pos q25pos2
492-
# @show q75pos q75pos2
474+
hist[:, end-1] .= ' '
475+
476+
avgpos = searchsortedlast(fences, avgtime)
477+
medpos = searchsortedlast(fences, medtime)
478+
q25pos = searchsortedlast(fences, quantile(t.times, 0.25)) # might be 0, that's OK
479+
q75pos = searchsortedlast(fences, quantile(t.times, 0.75))
493480

494481
# Above the histogram bars, print markers for special ones:
495482
printstyled(io, pad, "", boxspace; color=boxcolor)
@@ -499,12 +486,13 @@ function Base.show(io::IO, ::MIME"text/plain", t::Trial)
499486
if i == avgpos
500487
printstyled(io, "*", color=avgcolor, bold=true)
501488
elseif i == medpos ||
502-
(medpos==avgpos && i==medpos-1 && median(times)<=mean(times)) ||
503-
(medpos==avgpos && i==medpos+1 && median(times)>mean(times))
504-
# marker for "median" is moved one to the left if they collide exactly
489+
(medpos==avgpos && i==medpos-1 && medtime<=avgtime) ||
490+
(medpos==avgpos && i==medpos+1 && medtime>avgtime)
491+
# Marker for "median" is moved one to the left if they collide exactly
505492
# printstyled(io, "½", color=medcolor)
506493
printstyled(io, "", color=medcolor)
507494
elseif i == q25pos
495+
# Quartile markers exist partly to explain the median marker, without needing a legend
508496
# printstyled(io, "¼", color=:light_black)
509497
printstyled(io, "", color=:light_black)
510498
elseif i == q75pos
@@ -521,46 +509,42 @@ function Base.show(io::IO, ::MIME"text/plain", t::Trial)
521509
istop = findlast(!=(' '), view(hist, r, :))
522510
for (i, bar) in enumerate(view(hist, r, :))
523511
i > istop && break # don't print trailing spaces, as they waste space when line-wrapped
524-
color = :default
525512
if i == avgpos
526-
color = avgcolor
527-
elseif i == medpos # if the bars co-incide, colour the mean? matches labels
528-
color = medcolor
513+
printstyled(io, bar; color=avgcolor)
514+
# If mean & median bars co-incide, colour the mean. Matches markers above.
515+
elseif i == medpos
516+
printstyled(io, bar; color=medcolor)
529517
elseif bins[i] == 0
530-
color = :light_black
518+
printstyled(io, bar; color=:light_black)
519+
else
520+
printstyled(io, bar; color=:default)
531521
end
532-
printstyled(io, bar; color=color)
522+
533523
end
534524
end
535525

536-
remtrailingzeros(timestr) = replace(timestr, r"\.?0+ " => " ")
537-
minhisttime, maxhisttime = remtrailingzeros.(prettytime.(round.([histmin, histmax], sigdigits=3)))
526+
# Strings for axis labels, rounded again in case you supply :histmin => 123.456
527+
minhisttime = replace(prettytime(round(histmin, sigdigits=3)), r"\.?0+ " => " ")
528+
maxhisttime = replace(prettytime(round(histmax, sigdigits=3)), r"\.?0+ " => " ")
538529

539530
println(io)
540531
printstyled(io, pad, "", boxspace; color=boxcolor)
541532
print(io, minhisttime)
542-
# Caption is only printed if logbins has been selected:
543-
caption = logbins ? ("log(counts) from " * samplesstr) : samplesstr
544-
printstyled(io, " " ^ ((histwidth - length(caption)) ÷ 2 - length(minhisttime)), caption; color=:light_black)
533+
printstyled(io, " " ^ ((histwidth - length(caption)) ÷ 2 - length(minhisttime)), caption; color=captioncolor)
545534
print(io, lpad(maxhisttime, ceil(Int, (histwidth - length(caption)) / 2) - 1), " ")
546535
print(io, "+")
547-
# printstyled(io, "●", color=:light_black) # other options...
548-
# print(io, "⋯")
549-
# printstyled(io, "¹⁰⁰", color=:light_black)
536+
# printstyled(io, "●", color=:light_black) # other options "⋯" "¹⁰⁰"
550537
end
551538

552539
# I wondered about rounding to a few steps per decade. This looks good in 1:10, not great outside:
553540
# round.(0:0.01:10, sigdigits=3, base=2) |> unique
554-
function low_edge(times)
555-
# _min = minimum(times)
556-
_min = min(minimum(times), mean(times) / 1.03) # demand low edge 3% below mean
541+
function low_edge(times, lo=minimum(times), av=mean(times))
542+
_min = min(lo, av / 1.03) # demand low edge 3% from mean, or further
557543
return round(_min, RoundDown; sigdigits = 2)
558544

559545
end
560-
function high_edge(times)
561-
# _max = maximum(times)
562-
# _max = Base.max(maximum(times), 1.07 * low_edge(times)) # demand total width at least 7%
563-
_max = max(maximum(times), 1.03 * mean(times)) # demand high edge 3% above mean
546+
function high_edge(times, av=mean(times), hi=quantile(times, 0.99))
547+
_max = max(1, hi, 1.03 * av) # demand high edge 3% above mean, and at least 1ns
564548
return round(_max, RoundUp; sigdigits = 2)
565549
end
566550

@@ -592,3 +576,41 @@ function Base.show(io::IO, ::MIME"text/plain", t::TrialJudgement)
592576
printmemoryjudge(io, t)
593577
println(io, " (", prettypercent(params(t).memory_tolerance), " tolerance)")
594578
end
579+
580+
581+
#=
582+
# Some visual checks, designed so that mean/median should hit a bar
583+
584+
using BenchmarkTools: Trial, Parameters
585+
Trial(Parameters(), [pi * 10^9], [0], 0, 0) # one sample
586+
587+
# mean == median, one bar
588+
Trial(Parameters(), [pi, pi], [0, 0], 0, 0)
589+
Trial(Parameters(), fill(101, 33), vcat(zeros(32), 50), 0, 0)
590+
591+
# mean == median, three bars -- wrong highlighting before
592+
Trial(Parameters(), [3,4,5], [0,0,0], 0, 0)
593+
594+
# three bars, including mean not median -- wrong highlighting before
595+
Trial(Parameters(), pi * [1,3,4,4], [0,0,0,100], 1, 1)
596+
597+
# three bars, including median & both quartiles, not mean -- wrong before
598+
Trial(Parameters(), 99.9 * [1,1,3,14,16], [0,0,99,0,0], 222, 2)
599+
600+
# same, but smaller range. Note also max GC is not max
601+
Trial(Parameters(), 999 .+ [1,1,3,14,16], [0,0,123,0,0], 45e6, 7)
602+
603+
604+
# Check that auto-sizing stops on very small widths:
605+
io = IOContext(stdout, :displaysize => (25,30))
606+
show(io, MIME("text/plain"), Trial(Parameters(), [3,4,5], [0,0,0], 0, 0))
607+
show(io, MIME("text/plain"), Trial(Parameters(), repeat(100 * [3,4,5], 10^6), zeros(3*10^6), 0, 0))
608+
609+
io = IOContext(stdout, :displaysize => (25,50), :logbins => true) # this is wider
610+
show(io, MIME("text/plain"), Trial(Parameters(), 100 * [3,4,5], [0,0,0], 0, 0))
611+
612+
# Check that data off the left is OK, and median still highlighted:
613+
io = IOContext(stdout, :histmin => 200.123)
614+
show(io, MIME("text/plain"), Trial(Parameters(), 99.9 * [1,1,3,14,16], [0,0,99,0,0], 222, 2))
615+
616+
=#

0 commit comments

Comments
 (0)