@@ -13,14 +13,23 @@ def lrun(np):
1313
1414# Parallel options use 4 ranks per node since that is how many GPUs there are.
1515runs = {
16- "build-rzansel-blueos_3_ppc64le_ib_p9-clang@10.0.1.2_cuda-release" : {"policies" :["seq" , "omp" , "cuda" ], "launch" :lrun },
17- "build-rzwhippet-toss_4_x86_64_ib-clang@14.0.6-release" : {"policies" :["seq" , "omp" ], "launch" :srun },
18- "build-rzwhippet-toss_4_x86_64_ib-gcc@10.3.1-release" : {"policies" :["seq" , "omp" ], "launch" :srun },
19- "build-rzwhippet-toss_4_x86_64_ib-intel@2022.1.0-release" : {"policies" :["seq" , "omp" ], "launch" :srun },
20- "build-rzvernal-toss_4_x86_64_ib_cray-rocmcc@6.2.1_hip-release" : {"policies" :["seq" , "hip" ], "launch" :srun },
21- "build-rzvernal-toss_4_x86_64_ib_cray-clang@17.0.0_hip-release" : {"policies" :["seq" , "hip" ], "launch" :srun },
22- "build-rzadams-toss_4_x86_64_ib_cray-cce@18.0.0_hip-release" :{"policies" :["seq" , "hip" ], "launch" :flux_run },
23- "build-rzadams-toss_4_x86_64_ib_cray-rocmcc@6.2.1_hip-release" :{"policies" :["seq" , "hip" ], "launch" :flux_run }
16+ "build-dane-toss_4_x86_64_ib-gcc@13.3.1-release" : {"policies" :["seq" , "omp" ], "launch" :srun },
17+ "build-dane-toss_4_x86_64_ib-intel-oneapi-compilers@2025.2.0-release" : {"policies" :["seq" , "omp" ], "launch" :srun },
18+ "build-dane-toss_4_x86_64_ib-llvm@19.1.3-release" : {"policies" :["seq" , "omp" ], "launch" :srun },
19+ "build-matrix-toss_4_x86_64_ib-gcc@13.3.1_cuda-release" : {"policies" :["seq" , "omp" , "cuda" ], "launch" :srun },
20+ "build-matrix-toss_4_x86_64_ib-llvm@19.1.3_cuda-release" : {"policies" :["seq" , "omp" , "cuda" ], "launch" :srun },
21+ "build-rzwhippet-toss_4_x86_64_ib-llvm@19.1.3-release" : {"policies" :["seq" , "omp" ], "launch" :srun },
22+ "build-rzwhippet-toss_4_x86_64_ib-gcc@13.3.1-release" : {"policies" :["seq" , "omp" ], "launch" :srun },
23+ "build-rzwhippet-toss_4_x86_64_ib-intel-oneapi-compilers@2025.2.0-release" : {"policies" :["seq" , "omp" ], "launch" :srun },
24+ "build-rzvernal-toss_4_x86_64_ib_cray-cce@20.0.0_hip-release" : {"policies" :["seq" , "hip" ], "launch" :srun },
25+ "build-rzvernal-toss_4_x86_64_ib_cray-llvm-amdgpu@6.3.1_hip-release" : {"policies" :["seq" , "hip" ], "launch" :srun },
26+ "build-rzvernal-toss_4_x86_64_ib_cray-llvm-amdgpu@6.4.2_hip-release" : {"policies" :["seq" , "hip" ], "launch" :srun },
27+ "build-rzadams-toss_4_x86_64_ib_cray-cce@20.0.0_hip-release" :{"policies" :["seq" , "hip" ], "launch" :flux_run },
28+ "build-rzadams-toss_4_x86_64_ib_cray-llvm-amdgpu@6.3.1_hip-release" :{"policies" :["seq" , "hip" ], "launch" :flux_run },
29+ "build-rzadams-toss_4_x86_64_ib_cray-llvm-amdgpu@6.4.2_hip-release" :{"policies" :["seq" , "hip" ], "launch" :flux_run },
30+ "build-tioga-toss_4_x86_64_ib_cray-cce@20.0.0_hip-release" :{"policies" :["seq" , "hip" ], "launch" :flux_run },
31+ "build-tioga-toss_4_x86_64_ib_cray-llvm-amdgpu@6.3.1_hip-release" :{"policies" :["seq" , "hip" ], "launch" :flux_run },
32+ "build-tioga-toss_4_x86_64_ib_cray-llvm-amdgpu@6.4.2_hip-release" :{"policies" :["seq" , "hip" ], "launch" :flux_run }
2433}
2534
2635def generate (params ):
@@ -40,6 +49,10 @@ def generate(params):
4049 f .write ("CONCENTRIC_CIRCLES=./examples/mir_concentric_circles\n " )
4150 f .write ("CONCENTRIC_CIRCLES_MPI=./examples/mir_concentric_circles_mpi\n \n " )
4251
52+ f .write ("export OMP_PLACES=cores\n " )
53+ f .write ("export OMP_PROC_BIND=spread\n " )
54+ f .write ("export OMP_DYNAMIC=FALSE\n " )
55+
4356 dimension = params ["dimension" ]
4457 trials = params ["trials" ]
4558 for s in params ["sizes" ]:
@@ -68,7 +81,10 @@ def read_timings(filename, searchKey):
6881 retval = "" # no data
6982 try :
7083 lines = open (filename , "rt" ).readlines ()
71- print (f"Reading { filename } " )
84+ msg = ""
85+ if searchKey .find ("Algorithm" ) == - 1 :
86+ msg = f" Getting '{ searchKey } ' value."
87+ print (f"Reading { filename } .{ msg } " )
7288 for line in lines :
7389 pos = line .find (searchKey )
7490 if pos != - 1 :
@@ -113,10 +129,7 @@ def average(value, trials):
113129 avg = value
114130 return avg
115131
116- # Measure just the MIR algorithm
117- searchKey = "EquizAlgorithm"
118- if params ["method" ] == "elvira" :
119- searchKey = "ElviraAlgorithm"
132+ searchKey = params ["searchKey" ]
120133
121134 columns = []
122135 # Add NumZones column (either square or cube of s, depending on dimension)
@@ -184,9 +197,17 @@ def make_csv(params, outputfile):
184197 f .write (f"{ line } \n " )
185198 f .close ()
186199
187- def seriesName (name ):
200+ def seriesName (name , includeCompiler = False ):
188201 policies = {"SEQ" : "Serial" , "OMP" : "OpenMP" , "CUDA" : "CUDA" , "HIP" : "HIP" }
189- newName = name [:name .find ("-" )]
202+ if includeCompiler :
203+ d0 = name .find ("-" )
204+ d1 = name .rfind ("-" )
205+ s0 = name .rfind (" " )
206+ hostName = name [:d0 ]
207+ compName = name [d1 + 1 :s0 ]
208+ newName = f"{ hostName } { compName } "
209+ else :
210+ newName = name [:name .find ("-" )]
190211 for p in policies .keys ():
191212 if name [- len (p ) - 1 :] == " " + p :
192213 newName = newName + " " + policies [p ]
@@ -197,8 +218,15 @@ def lineProps(name):
197218 """
198219 Make line properties when given a series name.
199220 """
200- hostColor = {"rzansel" : "g" , "rzwhippet" : "r" , "rzvernal" : "o" , "rzadams" : "b" }
201- policyStyle = {"seq" : "--" , "omp" : ":" , "cuda" : "-" , "hip" : "-" }
221+ red = "#ff0000"
222+ green = "#00aa00"
223+ blue = "#0000ff"
224+ orange = "#ff6622"
225+ #magenta = "#ff00ff"
226+ #purple = "#8811aa"
227+ hostColor = {"rzwhippet" : red , "rzvernal" : green , "rzadams" : blue ,
228+ "dane" : red , "matrix" : green , "tioga" : orange , "tuolumne" : blue }
229+ policyStyle = {"seq" : "-" , "omp" : ":" , "cuda" : "--" , "hip" : "--" }
202230 policyMark = {"seq" : "o" , "omp" : "s" , "cuda" : "^" , "hip" : "^" }
203231 color = "b"
204232 style = "-"
@@ -214,6 +242,25 @@ def lineProps(name):
214242 break
215243 return color , style , mark
216244
245+ def hex_to_rgb (hex_color : str ):
246+ """
247+ Convert a color in "#rrggbb" (or "rrggbb") format to an (r, g, b) tuple.
248+ """
249+ s = hex_color .strip ()
250+ if s .startswith ("#" ):
251+ s = s [1 :]
252+ if len (s ) != 6 :
253+ raise ValueError (f"Expected 6 hex digits, got { len (s )} : { hex_color !r} " )
254+
255+ try :
256+ r = int (s [0 :2 ], 16 )
257+ g = int (s [2 :4 ], 16 )
258+ b = int (s [4 :6 ], 16 )
259+ except ValueError as e :
260+ raise ValueError (f"Invalid hex color: { hex_color !r} " ) from e
261+
262+ return (r , g , b )
263+
217264def plot (params ):
218265 """
219266 Read the available timing files and plot them.
@@ -233,22 +280,78 @@ def make_series(col1, col2):
233280 break
234281 return x ,y
235282
283+ def rgbToColor (r ,g ,b ):
284+ return "#%02x%02x%02x" % (r ,g ,b )
285+
286+ def lightenColor (r ,g ,b ):
287+ def clamp255 (value ):
288+ if value > 255 :
289+ return 255
290+ return int (value )
291+ scale = 1.3
292+ return rgbToColor (clamp255 (r * scale ), clamp255 (g * scale ), clamp255 (b * scale ))
293+
294+ def darkenColor (r ,g ,b ):
295+ scale = 0.7
296+ return rgbToColor (int (r * scale ), int (g * scale ), int (b * scale ))
297+
298+ def modifyColor (color , index ):
299+ if index == 0 :
300+ return color
301+ r ,g ,b = hex_to_rgb (color )
302+ if index == 1 :
303+ return darkenColor (r ,g ,b )
304+ return lightenColor (r ,g ,b )
305+
306+ def valid_column (column ):
307+ valid = False
308+ for i in range (1 , len (column )):
309+ if column [i ] != "" :
310+ valid = True
311+ return valid
312+
313+ # Make columns from the data
236314 columns = make_columns (params )
237315
316+ # Count how many plots would use the normal legend name
317+ counts = {}
318+ for c in range (1 , len (columns )):
319+ if not valid_column (columns [c ]):
320+ continue
321+ sn = seriesName (columns [c ][0 ])
322+ if sn in counts :
323+ count , _ = counts [sn ]
324+ counts [sn ] = (count + 1 , 0 )
325+ else :
326+ counts [sn ] = (1 , 0 )
327+
238328 import matplotlib .pyplot as plt
329+ seriesLegendNames = {}
239330 for c in range (1 , len (columns )):
240331 x , y = make_series (columns [0 ][1 :], columns [c ][1 :])
241332 if len (x ) > 0 :
242333 color , style , mark = lineProps (columns [c ][0 ])
243- plt .plot (x , y , marker = mark , linestyle = style , color = color , linewidth = 2. , label = seriesName (columns [c ][0 ]))
334+ sn = seriesName (columns [c ][0 ])
335+ if counts [sn ][0 ] > 1 :
336+ oldsn = sn
337+ sn = seriesName (columns [c ][0 ], True )
338+ color = modifyColor (color , counts [oldsn ][1 ])
339+ counts [oldsn ] = (counts [oldsn ][0 ], counts [oldsn ][1 ] + 1 )
340+ seriesLegendNames [c ] = sn
341+ plt .plot (x , y , marker = mark , linestyle = style , color = color , linewidth = 2. , label = sn )
244342
245343 dimension = params ["dimension" ]
246344 method = params ["method" ]
345+ searchKey = params ["searchKey" ]
346+ if searchKey .find ("Algorithm" ) != - 1 :
347+ # more concise
348+ searchKey = method
349+ doLabels = params ["labels" ]
247350
248351 # Add labels and title
249352 plt .xlabel ('Number of Zones' , fontsize = 24 )
250353 plt .ylabel ('Time (s)' , fontsize = 24 )
251- plt .title (f'{ dimension } D MIR Timings ({ method } )' , fontsize = 28 )
354+ plt .title (f'{ dimension } D MIR Timings ({ searchKey } )' , fontsize = 28 )
252355 xlabels = make_series (columns [0 ][1 :], columns [0 ][1 :])[0 ]
253356 plt .xticks (ticks = xlabels , labels = xlabels , fontsize = 18 )
254357 plt .yticks (fontsize = 18 )
@@ -268,6 +371,65 @@ def make_series(col1, col2):
268371 print (f"There was an error, probably because the benchmark is still running. { columns } " )
269372 raise
270373
374+ # Add labels
375+ if doLabels and len (columns ) >= 2 :
376+ def findIndex (seq , value ):
377+ eps = 1.e-8
378+ for i in range (len (seq )):
379+ if math .fabs (value - seq [i ]) < eps :
380+ return i
381+ return - 1
382+
383+ # Group the related series.
384+ seriesGroups = {}
385+ seriesSer = {}
386+ for c in seriesLegendNames .keys ():
387+ sn = seriesLegendNames [c ]
388+ host = sn [:sn .rfind (" " )]
389+ if host in seriesGroups :
390+ seriesGroups [host ].append (c )
391+ else :
392+ seriesGroups [host ] = [c ]
393+ if sn .find ("Serial" ) != - 1 :
394+ seriesSer [host ] = c
395+
396+ # Go through groups
397+ for host in seriesGroups :
398+ if not host in seriesSer :
399+ continue
400+
401+ # The serial column
402+ cser = seriesSer [host ]
403+
404+ # Compare other columns against the serial column
405+ for c in seriesGroups [host ]:
406+ if cser == c :
407+ continue
408+
409+ x1 , y1 = make_series (columns [0 ][1 :], columns [cser ][1 :])
410+ x2 , y2 = make_series (columns [0 ][1 :], columns [c ][1 :])
411+ xa = []
412+ ya = []
413+ labels = []
414+ idx = 0
415+ for i in range (len (x1 )):
416+ # Where series 1 (SEQ) > series 2 (OMP)
417+ j = findIndex (x2 , x1 [i ])
418+ if j != - 1 and y1 [i ] > y2 [j ]:
419+ xa .append (x2 [j ])
420+ ya .append (y2 [j ])
421+ labels .append (f"{ math .trunc (100. * y1 [i ] / y2 [i ]) / 100. :.2f} x" )
422+ plt .annotate (
423+ labels [idx ],
424+ (xa [idx ], ya [idx ]),
425+ xytext = (6 , - 6 ), textcoords = "offset points" , # pixel offset
426+ ha = "left" , va = "bottom" ,
427+ fontsize = 10 ,
428+ bbox = dict (boxstyle = "round,pad=0.2" , fc = "white" , ec = "none" , alpha = 0.8 ),
429+ arrowprops = dict (arrowstyle = "-" , color = "0.3" , lw = 0.8 )
430+ )
431+ idx = idx + 1
432+
271433 # Show the plot
272434 plt .grid (True )
273435 plt .show ()
@@ -319,13 +481,27 @@ def get_params():
319481 required = False
320482 )
321483
484+ parser .add_argument (
485+ "--key" ,
486+ type = str ,
487+ help = "Name of the caliper timings to extract from the logs" ,
488+ required = False
489+ )
490+
322491 parser .add_argument (
323492 "--trials" ,
324493 type = int ,
325494 help = "Number of times to run MIR" ,
326495 required = False
327496 )
328497
498+ parser .add_argument (
499+ "--labels" ,
500+ action = "store_true" ,
501+ help = "Boolean flag to indicate whether to draw speedup labels" ,
502+ required = False
503+ )
504+
329505 args = parser .parse_args ()
330506
331507 # Convert the comma-separated string into a tuple of integers
@@ -369,6 +545,20 @@ def get_params():
369545 else :
370546 params ["trials" ] = 1
371547
548+ if args .labels is not None :
549+ params ["labels" ] = args .labels
550+ else :
551+ params ["labels" ] = False
552+
553+ if args .key is not None :
554+ params ["searchKey" ] = args .key
555+ else :
556+ # Default to MIR algorithm timings
557+ if params ["method" ] == "elvira" :
558+ params ["searchKey" ] = "ElviraAlgorithm"
559+ else :
560+ params ["searchKey" ] = "EquizAlgorithm"
561+
372562 # Generate some sizes.
373563 sides = (50 , 100 , 200 , 500 , 1000 , 1500 , 2000 , 4000 , 8000 )
374564 if params ["dimension" ] == 3 :
0 commit comments