From 724060b86e6490490565e363209982bccd186df7 Mon Sep 17 00:00:00 2001 From: Venkataram Sivaram Date: Fri, 20 Sep 2024 14:40:03 -0700 Subject: [PATCH] Add instructions for running the benchmark locally --- .github/workflows/pages.yml | 3 ++- README.md | 32 ++++++++++++++++++++++++++++---- media/script.png | Bin 0 -> 14543 bytes 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 media/script.png diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index f79fdbe..9012207 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -4,7 +4,8 @@ on: branches: - master paths-ignore: - - README.md + - 'README.md' + - 'media/*' permissions: deployments: write diff --git a/README.md b/README.md index 8a139eb..1827f5e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [**See the charts here**](https://shader-slang.com/slang-material-modules-benchmark/dev/bench) -**How it works:** +## How it works 1. Commits to the master branch of [Slang](https://github.com/shader-slang/slang) will trigger a CI workflow that [runs the benchmark](https://github.com/shader-slang/slang/actions/workflows/push-benchmark-results.yml) and uploads the JSON file to **this** repository (specifically to `benchmarks.json`). 2. Changes to `benchmark.json` will trigger a CI workflow in this repository. 3. This workflow updates the `gh-pages` repository using [`github-action-benchmark`](fffffhttps://github.com/benchmark-action/github-action-benchmark). It reads the new results from `benchmarks.json` and updates a database in `gh-pages`. @@ -18,8 +18,32 @@ The diagram below summarizes these steps. - Each time `benchmarks.json` is updated (2), the `github-action-benchmark` workflow reads its contents and appends a database that is embedded in a [Javascript file](https://github.com/shader-slang/slang-material-modules-benchmark/blob/gh-pages/dev/bench/data.js). - There is currently no behaviour to limit the number of entries in the database, so it can grow to a couple megabytes. Manually trimming the data is possible by directly editing the Javascript file and removing entries. -**How to read the charts:** -- Each chart is title with the format ` : : ` +## Running the benchmark locally + +It is possible to run the benchmark locally to see immediate results or to customize the results. This requires the having cloning both the [Slang repository](https://github.com/shader-slang/slang) and the [MDL-SDK fork](https://github.com/shader-slang/MDL-SDK). Then, starting from the root of the Slang repository: +1. Build the `Release` version of slangc; it should be located in `build/Release/bin/slangc`. +2. Change directories to `tool/benchmark` within the Slang repository. +3. Copy the Slang shaders: + - From `/examples/mdl_sdk/dxr/content/slangified` (`*.slang` files specifically) + - To `tool/benchmark` +4. Run the `compile.py` script using Python (3.12+ recommended): + - Requires some light packages, which can be installed with `pip` using: `pip install prettytable argparse`. + - Linux users may have to tweak the script to set the `slangc` variable to end with `slangc` instead of `slangc.exe`. + - Script options: + - `--target` to select target mode: + - `spirv` generates SPIRV directly. + - `spirv-glsl` generates SPIRV via glsl. + - `dxil` generates DXIL through DXC. + - `dxil-embedded` generates DXIL through DXC, but precomiles slang modules to DXIL + - `--samples` to set the number of times to repeat and average the measurements over. + - `--output` path to the JSON file where results will be stored. + +The script will output timings in a Markdown friendly way, as shown below: + +![script](media/script.png) + +## How to read the charts +- Each chart is titled with the format ` : : ` - `` is one of `closesthit`, `anyhit`, and `shadow` - `` is either `mono` for monolithic compilation or `module` for modular compilation. - `` is currently fixed to DXIL. @@ -27,7 +51,7 @@ The diagram below summarizes these steps. - The $x$-axis tracks the commit hash. Unfortunately there is currently no way to display concrete dates. - The $y$-axis shows the time, in milliseconds, taken to compile the specific shader stage under the particular compilation mode and target. -**Interacting with the charts:** +## Interacting with the charts In case there is a commit which results in alarming measurements, there is a convenient way to reach the original commit/PR. Each data point of each graph can be highlighted as so: diff --git a/media/script.png b/media/script.png new file mode 100644 index 0000000000000000000000000000000000000000..c52a25338520566d52308f76d1acd80bd029ae7a GIT binary patch literal 14543 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

6=RL$cN@(B z4A)LzKG*BC8jZMANWa~0FTq@XI?fIs5An-t5F`jTU@Qb$#XrBE z`5i(>y&ETROUQQ6ahPy0(dZ#X-tW8lld(bA2;Y2;x`OUahat{? zcgKSTt|sbF!!(4njk;_Eu6~`x>R#7VzYI!hsqAqZYfsoXbtHFkX*L--csLb1P>Ffb z6glM~h`l*HV`!Y5ZV(wM`Z6rkg1zP0n-sYFl{Ix6 z-jPWrT4Vdyb{G!g5#cMV38ca@BR=4cRobL8x5y(R*GpUwW9Ox%=l7m&v`YPk0f^Rrv8RQX%|!(0 zBYrE2LUYxf3$!*Zgy%PKWzI=!?goaKYmCm+QRqDqr%MmL+ONe}L`XcNXCWm(>oq3v z>7I_>{1j4}EqijShzP~JBkF_RrnL~+zKE`f{DysrYbFEFc(i~mCo@xvK?{ra zvZO|g0S=ZbiGL(Wk7;*!5>nD^9ycM)A&0SqyhAApGBn=y&`tqg- zW85At#Wb`3&RU7E=2mkT%ZoKbM`U8VwMeE&_Y}sBG>);&4SV^qTjq)_hJiu2zH3VY zJ%s2vX1J-@m@kJpXM-_gjuy(%@CF4$U(iMPUm0WnzPQlYm;>SgQNXiR(y*%+z^gbYi=OP~k_#RS#FI6u;np(Z~n{!S`ENKmn5+mtv38r(ti>}|j9P`tceya{#2^&J0gt1)eU)%&yG)k7p#jx`6y zzyB(KpK*1L9cJ2ORMX2BZW7S*0yW4hz4LgN<&PjMl6CvTYOWgM_qxpNFD(T+mq^d2 z2SRoz^v>_)+bp8+=4bgeou{P0WS;VJ}4c<;_N}~|YmmTJx)0g3RpM`RZ z3}=GAh?A`126XQ{RuWe~hdG{Ylt6CR>wAW_+f=+l%3h%_>A}$N%b3D85Ln;WO0sz+ zl(xAqxCvk2H~su}H|b(BC*^^hf)=jxOoQ}9y|ByyLd3c=F;k`O9d~|x0W)P zJHXyjh1;bzCpg@nAYA)gr3O7}nyUrEOPJ SZs>~;0n!o*;zgo*U;hV=5zs3D literal 0 HcmV?d00001