yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Gangzheng TongFix benchmark/compile.py nested f-string and deprecated constant issues (#6773)6dd5bd52e

master
6.9 KiB246 linesraw
1import os
2import shutil
3import glob
4import subprocess
5import argparse
6import sys
7import prettytable
8import json
9
10### Setup ###
11
12def clear_mkdir(dir):
13    if os.path.exists(dir):
14        shutil.rmtree(dir)
15    os.makedirs(dir, exist_ok=True)
16
17clear_mkdir('modules')
18clear_mkdir('targets')
19clear_mkdir('targets/generated')
20
21target_choices = [
22    'spirv',         # SPIRV directly
23    'spirv-glsl',    # SPIRV through synthesized GLSL
24    'dxil',          # DXIL with HLSL and DXC
25    'dxil-embedded'  # DXIL with precompiled modules
26]
27
28parser = argparse.ArgumentParser()
29parser.add_argument('--target', type=str, default='spirv', choices=target_choices)
30parser.add_argument('--samples', type=int, default=1)
31parser.add_argument('--output', type=str, default='benchmarks.json')
32
33args = parser.parse_args(sys.argv[1:])
34
35slangc = '..\\..\\build\\Release\\bin\\slangc.exe'
36target = args.target
37samples = args.samples
38
39if target == 'spirv':
40    target = 'spirv -emit-spirv-directly'
41    target_ext = 'spirv'
42    embed = False
43elif target == 'spirv-glsl':
44    target = 'spirv -emit-spirv-via-glsl'
45    target_ext = 'spirv'
46    embed = False
47elif target == 'dxil-embedded':
48    target_ext = 'dxil'
49    embed = True
50elif target == 'dxil':
51    target_ext = 'dxil'
52    embed = False
53
54print(f'slangc:  {slangc}')
55print(f'target:  {target}')
56print(f'samples: {samples}\n')
57
58### Utility ###
59
60def parse(results):
61    results = results.split('\n')
62    results = [ r for r in results if r.startswith('[*]') ]
63    results = [ r.split() for r in results ]
64    profile = {}
65    for r in results:
66        profile[r[1]] = float(r[-1][:-2])
67    return profile
68
69timings = {}
70def run(command, key):
71    profile = {}
72    for i in range(samples):
73        try:
74            results = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True).decode('utf-8')
75        except subprocess.CalledProcessError as exc:
76            print(f"[Error] Failed to run command: {command}")
77            print(exc.output.decode('utf-8'))
78            return  # Return without adding to timings
79
80        p = parse(results)
81        if len(profile) == 0:
82            profile = p
83        else:
84            for k, v in p.items():
85                profile.setdefault(k, 0)
86                profile[k] += v
87
88    # Only add to timings if we have data
89    if profile:
90        for k in profile:
91            profile[k] /= samples
92        timings[key] = profile
93    else:
94        print(f"[Warning] No timing data collected for {key}")
95
96def compile_cmd(file, output, stage=None, entry=None, emit=False):
97    cmd = f'{slangc} -report-perf-benchmark {file}'
98
99    if stage:
100        cmd += f' -stage {stage}'
101        if entry:
102            cmd += f' -entry {entry}'
103        else:
104            cmd += f' -entry {stage}'
105
106    if emit:
107        cmd += f' -target {target_ext}'
108        output += '.' + target_ext
109        if target == 'dxil-embedded':
110            cmd += ' -profile lib_6_6'
111    elif embed:
112        cmd += ' -embed-dxil'
113
114    cmd += f' -o {output}'
115
116    return cmd
117
118### Monolithic compilation ###
119
120hit = 'hit.slang'
121
122cmd = compile_cmd(hit, f'targets/dxr-ch-mono', stage='closesthit', entry='MdlRadianceClosestHitProgram', emit=True)
123run(cmd, f'full/{target_ext}/mono/closesthit')
124print(f'[I] compiled shadow (monolithic)')
125
126cmd = compile_cmd(hit, f'targets/dxr-ah-mono', stage='anyhit', entry='MdlRadianceAnyHitProgram', emit=True)
127run(cmd, f'full/{target_ext}/mono/anyhit')
128print(f'[I] compiled shadow (monolithic)')
129
130cmd = compile_cmd(hit, f'targets/dxr-sh-mono', stage='anyhit', entry='MdlShadowAnyHitProgram', emit=True)
131run(cmd, f'full/{target_ext}/mono/shadow')
132print(f'[I] compiled shadow (monolithic)')
133
134### Module precompilation ###
135
136modules = []
137
138for file in glob.glob(f'*.slang'):
139    if not file.endswith('hit.slang'):
140        basename = os.path.basename(file)
141        run(compile_cmd(file, f'modules/{basename}-module'), 'module/' + file)
142        print(f'[I] compiled {file}.')
143
144### Module whole compilation ###
145
146cmd = compile_cmd(hit, f'targets/dxr-ch-modules', stage='closesthit', entry='MdlRadianceClosestHitProgram', emit=True)
147run(cmd, f'full/{target_ext}/module/closesthit')
148print(f'[I] compiled closesthit (module)')
149
150cmd = compile_cmd(hit, f'targets/dxr-ah-modules', stage='anyhit', entry='MdlRadianceAnyHitProgram', emit=True)
151run(cmd, f'full/{target_ext}/module/anyhit')
152print(f'[I] compiled anyhit (module)')
153
154cmd = compile_cmd(hit, f'targets/dxr-sh-modules', stage='anyhit', entry='MdlShadowAnyHitProgram', emit=True)
155run(cmd, f'full/{target_ext}/module/shadow')
156print(f'[I] compiled shadow (module)')
157
158# Module precompilation time
159precompilation_time = 0
160for k in timings:
161    if k.startswith('module'):
162        precompilation_time += timings[k]['compileInner']
163
164timings[f'full/{target_ext}/precompilation'] = { 'compileInner': precompilation_time }
165
166# Output to benchmark file
167json_data = []
168for k, v in timings.items():
169    if not k.startswith('full'):
170        continue
171
172    name = k.split('/')[1:]
173    name = ' : '.join(reversed(name))
174
175    data = {
176        'name': name,
177        'value': v['compileInner'],
178        'unit': 'milliseconds'
179    }
180
181    json_data.append(data)
182
183# TODO: append target to benchmark file name
184with open(args.output, 'w') as file:
185    json.dump(json_data, file, indent=4)
186
187# Generate readable Markdown as well
188print(4 * '\n')
189print('# Slang MDL benchmark results\n')
190print('## Module precompilation time\n')
191precomp_key = f'full/{target_ext}/precompilation'
192if precomp_key in timings:
193    print(f'Total: **{timings[precomp_key]["compileInner"]} ms**\n')
194else:
195    print("No precompilation data available\n")
196
197print('## Module compilation for entry points\n')
198
199entries = [ 'Closest Hit', 'Any Hit', 'Shadow' ]
200prefixes = [ 'closesthit', 'anyhit', 'shadow' ]
201
202table = prettytable.PrettyTable()
203table.set_style(prettytable.MARKDOWN)
204table.field_names = [ 'Entry', 'Total' ]
205
206total = 0
207for entry, prefix in zip(entries, prefixes):
208    row = [ entry ]
209    key = f'full/{target_ext}/module/{prefix}'
210    if key in timings:
211        db = timings[key]
212        spCompile = db.get('compileInner', 0)
213        row.append(f'{spCompile:.3f}ms')
214        table.add_row(row)
215        total += spCompile
216    else:
217        row.append('Failed')
218        table.add_row(row)
219        print(f"[Warning] Compilation failed for module/{prefix}")
220
221print(f'Total: **{total} ms**\n')
222print(table, end='\n\n')
223
224print('## Monolithic compilation for entry points\n')
225
226table = prettytable.PrettyTable()
227table.set_style(prettytable.MARKDOWN)
228table.field_names = [ 'Entry', 'Total' ]
229
230total = 0
231for entry, prefix in zip(entries, prefixes):
232    row = [ entry ]
233    key = f'full/{target_ext}/mono/{prefix}'
234    if key in timings:
235        db = timings[key]
236        spCompile = db.get('compileInner', 0)
237        row.append(f'{spCompile:.3f}ms')
238        table.add_row(row)
239        total += spCompile
240    else:
241        row.append('Failed')
242        table.add_row(row)
243        print(f"[Warning] Compilation failed for mono/{prefix}")
244
245print(f'Total: **{total} ms**\n')
246print(table, end='\n\n')