A fast, safe, and efficient CBOR serialization library for Swift on any platform. swiftpackageindex.com/thecoolwinter/CBOR/1.1.1/documentation/cbor
atproto swift cbor
at dag-cbor 164 lines 6.3 kB view raw
1#!/usr/bin/env python3 2""" 3Parse Swift Benchmark markdown (produced by `swift package benchmark ... --format markdown`) and produce compact summary 4markdown tables for Decoding and Encoding using the Time (total CPU) p50 values. 5 6Usage: 7 - Read from stdin: 8 swift package benchmark baseline compare swiftcbor --format markdown --no-progress | python3 bench_compare.py 9 - Or read from file: 10 python3 bench_compare.py benchmark.md 11 12The script prints two markdown tables (Decoding and Encoding) to stdout. 13""" 14 15import sys 16import re 17from pathlib import Path 18 19 20def parse_markdown(text): 21 lines = text.splitlines() 22 results = {"Decoding": {}, "Encoding": {}} 23 mode = None 24 i = 0 25 while i < len(lines): 26 line = lines[i] 27 if line.strip().startswith('## Decoding'): 28 mode = 'Decoding' 29 i += 1 30 continue 31 if line.strip().startswith('## Encoding'): 32 mode = 'Encoding' 33 i += 1 34 continue 35 36 m = re.match(r"###\s+(.+?)\s+metrics", line) 37 if m and mode: 38 bench = m.group(1).strip() 39 # look ahead for Time (total CPU) table 40 j = i + 1 41 while j < len(lines) and not lines[j].strip().startswith('###') and not lines[j].strip().startswith('## '): 42 lh = lines[j] 43 # find the section header line containing "Time (total CPU)" 44 m2 = re.search(r"Time\s*\(total CPU\)\s*(?:\(([^)]+)\))?", lh) 45 if m2: 46 unit = m2.group(1) if m2.group(1) else '' 47 # find the header row that contains p0/p25/p50 etc. 48 k = j + 1 49 while k < len(lines) and lines[k].strip() == '': 50 k += 1 51 header_line_index = None 52 p50_idx = None 53 for t in range(k, min(k + 60, len(lines))): 54 if 'p50' in lines[t]: 55 cols = [c.strip() for c in lines[t].split('|')][1:-1] 56 # locate p50 column 57 try: 58 p50_idx = next(idx for idx, c in enumerate(cols) if c.startswith('p50')) 59 except StopIteration: 60 p50_idx = None 61 header_line_index = t 62 break 63 swift_val = None 64 curr_val = None 65 if header_line_index is not None and p50_idx is not None: 66 # parse following rows to find swiftcbor and Current_run 67 for t in range(header_line_index + 1, header_line_index + 60): 68 if t >= len(lines): 69 break 70 row = lines[t] 71 if not row.strip().startswith('|'): 72 continue 73 cols = [c.strip() for c in row.split('|')][1:-1] 74 if not cols: 75 continue 76 name = cols[0] 77 # defensive check 78 if p50_idx < len(cols): 79 if 'swiftcbor' in name: 80 swift_val = cols[p50_idx] 81 if 'Current_run' in name: 82 curr_val = cols[p50_idx] 83 if swift_val and curr_val: 84 break 85 results[mode][bench] = (swift_val, curr_val, unit) 86 break 87 j += 1 88 i += 1 89 return results 90 91 92def clean_num(s): 93 if s is None: 94 return None 95 s = s.strip().replace(',', '') 96 # find first numeric token 97 m = re.search(r"([0-9]+(?:\.[0-9]+)?)", s) 98 if not m: 99 return None 100 try: 101 return float(m.group(1)) 102 except: 103 return None 104 105 106def fmt(n): 107 if n is None: 108 return '' 109 if n >= 1000: 110 return f"{int(round(n)):,}" 111 if n == int(n): 112 return str(int(n)) 113 return f"{n:.0f}" 114 115 116def render_table_section(title, rows, preferred_order=None): 117 print(f"### {title} (cpu time)\n") 118 print("| Benchmark | SwiftCBOR (p50) | CBOR (p50) | % Improvement |") 119 print("|---|---:|---:|---:|") 120 keys = [] 121 if preferred_order: 122 for k in preferred_order: 123 if k in rows: 124 keys.append(k) 125 # append remaining in alphabetical order 126 for k in sorted(rows.keys()): 127 if k not in keys: 128 keys.append(k) 129 for b in keys: 130 s_p, c_p, unit = rows.get(b, (None, None, '')) 131 sval = clean_num(s_p) 132 cval = clean_num(c_p) 133 s_str = (fmt(sval) + (' ' + unit if unit else '')) if sval is not None else (s_p or '') 134 c_str = (fmt(cval) + (' ' + unit if unit else '')) if cval is not None else (c_p or '') 135 perc = '' 136 if sval is not None and cval is not None and sval != 0: 137 pct = round((sval - cval) / sval * 100) 138 perc = f"**{pct}%**" 139 print(f"| {b} | {s_str} | {c_str} | {perc} |") 140 print("\n") 141 142 143def main(argv): 144 parser = __import__('argparse').ArgumentParser(description='Parse Swift benchmark markdown and print compact p50 tables') 145 parser.add_argument('file', nargs='?', default='-', help='Path to markdown file, or - for stdin (default)') 146 args = parser.parse_args(argv) 147 if args.file == '-': 148 text = sys.stdin.read() 149 else: 150 p = Path(args.file) 151 text = p.read_text() 152 153 results = parse_markdown(text) 154 155 # preferred orders to match your example (best-effort) 156 dec_order = ["Array","Complex Object","Date","Dictionary","Double","Float","Indeterminate String","Int","Int Small","Simple Object","String","String Small"] 157 enc_order = ["Array","Array Small","Bool","Complex Codable Object","Data","Data Small","Dictionary","Dictionary Small","Int","Int Small","Simple Codable Object","String","String Small"] 158 159 render_table_section('Decoding', results.get('Decoding', {}), preferred_order=dec_order) 160 render_table_section('Encoding', results.get('Encoding', {}), preferred_order=enc_order) 161 162 163if __name__ == '__main__': 164 main(sys.argv[1:])