summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/benchmark.yml47
-rw-r--r--source/core/slang-performance-profiler.cpp9
-rw-r--r--source/core/slang-performance-profiler.h4
-rw-r--r--source/slang/slang-compiler.cpp2
-rw-r--r--source/slang/slang.cpp12
-rw-r--r--tests/mdl/.gitignore4
-rw-r--r--tests/mdl/compile.py246
7 files changed, 319 insertions, 5 deletions
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
new file mode 100644
index 000000000..441334055
--- /dev/null
+++ b/.github/workflows/benchmark.yml
@@ -0,0 +1,47 @@
+name: Benchmark
+
+on:
+ push:
+ branches: [master]
+ paths-ignore:
+ - 'docs/**'
+ - 'LICENCE'
+ - 'CONTRIBUTION.md'
+ - 'README.md'
+ pull_request:
+ branches: [master]
+ paths-ignore:
+ - 'docs/**'
+ - 'LICENCE'
+ - 'CONTRIBUTION.md'
+ - 'README.md'
+
+jobs:
+ build:
+ runs-on: [Windows, benchmark, self-hosted]
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: 'true'
+ fetch-depth: '0'
+ - name: Common setup
+ uses: ./.github/actions/common-setup
+ with:
+ os: windows
+ compiler: cl
+ platform: x86_64
+ config: release
+ build-llvm: true
+ - name: Build Slang
+ run: |
+ cmake --preset default --fresh -DSLANG_SLANG_LLVM_FLAVOR=USE_SYSTEM_LLVM -DCMAKE_COMPILE_WARNING_AS_ERROR=false
+ cmake --workflow --preset release
+ - name: Setup
+ run: |
+ cd tests/mdl
+ pip install prettytable argparse
+ - name: Run benchmark
+ run: |
+ cd tests/mdl
+ Copy-Item -Path C:\slang-benchmarks -Destination . -Recurse
+ python compile.py --samples 16 --target dxil --ci \ No newline at end of file
diff --git a/source/core/slang-performance-profiler.cpp b/source/core/slang-performance-profiler.cpp
index b480e1c8e..f08b4998d 100644
--- a/source/core/slang-performance-profiler.cpp
+++ b/source/core/slang-performance-profiler.cpp
@@ -31,11 +31,14 @@ namespace Slang
}
virtual void getResult(StringBuilder& out) override
{
- for (auto func : data)
+ char buffer[512];
+ for (const auto& func : data)
{
- out << func.key << ": \t";
+ memset(buffer, 0, sizeof(buffer));
+ snprintf(buffer, sizeof(buffer), "[*] %30s", func.key);
+ out << buffer << " \t";
auto milliseconds = std::chrono::duration_cast< std::chrono::milliseconds >(func.value.duration);
- out << func.value.invocationCount << "\t" << milliseconds.count() << "ms\n";
+ out << func.value.invocationCount << " \t" << milliseconds.count() << "ms\n";
}
}
virtual void clear() override
diff --git a/source/core/slang-performance-profiler.h b/source/core/slang-performance-profiler.h
index 71b34d262..9895793c4 100644
--- a/source/core/slang-performance-profiler.h
+++ b/source/core/slang-performance-profiler.h
@@ -68,7 +68,9 @@ private:
List<ProfileInfo> m_profilEntries;
};
-#define SLANG_PROFILE PerformanceProfilerFuncRAIIContext _profileContext(__func__)
+#define SLANG_PROFILE PerformanceProfilerFuncRAIIContext _profileContext(__func__)
+#define SLANG_PROFILE_SECTION(s) PerformanceProfilerFuncRAIIContext _profileContext##s(#s)
+
}
#endif
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index 1ef17df1d..f5b7ff428 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -3,6 +3,7 @@
#include "../core/slang-basic.h"
#include "../core/slang-platform.h"
#include "../core/slang-io.h"
+#include "../core/slang-performance-profiler.h"
#include "../core/slang-string-util.h"
#include "../core/slang-hex-dump-util.h"
#include "../core/slang-riff.h"
@@ -2297,6 +2298,7 @@ namespace Slang
void EndToEndCompileRequest::generateOutput()
{
+ SLANG_PROFILE;
generateOutput(getSpecializedGlobalAndEntryPointsComponentType());
// If we are in command-line mode, we might be expected to actually
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 9ba02ee50..81171fdcc 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -2742,6 +2742,7 @@ static void _outputIncludes(const List<SourceFile*>& sourceFiles, SourceManager*
void FrontEndCompileRequest::parseTranslationUnit(
TranslationUnitRequest* translationUnit)
{
+ SLANG_PROFILE;
if (translationUnit->isChecked)
return;
@@ -2924,6 +2925,7 @@ void FrontEndCompileRequest::checkAllTranslationUnits()
void FrontEndCompileRequest::generateIR()
{
+ SLANG_PROFILE;
SLANG_AST_BUILDER_RAII(getLinkage()->getASTBuilder());
// Our task in this function is to generate IR code
@@ -3021,6 +3023,7 @@ static SourceLanguage inferSourceLanguage(FrontEndCompileRequest* request)
SlangResult FrontEndCompileRequest::executeActionsInner()
{
+ SLANG_PROFILE_SECTION(frontEndExecute);
SLANG_AST_BUILDER_RAII(getLinkage()->getASTBuilder());
for (TranslationUnitRequest* translationUnit : translationUnits)
@@ -3046,7 +3049,11 @@ SlangResult FrontEndCompileRequest::executeActionsInner()
return SLANG_FAIL;
// Perform semantic checking on the whole collection
- checkAllTranslationUnits();
+ {
+ SLANG_PROFILE_SECTION(SemanticChecking);
+ checkAllTranslationUnits();
+ }
+
if (getSink()->getErrorCount() != 0)
return SLANG_FAIL;
@@ -3172,6 +3179,7 @@ void EndToEndCompileRequest::init()
SlangResult EndToEndCompileRequest::executeActionsInner()
{
+ SLANG_PROFILE_SECTION(endToEndActions);
// If no code-generation target was specified, then try to infer one from the source language,
// just to make sure we can do something reasonable when invoked from the command line.
//
@@ -6303,6 +6311,7 @@ SlangResult EndToEndCompileRequest::compile()
if (getOptionSet().getBoolOption(CompilerOptionName::ReportDownstreamTime))
{
getSession()->getCompilerElapsedTime(&totalStartTime, &downstreamStartTime);
+ PerformanceProfiler::getProfiler()->clear();
}
#if !defined(SLANG_DEBUG_INTERNAL_ERROR)
// By default we'd like to catch as many internal errors as possible,
@@ -6317,6 +6326,7 @@ SlangResult EndToEndCompileRequest::compile()
try
{
+ SLANG_PROFILE_SECTION(compileInner);
res = executeActions();
}
catch (const AbortCompilationException& e)
diff --git a/tests/mdl/.gitignore b/tests/mdl/.gitignore
new file mode 100644
index 000000000..1719095d1
--- /dev/null
+++ b/tests/mdl/.gitignore
@@ -0,0 +1,4 @@
+slang-benchmarks
+targets/
+modules/
+*.json \ No newline at end of file
diff --git a/tests/mdl/compile.py b/tests/mdl/compile.py
new file mode 100644
index 000000000..0d995636d
--- /dev/null
+++ b/tests/mdl/compile.py
@@ -0,0 +1,246 @@
+import os
+import shutil
+import glob
+import subprocess
+import argparse
+import halo
+import sys
+import prettytable
+import json
+
+### Setup ###
+
+def clear_mkdir(dir):
+ if os.path.exists(dir):
+ shutil.rmtree(dir)
+ os.makedirs(dir, exist_ok=True)
+
+clear_mkdir('modules')
+clear_mkdir('targets')
+clear_mkdir('targets/generated')
+
+target_choices = ['spirv', 'spirv-glsl', 'dxil', 'dxil-embedded']
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--target', type=str, default='spirv', choices=target_choices)
+parser.add_argument('--samples', type=int, default=1)
+parser.add_argument('--output', type=str, default='benchmarks.json')
+parser.add_argument('--ci', action='store_true')
+
+args = parser.parse_args(sys.argv[1:])
+
+repo = 'slang-benchmarks'
+if args.ci:
+ repo = 'C:\\slang-benchmarks'
+
+if not os.path.exists(repo):
+ repo = 'ssh://git@gitlab-master.nvidia.com:12051/slang/slang-benchmarks.git'
+ command = f'git clone {repo}'
+ subprocess.check_output(command)
+
+dxc = 'dxc.exe'
+slangc = '..\\..\\build\\Release\\bin\\slangc.exe'
+target = args.target
+samples = args.samples
+
+if target == 'spirv':
+ target = 'spirv -emit-spirv-directly'
+ target_ext = 'spirv'
+ embed = False
+elif target == 'spirv-glsl':
+ target = 'spirv -emit-spirv-via-glsl'
+ target_ext = 'spirv'
+ embed = False
+elif target == 'dxil-embedded':
+ target_ext = 'dxil'
+ embed = True
+elif target == 'dxil':
+ target_ext = 'dxil'
+ embed = False
+
+print(f'slangc: {slangc}')
+print(f'target: {target}')
+print(f'samples: {samples}\n')
+
+### Utility ###
+
+def parse(results):
+ results = results.split('\n')
+ results = [ r for r in results if r.startswith('[*]') ]
+ results = [ r.split() for r in results ]
+ profile = {}
+ for r in results:
+ profile[r[1]] = float(r[-1][:-2])
+ return profile
+
+timings = {}
+def run(command, key):
+ profile = {}
+ for i in range(samples):
+ try:
+ results = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True).decode('utf-8')
+ except subprocess.CalledProcessError as exc:
+ print(exc.output.decode('utf-8'))
+ return
+ # exit(-1)
+
+ p = parse(results)
+ if len(profile) == 0:
+ profile = p
+ else:
+ for k, v in p.items():
+ profile.setdefault(k, 0)
+ profile[k] += v
+
+ for k in profile:
+ profile[k] /= samples
+
+ timings[key] = profile
+
+def compile_cmd(file, output, stage=None, entry=None, emit=False):
+ cmd = f'{slangc} -report-perf-benchmark {file}'
+
+ if stage:
+ cmd += f' -stage {stage}'
+ if entry:
+ cmd += f' -entry {entry}'
+ else:
+ cmd += f' -entry {stage}'
+
+ if emit:
+ cmd += f' -target {target_ext}'
+ output += '.' + target_ext
+ elif embed:
+ cmd += ' -embed-dxil'
+ cmd += ' -profile lib_6_6'
+ cmd += ' -incomplete-library'
+
+ cmd += f' -o {output}'
+
+ return cmd
+
+### Module precompilation ###
+
+modules = []
+
+for file in glob.glob(f'{repo}\\mdl\\*.slang'):
+ if file.endswith('hit.slang'):
+ run(compile_cmd(file, 'modules/closesthit.slang-module', stage='closesthit'), 'module/closesthit')
+ run(compile_cmd(file, 'modules/anyhit.slang-module', stage='anyhit'), 'module/anyhit')
+ run(compile_cmd(file, 'modules/shadow.slang-module', stage='anyhit', entry='shadow'), 'module/shadow')
+ else:
+ basename = os.path.basename(file)
+ run(compile_cmd(file, f'modules/{basename}-module'), 'module/' + file)
+ modules.append(f'modules/{basename}-module')
+
+ print(f'[I] compiled {file}.')
+
+### Entrypoint compilation ###
+hit = 'slang-benchmarks/mdl/hit.slang'
+files = ' '.join(modules)
+
+# Module
+cmd = compile_cmd(f'{files} modules/closesthit.slang-module', f'targets/dxr-ch-modules', stage='closesthit', emit=True)
+run(cmd, f'full/{target_ext}/module/closesthit')
+
+print(f'[I] compiled closesthit (module)')
+
+cmd = compile_cmd(f'{files} modules/anyhit.slang-module', f'targets/dxr-ah-modules', stage='anyhit', emit=True)
+run(cmd, f'full/{target_ext}/module/anyhit')
+
+print(f'[I] compiled anyhit (module)')
+
+cmd = compile_cmd(f'{files} modules/shadow.slang-module', f'targets/dxr-sh-modules', stage='anyhit', entry='shadow', emit=True)
+run(cmd, f'full/{target_ext}/module/shadow')
+
+print(f'[I] compiled shadow (module)')
+
+# Monolithic
+cmd = compile_cmd(hit, f'targets/dxr-ch-mono', stage='closesthit', emit=True)
+run(cmd, f'full/{target_ext}/mono/closesthit')
+
+print(f'[I] compiled shadow (monolithic)')
+
+cmd = compile_cmd(hit, f'targets/dxr-ah-mono', stage='anyhit', emit=True)
+run(cmd, f'full/{target_ext}/mono/anyhit')
+
+print(f'[I] compiled shadow (monolithic)')
+
+cmd = compile_cmd(hit, f'targets/dxr-sh-mono', stage='anyhit', entry='shadow', emit=True)
+run(cmd, f'full/{target_ext}/mono/shadow')
+
+print(f'[I] compiled shadow (monolithic)')
+
+# Module precompilation time
+precompilation_time = 0
+for k in timings:
+ if k.startswith('module'):
+ precompilation_time += timings[k]['compileInner']
+
+timings[f'full/{target_ext}/precompilation'] = { 'compileInner': precompilation_time }
+
+# Output to benchmark file
+json_data = []
+for k, v in timings.items():
+ if not k.startswith('full'):
+ continue
+
+ name = k.split('/')[1:]
+ name = ' : '.join(reversed(name))
+
+ data = {
+ 'name': name,
+ 'unit': 'milliseconds',
+ 'value': v['compileInner']
+ }
+
+ json_data.append(data)
+
+# TODO: append target to benchmark file name
+with open(args.output, 'w') as file:
+ json.dump(json_data, file, indent=4)
+
+# Generate readable Markdown as well
+print(4 * '\n')
+print('# Slang MDL benchmark results\n')
+print('## Module precompilation time\n')
+print(f'Total: **{timings[f'full/{target_ext}/precompilation']['compileInner']} ms**\n')
+
+print('## Module compilation for entry points\n')
+
+entries = [ 'Closest Hit', 'Any Hit', 'Shadow' ]
+prefixes = [ 'closesthit', 'anyhit', 'shadow' ]
+
+table = prettytable.PrettyTable()
+table.set_style(prettytable.MARKDOWN)
+table.field_names = [ 'Entry', 'Total' ]
+
+total = 0
+for entry, prefix in zip(entries, prefixes):
+ row = [ entry ]
+ db = timings[f'full/{target_ext}/module/{prefix}']
+ spCompile = db['compileInner']
+ row.append(f'{spCompile:.3f}s')
+ table.add_row(row)
+ total += spCompile
+
+print(f'Total: **{total} ms**\n')
+print(table, end='\n\n')
+
+print('## Monolithic compilation for entry points\n')
+
+table = prettytable.PrettyTable()
+table.set_style(prettytable.MARKDOWN)
+table.field_names = [ 'Entry', 'Total' ]
+
+total = 0
+for entry, prefix in zip(entries, prefixes):
+ row = [ entry ]
+ db = timings[f'full/{target_ext}/mono/{prefix}']
+ spCompile = db['compileInner']
+ row.append(f'{spCompile:.3f}s')
+ table.add_row(row)
+ total += spCompile
+
+print(f'Total: **{total} ms**\n')
+print(table, end='\n\n') \ No newline at end of file