Python · 完整文件
plot_release.py
从保存的测量结果生成性能图表,不重新执行性能测试。
benchmarks/spsc/plot_release.py
1"""Plot saved matched-parameter write costs with ReportLab; never select trials or rerun."""2import argparse3import json4from pathlib import Path5import xml.etree.ElementTree as ET67import reportlab8from reportlab.graphics.charts.barcharts import VerticalBarChart9from reportlab.graphics.shapes import Drawing, Rect, String10from reportlab.graphics import renderSVG11from reportlab.lib.colors import HexColor1213ROOT = Path(__file__).resolve().parents[2]14parser = argparse.ArgumentParser(description=__doc__)15parser.add_argument('--data', type=Path, default=ROOT/'docs/performance_data/release_20261011_rigtorp_fixed')16parser.add_argument('--output', type=Path, default=ROOT/'docs/assets/write_cost_8m.svg')17args = parser.parse_args()18series = []19repetitions = None20for mode,label,color in [('normal','Ordinary write','#3864d9'),('session','WriteSession','#087f73')]:21 result = json.loads((args.data/f'8m_{mode}/results.json').read_text(encoding='utf-8'))22 assert result['profile']['cache_mib'] == 823 rows = sorted((g for g in result['groups'] if g['transport']=='two_buf_queue'),key=lambda g:g['payload_bytes'])24 assert [g['payload_bytes'] for g in rows] == [8,16,32,64]25 runs = result['profile']['repetitions']26 if repetitions is None: repetitions = runs27 assert repetitions == runs28 assert all(g['callback_iterations']==0 and len(g['p50_runs_ns'])==runs for g in rows)29 series.append(dict(mode=mode,label=label,color=color,rows=rows))30for a,b in zip(series[0]['rows'],series[1]['rows']):31 pa,pb=dict(a['parameters']),dict(b['parameters'])32 pa.pop('use_write_session');pb.pop('use_write_session')33 assert pa==pb3435drawing=Drawing(820,370)36drawing.add(Rect(0,0,820,370,fillColor=HexColor('#f7f9fc'),strokeColor=None))37drawing.add(String(30,341,'TwoBufQueue: ordinary write vs WriteSession',fontName='Helvetica-Bold',fontSize=18,fillColor=HexColor('#172b4d')))38drawing.add(String(30,321,f'p50 of 1,000-write group averages | mean of {repetitions} measured runs',fontName='Helvetica',fontSize=11,fillColor=HexColor('#475569')))39chart=VerticalBarChart()40chart.x=80;chart.y=63;chart.width=670;chart.height=22441chart.data=[[g['p50_ns'] for g in s['rows']] for s in series]42chart.categoryAxis.categoryNames=['8 B','16 B','32 B','64 B']43chart.categoryAxis.labels.fontName='Helvetica';chart.categoryAxis.labels.fontSize=1144chart.categoryAxis.strokeColor=HexColor('#94a3b8')45chart.valueAxis.valueMin=0;chart.valueAxis.valueMax=3;chart.valueAxis.valueStep=.546chart.valueAxis.labels.fontName='Helvetica';chart.valueAxis.labels.fontSize=1047chart.valueAxis.visibleGrid=True;chart.valueAxis.gridStrokeColor=HexColor('#dce3ed')48chart.valueAxis.strokeColor=HexColor('#94a3b8')49chart.barWidth=16;chart.barSpacing=5;chart.groupSpacing=2050chart.barLabelFormat='%.3f';chart.barLabels.nudge=751chart.barLabels.fontName='Helvetica';chart.barLabels.fontSize=1052for i,s in enumerate(series):53 chart.bars[i].fillColor=HexColor(s['color']);chart.bars[i].strokeColor=None54 x=478+i*15655 drawing.add(Rect(x,298,12,8,fillColor=HexColor(s['color']),strokeColor=None))56 drawing.add(String(x+18,297,s['label'],fontName='Helvetica',fontSize=10,fillColor=HexColor('#172b4d')))57drawing.add(String(44,298,'ns / write',fontName='Helvetica',fontSize=11,fillColor=HexColor('#475569')))58drawing.add(chart)59drawing.add(String(410,25,'Apple M3 | 8 MiB per layer | callback loops 0 | high QoS, no hard affinity',textAnchor='middle',fontName='Helvetica',fontSize=10,fillColor=HexColor('#475569')))60args.output.parent.mkdir(parents=True,exist_ok=True)61renderSVG.drawToFile(drawing,str(args.output))62namespace='http://www.w3.org/2000/svg'63ET.register_namespace('',namespace)64svg=ET.parse(args.output);root=svg.getroot()65title=ET.Element('{'+namespace+'}title');title.text='TwoBufQueue ordinary write and WriteSession group-average p50'66desc=ET.Element('{'+namespace+'}desc');desc.text=f'Matched parameters, Apple M3. Each bar is the mean of {repetitions} runs; quantiles of 1000-write averages, not single-call or end-to-end latency.'67root.insert(0,title);root.insert(1,desc);svg.write(args.output,encoding='utf-8',xml_declaration=True)68data={'metric':f'p50 of 1000-write group averages, {repetitions}-run arithmetic mean','repetitions':repetitions,'data_directory':args.data.name,'reportlab_version':reportlab.Version,'series':[{'mode':s['mode'],'points':[{'payload_bytes':g['payload_bytes'],'p50_ns':g['p50_ns'],'p50_runs_ns':g['p50_runs_ns']} for g in s['rows']]} for s in series]}69args.output.with_suffix('.json').write_text(json.dumps(data,indent=2)+'\n',encoding='utf-8')70print(args.output)