1 # Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  2 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  3 #
  4 # This code is free software; you can redistribute it and/or modify it
  5 # under the terms of the GNU General Public License version 2 only, as
  6 # published by the Free Software Foundation.
  7 #
  8 # This code is distributed in the hope that it will be useful, but WITHOUT
  9 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 10 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 11 # version 2 for more details (a copy is included in the LICENSE file that
 12 # accompanied this code).
 13 #
 14 # You should have received a copy of the GNU General Public License version
 15 # 2 along with this work; if not, write to the Free Software Foundation,
 16 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 17 #
 18 # Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 19 # or visit www.oracle.com if you need additional information or have any
 20 # questions.
 21 
 22 #======================================================================
 23 # Overview
 24 #
 25 # This file provides the framework for running start-up benchmarks that have very
 26 # short elapsed time.
 27 #
 28 # The goal is to be able measure very minor improvements (under 1%) even when there
 29 # are system noises.
 30 #
 31 # - Measure the entire elapsed time with 'perf stat -r 16 bin/java .....'
 32 # - Interleave the execution of the test JVMs. This way, the effect of system noises 
 33 #   (CPU overheating, throttling, background tasks) is likely evenly distributed across
 34 #   the test JVMs.
 35 # - Save the results in a CSV file so you can load it in a spreadsheet and manually
 36 #   check for noisy samples.
 37 #
 38 # NOTE: you can see the effect of CPU throttling in the sample data below. See the
 39 # 2_xon column, which goes from 123ms when the CPU is cold to 128ms when the CPU is hot.
 40 
 41 #======================================================================
 42 # Config
 43 #
 44 # See ../javac_new_workflow/run_bench.sh for an example of using this file
 45 #
 46 # Before sourcing this file
 47 # The caller script should set up the following variables
 48 
 49 # required
 50 #     CMDLINE
 51 #     APP
 52 # optional
 53 #     HEAP_SIZE
 54 #     RUNS
 55 #     REPEAT
 56 #
 57 #     LEYDEN_CALLER_OPTS
 58 #       extra args to be added to Leyden JVM runs
 59 #       e.g. LEYDEN_CALLER_OPTS='-Xlog:scc=error'
 60 #     TASKSET
 61 #       allows pinning of perf runs to specific processors
 62 #       e.g. setting TASKSET='taskest 0xff0' will execute
 63 #       benchmark runs as
 64 #         "taskest 0xff0 perf stat -r $REPEAT $JAVA ... $APP $CMDLINE"
 65 
 66 # The number of the outer loop
 67 if test "$RUNS" = ""; then
 68     RUNS=10
 69 fi
 70 
 71 # The number passed to "perf stat -r"
 72 if test "$REPEAT" = ""; then
 73     REPEAT=16
 74 fi
 75 
 76 #======================================================================
 77 # Example
 78 #
 79 # FYI: Results I got on 2024/05/14, AArch64
 80 # With JDK mainline repo and leyden-premain branch that are pretty up to date.
 81 #
 82 # $ cd test/hotspot/jtreg/premain/javac_helloworld
 83 # $ bash run.sh .../mainline/images/jdk/bin/java .../leyden/images/jdk/bin/java
 84 # 
 85 # ===report.csv================================================
 86 # Run,1_xoff,1_xon,2_xoff,2_xon,2_td,2_aot
 87 # 1,245.95000,165.35000,253.3000,135.85000,114.08000,80.13000
 88 # 2,255.98000,172.27000,249.61000,131.03000,111.25000,92.39000
 89 # 3,244.63000,149.85000,247.29000,138.09000,113.11000,94.04000
 90 # 4,252.84000,150.57000,247.35000,144.17000,110.42000,93.87000
 91 # 5,247.72000,156.89000,249.38000,149.35000,111.93000,94.74000
 92 # 6,259.24000,167.54000,239.00000,147.69000,109.98000,84.89000
 93 # 7,241.83000,150.16000,248.35000,154.1000,110.58000,91.57000
 94 # 8,247.79000,143.51000,248.72000,144.88000,108.20000,78.43000
 95 # 9,254.68000,155.95000,253.50000,134.91000,114.17000,93.27000
 96 # 10,250.66000,166.34000,260.68000,142.91000,117.24000,87.14000
 97 # ==============================jvm1 /home/adinn/redhat/openjdk/jdkdev/image-jdk-master/jdk/bin/java
 98 # [1_xoff] Mainline JDK (CDS disabled)                   250.08 ms
 99 # [1_xon ] Mainline JDK (CDS enabled)                    157.58 ms
