Skip to content

Commit 3510065

Browse files
committed
Convert benchable to use the Perl 6 SVG::Plot
1 parent 326c44a commit 3510065

File tree

2 files changed

+83
-60
lines changed

2 files changed

+83
-60
lines changed

Benchable.p6

Lines changed: 82 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ use Whateverable;
2121

2222
use IRC::Client;
2323

24+
use SVG;
25+
use SVG::Plot;
26+
use File::Directory::Tree;
2427
use Stats;
25-
use Chart::Gnuplot:from<Perl5>;
26-
use Chart::Gnuplot::DataSet:from<Perl5>;
2728

2829
unit class Benchable is Whateverable;
2930

@@ -40,7 +41,7 @@ multi method benchmark-code($full-commit, $filename) {
4041
my @times;
4142
my %stats;
4243
for ^ITERATIONS {
43-
my ($, $exit, $signal, $time) = self.get-output("{BUILDS}/$full-commit/bin/perl6", $filename);
44+
my ($, $exit, $signal, $time) = self.run-snippet($full-commit, $filename);
4445
if $exit == 0 {
4546
@times.push: sprintf('%.4f', $time);
4647
} else {
@@ -57,42 +58,72 @@ multi method benchmark-code($full-commit, $filename) {
5758
return %stats;
5859
}
5960

60-
multi method benchmark-code($full-commit, @code) {
61+
multi method benchmark-code($full-commit-hash, @code) {
6162
my $code-to-compare = 'use Bench; my %subs = ' ~ @code.kv.map({ $^k => " => sub \{ $^v \} " }).join(',') ~ ';'
6263
~ ' my $b = Bench.new; $b.cmpthese(' ~ ITERATIONS*2 ~ ', %subs)';
63-
my ($timing) = self.get-output("{BUILDS}/$full-commit/bin/perl6", '-I', "{LIB-DIR}/perl6-bench/lib,{LIB-DIR}/Perl6-Text--Table--Simple/lib", '-e', $code-to-compare);
6464

65+
# old builds # TODO remove after transition
66+
if {LEGACY-BUILDS-LOCATION}/$full-commit-hash.IO ~~ :e {
67+
if {LEGACY-BUILDS-LOCATION}/$full-commit-hash/bin/perl6.IO !~~ :e {
68+
return commit exists, but a perl6 executable could not be built for it;
69+
}
70+
return self.get-output({LEGACY-BUILDS-LOCATION}/$full-commit-hash/bin/perl6, '--setting=RESTRICTED', '-I', "{LIB-DIR}/perl6-bench/lib,{LIB-DIR}/Perl6-Text--Table--Simple/lib", '-e', $code-to-compare).head;
71+
}
72+
73+
# lock on the destination directory to make
74+
# sure that other bots will not get in our way.
75+
while run(mkdir, --, {BUILDS-LOCATION}/$full-commit-hash).exitcode != 0 {
76+
sleep 0.5;
77+
# Uh, wait! Does it mean that at the same time we can use only one
78+
# specific build? Yes, and you will have to wait until another bot
79+
# deletes the directory so that you can extract it back again…
80+
# There are some ways to make it work, but don't bother. Instead,
81+
# we should be doing everything in separate isolated containers (soon),
82+
# so this problem will fade away.
83+
}
84+
my $proc = run(:out, :bin, zstd, -dqc, --, {ARCHIVES-LOCATION}/$full-commit-hash.zst);
85+
run(:in($proc.out), :bin, tar, x, --absolute-names);
86+
my $timing;
87+
if {BUILDS-LOCATION}/$full-commit-hash/bin/perl6.IO !~~ :e {
88+
return Commit exists, but a perl6 executable could not be built for it;
89+
} else {
90+
$timing = self.get-output({BUILDS-LOCATION}/$full-commit-hash/bin/perl6, '--setting=RESTRICTED', '-I', "{LIB-DIR}/perl6-bench/lib,{LIB-DIR}/Perl6-Text--Table--Simple/lib", '-e', $code-to-compare).head;
91+
}
92+
rmtree {BUILDS-LOCATION}/$full-commit-hash;
6593
return $timing;
6694
}
67-
6895

69-
multi method irc-to-me($message where .text ~~ /^ \s* $<config>=([:i compare \s]? \S+) \s+ $<code>=.+ /) {
70-
my ($value, %additional-files) = self.process($message, ~$<config>, ~$<code>);
71-
return ResponseStr.new(:$value, :$message, :%additional-files);
96+
multi method irc-to-me($message where { .text !~~ /:i ^ [help|source|url] ‘?’? $ | ^stdin /
97+
# ↑ stupid, I know. See RT #123577
98+
and .text ~~ /^ \s* $<config>=([:i compare \s]? \S+) \s+ $<code>=.+ / }) {
99+
my ($value, %additional_files) = self.process($message, ~$<config>, ~$<code>);
100+
return ResponseStr.new(:$value, :$message, :%additional_files);
72101
}
73102

74103
method process($message, $config, $code is copy) {
75104
my $start-time = now;
105+
my @commits;
76106
my $old-dir = $*CWD;
77107

78108
my $msg-response = '';
79109
my %graph;
80110

81-
my @commits;
82-
if $config ~~ / ',' / {
83-
@commits = $config.split: ',';
84-
} elsif $config ~~ /^ $<start>=\S+ \.\. $<end>=\S+ $/ {
85-
chdir RAKUDO;
86-
return "Bad start" if run('git', 'rev-parse', '--verify', $<start>).exitcode != 0;
87-
return "Bad end" if run('git', 'rev-parse', '--verify', $<end>).exitcode != 0;
88-
89-
my ($result, $exit-status, $exit-signal, $time) = self.get-output('git', 'rev-list', "$<start>^..$<end>");
90-
91-
return "Couldn't find anything in the range" if $exit-status != 0;
92-
93-
@commits = $result.split: "\n";
111+
if $config ~~ / ‘,’ / {
112+
@commits = $config.split: ,;
113+
} elsif $config ~~ /^ $<start>=\S+ ‘..’ $<end>=\S+ $/ {
114+
chdir RAKUDO; # goes back in LEAVE
115+
if run(git, rev-parse, --verify, $<start>).exitcode != 0 {
116+
return Bad start, cannot find a commit for “$<start>”;
117+
}
118+
if run(git, rev-parse, --verify, $<end>).exitcode != 0 {
119+
return Bad end, cannot find a commit for “$<end>”;
120+
}
121+
my ($result, $exit-status, $exit-signal, $time) =
122+
self.get-output(git, rev-list, $<start>^..$<end>); # TODO unfiltered input
123+
return Couldn't find anything in the range if $exit-status != 0;
124+
@commits = $result.split: \n;
94125
my $num-commits = @commits.elems;
95-
return "Too many commits ($num-commits) in range, you're only allowed " ~ LIMIT if $num-commits > LIMIT;
126+
return Too many commits ($num-commits) in range, you're only allowed {LIMIT} if $num-commits > LIMIT;
96127
} elsif $config ~~ /:i releases / {
97128
@commits = @.releases;
98129
} elsif $config ~~ /:i compare \s $<commit>=\S+ / {
@@ -107,15 +138,16 @@ method process($message, $config, $code is copy) {
107138

108139
my $filename = self.write-code($code);
109140

141+
$message.reply: "starting to benchmark the {+@commits} given commits";
110142
my %times;
111143
for @commits -> $commit {
112144
# convert to real ids so we can look up the builds
113145
my $full-commit = self.to-full-commit($commit);
114146
my $short-commit = $commit.substr(0, 7);
115-
if !$full-commit.defined {
116-
%times{$short-commit}<err> = 'Cannot find this revision';
117-
} elsif {BUILDS}/$full-commit/bin/perl6.IO !~~ :e {
118-
%times{$short-commit}<err> = 'No build for this commit';
147+
if not defined $full-commit {
148+
%times{$short-commit}<err> = Cannot find this revision;
149+
} elsif not self.build-exists($full-commit) {
150+
%times{$short-commit}<err> = No build for this commit;
119151
} else { # actually run the code
120152
if $config ~~ /:i compare / {
121153
%times{$short-commit} = self.benchmark-code($full-commit, $code.split('|||'));
@@ -133,9 +165,10 @@ method process($message, $config, $code is copy) {
133165
# recursively find the commit in the middle until there are either no more large speed differences or no
134166
# more commits inbetween (i.e., the next commit is the exact one that caused the difference)
135167
if $config ~~ /:i releases / or $config ~~ / ',' / {
168+
$message.reply: 'benchmarked the given commits, now zooming in on performance differences';
136169
chdir RAKUDO;
137170

138-
Z: loop (my int $x = 0; $x < +@commits - 1; $x++) {
171+
Z: loop (my int $x = 0; $x < @commits - 1; $x++) {
139172
if (now - $start-time > TOTAL-TIME) {
140173
return "«hit the total time limit of {TOTAL-TIME} seconds»";
141174
}
@@ -144,10 +177,10 @@ Z: loop (my int $x = 0; $x < +@commits - 1; $x++) {
144177
next if %times{@commits[$x]}<err>:exists or %times{@commits[$x + 1]}<err>:exists; # and without error
145178
if abs(%times{@commits[$x]}<min> - %times{@commits[$x + 1]}<min>) >= %times{@commits[$x]}<min>*0.1 {
146179
my ($new-commit, $exit-status, $exit-signal, $time) = self.get-output('git', 'rev-list', '--bisect', '--no-merges', @commits[$x] ~ '^..' ~ @commits[$x + 1]);
147-
if $exit-status == 0 and $new-commit.defined and $new-commit ne '' {
180+
if $exit-status == 0 and $new-commit.defined and $new-commit ne '' {
148181
my $short-commit = $new-commit.substr(0, 7);
149-
if "{BUILDS}/$new-commit/bin/perl6".IO !~~ :e {
150-
%times{$short-commit}<err> = 'No build for this commit';
182+
if not self.build-exists($new-commit) {
183+
%times{$short-commit}<err> = No build for this commit;
151184
} elsif %times{$short-commit}:!exists and $short-commit ne @commits[$x] and $short-commit ne @commits[$x + 1] { # actually run the code
152185
%times{$short-commit} = self.benchmark-code($new-commit, $filename);
153186
@commits.splice($x + 1, 0, $short-commit);
@@ -158,38 +191,28 @@ Z: loop (my int $x = 0; $x < +@commits - 1; $x++) {
158191
}
159192
}
160193

194+
@commits .= map(*.substr(0, 7));
195+
161196
if @commits >= ITERATIONS {
162-
chdir $old-dir;
163-
my $gfilename = 'graph.svg';
197+
my $pfilename = 'plot.svg';
164198
my $title = "$config $code".trans(['"'] => ['\"']);
165-
my @ydata = @commits.map({ .<err> // .<min> with %times{$_.substr(0, 7)} });
166-
my $chart = Chart::Gnuplot.new(
167-
output => $gfilename,
168-
encoding => 'utf8',
169-
title => {
170-
text => $title.encode('UTF-8'),
171-
enhanced => 'off',
172-
},
173-
size => '2,1',
174-
# terminal => 'svg mousing',
175-
xlabel => {
176-
text => 'Commits\\nMean,Max,Stddev',
177-
offset => '0,-1',
178-
},
179-
xtics => { labels => [@commits.kv.map({ my $commit = $^v.substr(0, 7); "\"$commit\\n{.<err> // .<mean max stddev>.join(',') with %times{$commit}}\" $^k" })], },
180-
ylabel => 'Seconds',
181-
yrange => [0, @ydata.grep(*.Num).max * 1.25],
182-
);
183-
my $dataSet = Chart::Gnuplot::DataSet.new(
184-
ydata => item(@ydata),
185-
style => 'linespoints',
186-
);
187-
$chart.plot2d($dataSet);
188-
189-
%graph{$gfilename} = $gfilename.IO.slurp;
199+
my @valid-commits = @commits.grep({ %times{$_}<err>:!exists });
200+
my @values = @valid-commits.map({ %times{$_}<min> });
201+
my @labels = @valid-commits.map({ "$_ ({ .<mean max stddev>.map({ sprintf("%.2f", $_) }).join(',') with %times{$_} })" });
202+
203+
my $plot = SVG::Plot.new(
204+
width => 1000,
205+
height => 800,
206+
min-y-axis => 0,
207+
:$title,
208+
values => (@values,),
209+
:@labels,
210+
).plot(:lines);
211+
212+
%graph{$pfilename} = SVG.serialize($plot);
190213
}
191214

192-
$msg-response ~= '¦' ~ @commits.map({ my $c = .substr(0, 7); "«$c»:" ~ (%times{$c}<err> // %times{$c}<min> // %times{$c}) }).join("\n¦");
215+
$msg-response ~= '¦' ~ @commits.map({ "«$_»:" ~(%times{$_}<err> // %times{$_}<min> // %times{$_}) }).join("\n¦");
193216

194217
return ($msg-response, %graph);
195218

Whateverable.pm6

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ unit class Whateverable does IRC::Client::Plugin;
3737

3838
has $!timeout = 10;
3939
has $!stdin = slurp stdin;
40-
has $!releases = <2015.10 2015.11 2015.12 2016.02 2016.03 2016.04 2016.05 2016.06 2016.07.1 2016.08.1 HEAD>;
40+
has $.releases = <2015.10 2015.11 2015.12 2016.02 2016.03 2016.04 2016.05 2016.06 2016.07.1 2016.08.1 HEAD>;
4141

4242
class ResponseStr is Str is export {
4343
# I know it looks crazy, but we will subclass a Str and hope

0 commit comments

Comments
 (0)