1 /* 2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 Used by DemoSupport.gmk: 26 27 - calculate the geomean and stdev of benchmark runs 28 - write markdown snippets for rendering benchmark results as a chart on GitHub .MD files 29 30 Example: 31 32 helidon-quickstart-se$ tbjava ../lib/GithubMDChart.java < mainline_vs_premain.csv 33 run,mainline default,mainline custom static CDS,premain custom static CDS only,premain CDS + AOT 34 1,398,244,144,107 35 2,387,247,142,108 36 3,428,238,143,107 37 4,391,252,142,111 38 5,417,247,141,107 39 6,390,239,139,127 40 7,387,247,145,111 41 8,387,240,147,110 42 9,388,242,147,108 43 10,400,242,167,108 44 Geomean,397.08,243.76,145.52,110.26 45 Stdev,13.55,4.19,7.50,5.73 46 47 ```mermaid 48 --- 49 config: 50 xyChart: 51 chartOrientation: horizontal 52 height: 300 53 --- 54 xychart-beta 55 x-axis "variant" ["mainline default", "mainline custom static CDS", "premain custom static CDS only", "premain CDS + AOT"] 56 y-axis "Elapsed time (ms, smaller is better)" 0 --> 397 57 bar [397, 244, 146, 110] 58 ``` 59 60 */ 61 62 import java.io.PrintWriter; 63 import java.util.Scanner; 64 import java.util.ArrayList; 65 import java.util.Arrays; 66 67 public class GithubMDChart { 68 @SuppressWarnings("unchecked") 69 public static void main(String args[]) throws Exception { 70 Scanner input = new Scanner(System.in); 71 String line = input.nextLine(); 72 //System.out.println(line); 73 String head[] = line.split(","); 74 Object[] groups = new Object[head.length]; 75 String[] geomeans = new String[head.length]; 76 for (int i = 0; i < head.length; i++) { 77 groups[i] = new ArrayList<Double>(); 78 } 79 while (input.hasNext()) { 80 line = input.nextLine(); 81 //System.out.println(line); 82 String parts[] = line.split(","); 83 for (int i = 0; i < head.length; i++) { 84 ArrayList<Double> list = (ArrayList<Double>)(groups[i]); 85 list.add(Double.valueOf(Double.parseDouble(parts[i]))); 86 } 87 } 88 89 for (int i = 0; i < head.length; i++) { 90 ArrayList<Double> list = (ArrayList<Double>)(groups[i]); 91 if (i == 0) { 92 System.out.print("Geomean"); 93 } else { 94 System.out.print(","); 95 geomeans[i] = geomean(list); 96 System.out.print(geomeans[i]); 97 } 98 } 99 100 System.out.println(); 101 102 for (int i = 0; i < head.length; i++) { 103 ArrayList<Double> list = (ArrayList<Double>)(groups[i]); 104 if (i == 0) { 105 System.out.print("Stdev"); 106 } else { 107 System.out.print(","); 108 System.out.print(stdev(list)); 109 } 110 } 111 System.out.println(); 112 System.out.println("Markdown snippets in " + args[0]); 113 114 String[] norm = new String[geomeans.length]; 115 for (int i = 1; i < head.length; i++) { 116 double base = Double.parseDouble(geomeans[1]); 117 double me = Double.parseDouble(geomeans[i]); 118 norm[i] = String.format("%.0f", 1000.0 * me / base); 119 } 120 121 PrintWriter pw = new PrintWriter(args[0]); 122 // Horizontal avoids issues with long names overlapping each other, 123 // and setting a smaller-than-default height makes it easier to overlook and compare. 124 pw.println(""" 125 ```mermaid 126 --- 127 config: 128 xyChart: 129 chartOrientation: horizontal 130 height: 300 131 --- 132 xychart-beta 133 x-axis "variant" [$names] 134 y-axis "Elapsed time (ms, smaller is better)" 0 --> $maxtime 135 bar [$geomeans] 136 ``` 137 138 -----------------Normalized--------------------------------------------- 139 ```mermaid 140 --- 141 config: 142 xyChart: 143 chartOrientation: horizontal 144 height: 300 145 --- 146 xychart-beta 147 x-axis "variant" [$names] 148 y-axis "Elapsed time (normalized, smaller is better)" 0 --> 1000 149 bar [$norms] 150 ``` 151 """ 152 .replace("$names", '"' + String.join("\", \"", Arrays.copyOfRange(head, 1, head.length)) + '"') 153 .replace("$maxtime", geomeans[1]) 154 .replace("$geomeans", String.join(", ", Arrays.copyOfRange(geomeans, 1, geomeans.length))) 155 .replace("$norms", String.join(", ", Arrays.copyOfRange(norm, 1, norm.length))) 156 ); 157 pw.close(); 158 } 159 160 161 static String geomean(ArrayList<Double> list) { 162 double log = 0.0d; 163 for (Double d : list) { 164 double v = d.doubleValue(); 165 if (v <= 0) { 166 v = 0.000001; 167 } 168 log += Math.log(v); 169 } 170 171 return String.format("%.2f", Math.exp(log / list.size())); 172 } 173 174 static String stdev(ArrayList<Double> list) { 175 double sum = 0.0; 176 for (Double d : list) { 177 sum += d.doubleValue(); 178 } 179 180 double length = list.size(); 181 double mean = sum / length; 182 183 double stdev = 0.0; 184 for (Double d : list) { 185 stdev += Math.pow(d.doubleValue() - mean, 2); 186 } 187 188 return String.format("%.2f", Math.sqrt(stdev / length)); 189 } 190 }