100 # ==============================jvm2 /home/adinn/redhat/openjdk/jdkdev/image-premain/jdk/bin/java
101 # [2_xoff] Premain JDK (CDS disabled)                    249.66 ms
102 # [2_xon ] Premain JDK (CDS enabled)                     142.13 ms
103 # [2_td  ] Premain Prototype (CDS + Training Data)        112.07 ms
104 # [2_aot ] Premain Prototype (CDS + Training Data + AOT)  88.86 ms
105 # 
106 #======================================================================
107 
108 
109 # "$@" is the command-line specified by the user. Each element specifies a 
110 # JVM (which optionally can contain VM parameters).
111 #
112 # We automatically detect whether the JVM is from the mainline, or from the leyden
113 # repo.
114 function main () {
115     do_dump "$@"
116     do_exec "$@"
117     do_summary "$@"
118 }
119 
120 function do_dump () {
121     if test "$NODUMP" != ""; then
122         return
123     fi
124 
125     local n=0
126     for i in "$@"; do
127         n=$(expr $n + 1)
128         JAVA="$i"
129         if test "$HEAP_SIZE" != ""; then
130             JAVA="$JAVA $HEAP_SIZE"
131         fi
132         dump_one_jvm $n
133     done
134 }
135 
136 function do_exec () {
137     if test "$NOEXEC" != ""; then
138         return
139     fi
140 
141     rm -rf logs
142     mkdir -p logs
143     rm -f report.csv
144 
145     REPORT_HEADER='Run'
146 
147     local n=0
148     for i in "$@"; do
149         n=$(expr $n + 1)
150         JAVA="$i"
151         if test "$HEAP_SIZE" != ""; then
152             JAVA="$JAVA $HEAP_SIZE"
153         fi
154         local APPID=$APP-jvm$n
155         if test -f $APPID-mainline.classlist; then
156             REPORT_HEADER=${REPORT_HEADER},${n}_xoff
157             REPORT_HEADER=${REPORT_HEADER},${n}_xon
158         else
159             REPORT_HEADER=${REPORT_HEADER},${n}_xoff
160             REPORT_HEADER=${REPORT_HEADER},${n}_xon
161             REPORT_HEADER=${REPORT_HEADER},${n}_td
162             REPORT_HEADER=${REPORT_HEADER},${n}_aot
163         fi
164         #report jvm$n = $JAVA
165     done
166 
167     report $REPORT_HEADER
168 
169     # The #0 run is to warm up the CPU and is not counted
170     for r in $(seq 0 $RUNS); do
171         if test $r = 0; then
172             echo "RUN $r (warmup - discarded)"
173         else
174             echo "RUN $r of $RUNS"
175         fi
176         RUNLOG=$r
177         local n=0
178         for i in "$@"; do
179             n=$(expr $n + 1)
180             JAVA="$i"
181             if test "$HEAP_SIZE" != ""; then
182                 JAVA="$JAVA $HEAP_SIZE"
183             fi
184             echo === jvm$n
185             exec_one_jvm $n $r
186         done
187 
188         if test $r -gt 0; then
189             report $RUNLOG
190         else
191             echo $RUNLOG
192         fi
193     done
194 }
195 
196 function dump_one_jvm () {
197     local binjava=$(echo $JAVA | sed -e 's/ .*//g')
198     local APPID=$APP-jvm$1
199 
200     if $JAVA --version > /dev/null; then
201         if $JAVA -XX:+PrintFlagsFinal --version | grep CDSManualFinalImage > /dev/null; then
202             local is_premain=1
203             local type_msg="(premain )"
204         else
205             local is_premain=0
206             local type_msg="(mainline)"
207         fi
208     else
209         echo "$JAVA doesn't seem to be a real JVM"
210         exit 1
211     fi
212 
213     echo ========================================
214     echo dumping archive "$type_msg" for $JAVA
215     echo ========================================
216 
217     if test $is_premain = 0; then
218         echo "(Mainline) Dump classlist"
219         (set -x; $JAVA -Xshare:off -XX:DumpLoadedClassList=$APPID-mainline.classlist $CMDLINE) || exit 1
220 
221         echo "(Mainline) Dump CDS archive"
222         rm -f $APPID-mainline-static.dump.log
223         (set -x; $JAVA -Xshare:dump -XX:SharedArchiveFile=$APPID-mainline.jsa -XX:SharedClassListFile=$APPID-mainline.classlist \
224                        -Xlog:cds=debug,cds+class=debug:file=$APPID-mainline-static.dump.log::filesize=0 $CMDLINE) || exit 1
225 
226     else
227         echo "(Premain) Dump classlist"
228         (set -x; $JAVA -Xshare:off -XX:DumpLoadedClassList=$APPID-premain.classlist $CMDLINE) || exit 1
229 
230         echo "(Premain) Dump CDS archive"
231         rm -f $APPID-premain-static.dump.log
232         (set -x; $JAVA -Xshare:dump -XX:SharedArchiveFile=$APPID-premain.jsa -XX:SharedClassListFile=$APPID-premain.classlist \
233                        -Xlog:cds=debug,cds+class=debug:file=$APPID-premain-static.dump.log::filesize=0 $CMDLINE) || exit 1
234 
235 
236         LEYDEN_OPTS="$LEYDEN_CALLER_OPTS"
237 
238         echo "(Premain) Dump Leyden archive"
239         (set -x; $JAVA -XX:CacheDataStore=$APPID.cds $LEYDEN_OPTS $CMDLINE) || exit 1
240 
241     fi
242 }
243 
244 function exec_one_jvm () {
245     local APPID=$APP-jvm$1
246 
247     if test -f $APPID-mainline.classlist; then
248         (set -x;
249          $TASKSET perf stat -r $REPEAT $JAVA -Xshare:off $CMDLINE 2> logs/${1}_xoff.$2
250         )
251         RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xoff.$2)
252 
253         (set -x;
254          $TASKSET perf stat -r $REPEAT $JAVA -XX:SharedArchiveFile=$APPID-mainline.jsa $CMDLINE 2> logs/${1}_xon.$2
255         )
256         RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xon.$2)
257     fi
258 
259     if test -f $APPID.cds; then
260         LEYDEN_OPTS="$LEYDEN_CALLER_OPTS"
261         (set -x;
262          $TASKSET perf stat -r $REPEAT $JAVA -Xshare:off $CMDLINE 2> logs/${1}_xoff.$2
263         )
264         RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xoff.$2)
265 
266         (set -x;
267          $TASKSET perf stat -r $REPEAT $JAVA -XX:SharedArchiveFile=$APPID-premain.jsa $CMDLINE 2> logs/${1}_xon.$2
268         )
269         RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xon.$2)
270         (set -x;
271          $TASKSET perf stat -r $REPEAT $JAVA -XX:CacheDataStore=$APPID.cds -XX:-LoadCachedCode $LEYDEN_OPTS  $CMDLINE 2> logs/${1}_td.$2
272         )
273         RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_td.$2)
274         (set -x;
275          $TASKSET perf stat -r $REPEAT $JAVA -XX:CacheDataStore=$APPID.cds  -XX:+LoadCachedCode $LEYDEN_OPTS  $CMDLINE 2> logs/${1}_aot.$2
276         )
277         RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_aot.$2)
278     fi
279 }
280 
281 function get_elapsed () {
282     elapsed=$(bc <<< "scale=3; $(cat $1 | grep elapsed | sed -e 's/[+].*//') * 1000")
283     echo $elapsed >> $1.elapsed
284     echo $elapsed
285 }
286 
287 
288 function report () {
289     echo "$@"
290     echo "$@" >> report.csv
291 }
292 
293 function geomean () {
294     printf "%6.2f ms" $(awk 'BEGIN{E = exp(1);} $1>0{tot+=log($1); c++} END{m=tot/c; printf "%.2f\n", E^m}' $*)
295 }
296 
297 
298 
299 function do_summary () {
300     echo ===report.csv================================================
301     cat report.csv
302 
303     mkdir -p logs/warmup
304     mv logs/*.0.elapsed logs/*.0 logs/warmup
305     local n=0
306     for i in "$@"; do
307         n=$(expr $n + 1)
308         local APPID=$APP-jvm$n
309         report ==============================jvm$n $i
310         if test -f $APPID-mainline.classlist; then
311             report "[$(printf %-6s ${n}_xoff)] Mainline JDK (CDS disabled)                   $(geomean logs/${n}_xoff.*.elapsed)"
312             report "[$(printf %-6s ${n}_xon )] Mainline JDK (CDS enabled)                    $(geomean logs/${n}_xon.*.elapsed)"
313         else
314             report "[$(printf %-6s ${n}_xoff)] Premain JDK (CDS disabled)                    $(geomean logs/${n}_xoff.*.elapsed)"
315             report "[$(printf %-6s ${n}_xon )] Premain JDK (CDS enabled)                     $(geomean logs/${n}_xon.*.elapsed)"
316             report "[$(printf %-6s ${n}_td )] Premain Prototype (CDS + Training Data)        $(geomean logs/${n}_td.*.elapsed)"
317             report "[$(printf %-6s ${n}_aot )] Premain Prototype (CDS + Training Data + AOT) $(geomean logs/${n}_aot.*.elapsed)"
318         fi
319     done
320 }
321 
322 main "$@"