@@ -21,9 +21,10 @@ use Whateverable;
21
21
22
22
use IRC::Client;
23
23
24
+ use SVG;
25
+ use SVG::Plot;
26
+ use File::Directory::Tree;
24
27
use Stats;
25
- use Chart::Gnuplot: from<Perl5 >;
26
- use Chart::Gnuplot::DataSet: from<Perl5 >;
27
28
28
29
unit class Benchable is Whateverable;
29
30
@@ -40,7 +41,7 @@ multi method benchmark-code($full-commit, $filename) {
40
41
my @ times ;
41
42
my % stats ;
42
43
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 );
44
45
if $ exit == 0 {
45
46
@ times . push : sprintf (' %.4f' , $ time );
46
47
} else {
@@ -57,42 +58,72 @@ multi method benchmark-code($full-commit, $filename) {
57
58
return % stats ;
58
59
}
59
60
60
- multi method benchmark-code ($ full-commit , @ code ) {
61
+ multi method benchmark-code ($ full-commit-hash , @ code ) {
61
62
my $ code-to-compare = ' use Bench; my %subs = ' ~ @ code . kv . map ({ $ ^ k => " => sub \{ $ ^ v \} " }). join (' ,' ) ~ ' ;'
62
63
~ ' 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 );
64
64
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” ;
65
93
return $ timing ;
66
94
}
67
-
68
95
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 );
72
101
}
73
102
74
103
method process ($ message , $ config , $ code is copy ) {
75
104
my $ start-time = now;
105
+ my @ commits ;
76
106
my $ old-dir = $ * CWD ;
77
107
78
108
my $ msg-response = ' ' ;
79
109
my % graph ;
80
110
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 ” ;
94
125
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;
96
127
} elsif $ config ~~ /:i releases / {
97
128
@ commits = @ . releases ;
98
129
} elsif $ config ~~ /:i compare \s $ < commit > =\S + / {
@@ -107,15 +138,16 @@ method process($message, $config, $code is copy) {
107
138
108
139
my $ filename = self . write-code($ code );
109
140
141
+ $ message . reply: " starting to benchmark the { + @ commits } given commits" ;
110
142
my % times ;
111
143
for @ commits -> $ commit {
112
144
# convert to real ids so we can look up the builds
113
145
my $ full-commit = self . to-full-commit($ commit );
114
146
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’ ;
119
151
} else { # actually run the code
120
152
if $ config ~~ /:i compare / {
121
153
% times {$ short-commit } = self . benchmark-code($ full-commit , $ code . split (' |||' ));
@@ -133,9 +165,10 @@ method process($message, $config, $code is copy) {
133
165
# recursively find the commit in the middle until there are either no more large speed differences or no
134
166
# more commits inbetween (i.e., the next commit is the exact one that caused the difference)
135
167
if $ config ~~ /:i releases / or $ config ~~ / ',' / {
168
+ $ message . reply: ' benchmarked the given commits, now zooming in on performance differences' ;
136
169
chdir RAKUDO;
137
170
138
- Z : loop (my int $ x = 0 ; $ x < + @ commits - 1 ; $ x ++ ) {
171
+ Z : loop (my int $ x = 0 ; $ x < @ commits - 1 ; $ x ++ ) {
139
172
if (now - $ start-time > TOTAL-TIME) {
140
173
return " «hit the total time limit of { TOTAL-TIME} seconds»" ;
141
174
}
@@ -144,10 +177,10 @@ Z: loop (my int $x = 0; $x < +@commits - 1; $x++) {
144
177
next if % times {@ commits [$ x ]}<err >: exists or % times {@ commits [$ x + 1 ]}<err >: exists ; # and without error
145
178
if abs (% times {@ commits [$ x ]}<min > - % times {@ commits [$ x + 1 ]}<min >) >= % times {@ commits [$ x ]}<min >* 0.1 {
146
179
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 ' ' {
148
181
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’ ;
151
184
} elsif % times {$ short-commit }:! exists and $ short-commit ne @ commits [$ x ] and $ short-commit ne @ commits [$ x + 1 ] { # actually run the code
152
185
% times {$ short-commit } = self . benchmark-code($ new-commit , $ filename );
153
186
@ commits . splice ($ x + 1 , 0 , $ short-commit );
@@ -158,38 +191,28 @@ Z: loop (my int $x = 0; $x < +@commits - 1; $x++) {
158
191
}
159
192
}
160
193
194
+ @ commits .= map (*. substr (0 , 7 ));
195
+
161
196
if @ commits >= ITERATIONS {
162
- chdir $ old-dir ;
163
- my $ gfilename = ' graph.svg' ;
197
+ my $ pfilename = ' plot.svg' ;
164
198
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 );
190
213
}
191
214
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 ¦" );
193
216
194
217
return ($ msg-response , % graph );
195
218
0 commit comments