Skip to content

Commit 01ca5ed

Browse files
committed
feat: Report per-ShaderGroup complexity statistics leaderboard
Store the new compile-time complexity metrics that were added by PR 2085 (but only printed the the log as each group compiles) as fields on ShaderGroup so they survive after optimization. Expose them via getattribute(group, "stat:compiled_active_layers") etc., and extend getstats() to emit a "Shader compilation stats, post-optimized" section listing min/max/median and a leaderboard with a ranked top-N list for each metric (controlled by the existing "stat:rank_groups" option). Tests: testsuite/compstats/ test covering both the getstats output and the getattribute keys. Added a testshade --print-group-stats flag for querying per-group stat keys. Assisted-by: Claude Code / claude-sonnet-4-6 Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent 6f7e47f commit 01ca5ed

11 files changed

Lines changed: 201 additions & 2 deletions

File tree

src/cmake/testing.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ macro (osl_add_all_tests)
280280
cellnoise closure closure-array closure-layered closure-parameters closure-zero closure-conditional
281281
color color2 color4 color-reg colorspace comparison
282282
complement-reg compile-buffer compassign-bool compassign-reg
283-
component-range
283+
component-range compstats
284284
control-flow-reg connect-components
285285
const-array-params const-array-fill
286286
debugnan debug-uninit

src/liboslexec/oslexec_pvt.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,7 @@ class ShadingSystemImpl {
889889

890890
// Options
891891
int m_statslevel; ///< Statistics level
892+
int m_stat_rank_groups = 5; ///< How many groups to list in ranked stats
892893
bool m_lazylayers; ///< Evaluate layers on demand?
893894
bool m_lazyglobals; ///< Run lazily even if globals write?
894895
bool m_lazyunconnected; ///< Run lazily even if not connected?
@@ -1841,6 +1842,15 @@ class ShaderGroup {
18411842

18421843
long long int executions() const { return m_executions; }
18431844

1845+
int stat_active_layers() const { return m_stat_active_layers; }
1846+
void stat_active_layers(int n) { m_stat_active_layers = n; }
1847+
int stat_network_depth() const { return m_stat_network_depth; }
1848+
void stat_network_depth(int n) { m_stat_network_depth = n; }
1849+
int stat_texture_ops() const { return m_stat_texture_ops; }
1850+
void stat_texture_ops(int n) { m_stat_texture_ops = n; }
1851+
int stat_noise_ops() const { return m_stat_noise_ops; }
1852+
void stat_noise_ops(int n) { m_stat_noise_ops = n; }
1853+
18441854
void start_running()
18451855
{
18461856
#ifndef NDEBUG
@@ -2057,6 +2067,11 @@ class ShaderGroup {
20572067
bool m_unknown_attributes_needed;
20582068
atomic_ll m_executions { 0 }; ///< Number of times the group executed
20592069
atomic_ll m_stat_total_shading_time_ticks { 0 }; // Shading time (ticks)
2070+
// Post-optimization compile stats (written once in RuntimeOptimizer::run)
2071+
int m_stat_active_layers = 0; // Non-unused layers after dead-layer elim
2072+
int m_stat_network_depth = 0; // Max layer-to-layer connection chain length
2073+
int m_stat_texture_ops = 0; // Texture op count across active layers
2074+
int m_stat_noise_ops = 0; // Noise op count across active layers
20602075

20612076
std::string m_optix_cache_key;
20622077

src/liboslexec/runtimeoptimize.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3474,6 +3474,11 @@ RuntimeOptimizer::run()
34743474
max_network_depth = std::max(max_network_depth, layer_depth[layer]);
34753475
}
34763476

3477+
group().stat_active_layers(active_layers);
3478+
group().stat_network_depth(max_network_depth);
3479+
group().stat_texture_ops(n_texture_ops);
3480+
group().stat_noise_ops(n_noise_ops);
3481+
34773482
m_stat_specialization_time = rop_timer();
34783483
{
34793484
// adjust memory stats

src/liboslexec/shadingsys.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,6 +1605,7 @@ ShadingSystemImpl::attribute(string_view name, TypeDesc type, const void* val)
16051605

16061606
lock_guard guard(m_mutex); // Thread safety
16071607
ATTR_SET("statistics:level", int, m_statslevel);
1608+
ATTR_SET("stat:rank_groups", int, m_stat_rank_groups);
16081609
ATTR_SET("debug", int, m_debug);
16091610
ATTR_SET("lazylayers", int, m_lazylayers);
16101611
ATTR_SET("lazyglobals", int, m_lazyglobals);
@@ -1797,6 +1798,7 @@ ShadingSystemImpl::getattribute(string_view name, TypeDesc type, void* val)
17971798
ATTR_DECODE_STRING("searchpath:shader", m_searchpath);
17981799
ATTR_DECODE_STRING("searchpath:library", m_library_searchpath);
17991800
ATTR_DECODE("statistics:level", int, m_statslevel);
1801+
ATTR_DECODE("stat:rank_groups", int, m_stat_rank_groups);
18001802
ATTR_DECODE("lazylayers", int, m_lazylayers);
18011803
ATTR_DECODE("lazyglobals", int, m_lazyglobals);
18021804
ATTR_DECODE("lazyunconnected", int, m_lazyunconnected);
@@ -2168,6 +2170,22 @@ ShadingSystemImpl::getattribute(ShaderGroup* group, string_view name,
21682170
*(int*)val = group->m_exec_repeat;
21692171
return true;
21702172
}
2173+
if (name == "stat:compiled_active_layers" && type == TypeInt) {
2174+
*(int*)val = group->stat_active_layers();
2175+
return true;
2176+
}
2177+
if (name == "stat:compiled_network_depth" && type == TypeInt) {
2178+
*(int*)val = group->stat_network_depth();
2179+
return true;
2180+
}
2181+
if (name == "stat:compiled_texture_ops" && type == TypeInt) {
2182+
*(int*)val = group->stat_texture_ops();
2183+
return true;
2184+
}
2185+
if (name == "stat:compiled_noise_ops" && type == TypeInt) {
2186+
*(int*)val = group->stat_noise_ops();
2187+
return true;
2188+
}
21712189
if (name == "ptx_compiled_version" && type.basetype == TypeDesc::PTR) {
21722190
bool exists = !group->m_llvm_ptx_compiled_version.empty();
21732191
*(std::string*)val = exists ? group->m_llvm_ptx_compiled_version : "";
@@ -2714,6 +2732,70 @@ ShadingSystemImpl::getstats(int level) const
27142732
}
27152733
}
27162734

2735+
// Ranked shader groups by compile-time complexity metrics
2736+
if (m_stat_groups_compiled > 0) {
2737+
// Collect a snapshot of all still-live compiled (optimized) groups.
2738+
std::vector<ShaderGroupRef> groups;
2739+
{
2740+
spin_lock lock(m_all_shader_groups_mutex);
2741+
for (auto&& w : m_all_shader_groups)
2742+
if (ShaderGroupRef g = w.lock())
2743+
if (g->optimized())
2744+
groups.push_back(g);
2745+
}
2746+
using StatVal = std::pair<int, ustring>;
2747+
print(out, " Shader compilation stats, post-optimized:\n");
2748+
auto emit_ranked_groups =
2749+
[&](string_view label, string_view unit,
2750+
std::function<int(const ShaderGroup&)> getter) {
2751+
if (groups.empty())
2752+
return;
2753+
// Gather values from all compiled groups for aggregate stats.
2754+
std::vector<int> vals;
2755+
vals.reserve(groups.size());
2756+
for (auto&& g : groups)
2757+
vals.push_back(getter(*g));
2758+
std::sort(vals.begin(), vals.end());
2759+
int vmin = vals.front();
2760+
int vmax = vals.back();
2761+
int vmedian = vals[vals.size() / 2];
2762+
print(out, " {}: min={} max={} median={}\n", label, vmin,
2763+
vmax, vmedian);
2764+
// Ranked list: exclude groups with value 0.
2765+
std::vector<StatVal> ranked;
2766+
for (auto&& g : groups) {
2767+
int v = getter(*g);
2768+
if (v > 0)
2769+
ranked.emplace_back(v, g->name());
2770+
}
2771+
if (ranked.empty())
2772+
return;
2773+
std::sort(ranked.begin(), ranked.end(),
2774+
[](const StatVal& a, const StatVal& b) {
2775+
return a.first != b.first ? a.first > b.first
2776+
: a.second < b.second;
2777+
});
2778+
if ((int)ranked.size() > m_stat_rank_groups)
2779+
ranked.resize(m_stat_rank_groups);
2780+
print(out, " Top shader groups:\n");
2781+
for (auto&& [v, name] : ranked)
2782+
print(out, " {:>6} {} \"{}\"\n", v, unit,
2783+
name.size() ? name.c_str() : "<unnamed>");
2784+
};
2785+
emit_ranked_groups("Active layers", "layers", [](const ShaderGroup& g) {
2786+
return g.stat_active_layers();
2787+
});
2788+
emit_ranked_groups("Network depth", "depth", [](const ShaderGroup& g) {
2789+
return g.stat_network_depth();
2790+
});
2791+
emit_ranked_groups("Texture ops", "ops", [](const ShaderGroup& g) {
2792+
return g.stat_texture_ops();
2793+
});
2794+
emit_ranked_groups("Noise ops", "ops", [](const ShaderGroup& g) {
2795+
return g.stat_noise_ops();
2796+
});
2797+
}
2798+
27172799
return out.str();
27182800
}
27192801

src/testshade/testshade.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ static bool debug_uninit = false;
8181
static bool use_group_outputs = false;
8282
static bool do_oslquery = false;
8383
static bool print_groupdata = false;
84+
static bool print_group_stats = false;
8485
static bool inbuffer = false;
8586
static bool use_shade_image = false;
8687
static bool userdata_isconnected = false;
@@ -828,6 +829,8 @@ getargs(int argc, const char* argv[])
828829
.help("Test OSLQuery at runtime");
829830
ap.arg("--print-groupdata", &print_groupdata)
830831
.help("Print groupdata size to stdout");
832+
ap.arg("--print-group-stats", &print_group_stats)
833+
.help("Print per-group compile stats (active_layers, network_depth, texture_ops, noise_ops) to stdout");
831834
ap.arg("--inbuffer", &inbuffer)
832835
.help("Compile osl source from and to jbuffer");
833836
ap.arg("--no-output-placement")
@@ -2335,6 +2338,23 @@ test_shade(int argc, const char* argv[])
23352338
std::cout << "Groupdata size: " << groupdata_size << "\n";
23362339
}
23372340

2341+
if (print_group_stats && !batched) {
2342+
int active_layers = 0, network_depth = 0, texture_ops = 0,
2343+
noise_ops = 0;
2344+
shadingsys->getattribute(shadergroup.get(),
2345+
"stat:compiled_active_layers", active_layers);
2346+
shadingsys->getattribute(shadergroup.get(),
2347+
"stat:compiled_network_depth", network_depth);
2348+
shadingsys->getattribute(shadergroup.get(), "stat:compiled_texture_ops",
2349+
texture_ops);
2350+
shadingsys->getattribute(shadergroup.get(), "stat:compiled_noise_ops",
2351+
noise_ops);
2352+
OSL::print("stat:compiled_active_layers={}\n", active_layers);
2353+
OSL::print("stat:compiled_network_depth={}\n", network_depth);
2354+
OSL::print("stat:compiled_texture_ops={}\n", texture_ops);
2355+
OSL::print("stat:compiled_noise_ops={}\n", noise_ops);
2356+
}
2357+
23382358

23392359
// Give the renderer a chance to do initial cleanup while everything is still alive
23402360
rend->clear();

testsuite/compstats/layer_a.osl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright Contributors to the Open Shading Language project.
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
shader layer_a(output color Cout = 0)
6+
{
7+
Cout = texture("test.tx", u, v);
8+
}

testsuite/compstats/layer_b.osl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright Contributors to the Open Shading Language project.
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
shader layer_b(color Cin = 0, output color Cout = 0)
6+
{
7+
Cout = Cin + noise("perlin", P) + noise("perlin", P * 2);
8+
}

testsuite/compstats/ref/out.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Shader compilation stats, post-optimized:
2+
Active layers: min=2 max=2 median=2
3+
Top shader groups:
4+
2 layers "complex"
5+
Network depth: min=2 max=2 median=2
6+
Top shader groups:
7+
2 depth "complex"
8+
Texture ops: min=1 max=1 median=1
9+
Top shader groups:
10+
1 ops "complex"
11+
Noise ops: min=2 max=2 median=2
12+
Top shader groups:
13+
2 ops "complex"
14+
stat:compiled_active_layers=2
15+
stat:compiled_network_depth=2
16+
stat:compiled_texture_ops=1
17+
stat:compiled_noise_ops=2

testsuite/compstats/run.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright Contributors to the Open Shading Language project.
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
# https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
6+
7+
# Test that per-group compile stats are recorded and reported in getstats().
8+
#
9+
# "complex" group: layer_a (1 texture op) -> layer_b (2 noise ops)
10+
# Expected: active_layers=2, network_depth=2, texture_ops=1, noise_ops=2
11+
#
12+
# With statistics:level=1, getstats() should emit min/max/median and a
13+
# ranked list for each metric.
14+
15+
command = testshade(
16+
"--options statistics:level=1"
17+
" --groupname complex"
18+
" --shader layer_a la"
19+
" --shader layer_b lb"
20+
" --connect la Cout lb Cin"
21+
" -o Cout null"
22+
)
23+
24+
command += testshade(
25+
"--print-group-stats"
26+
" --groupname complex"
27+
" --shader layer_a la"
28+
" --shader layer_b lb"
29+
" --connect la Cout lb Cin"
30+
" -o Cout null"
31+
)
32+
33+
# Filter to only the new per-group ranked stats lines and getattribute
34+
# stat key output; everything else is machine- or build-specific.
35+
# Note: runtest uses re.match() (anchored at line start), so prefix with .*
36+
filter_re = r".*(Shader compilation stats|Active layers|Network depth|Texture ops|Noise ops|Top shader groups|stat:)"

testsuite/compstats/simple.osl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright Contributors to the Open Shading Language project.
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
shader simple(output color Cout = 0)
6+
{
7+
Cout = color(u, v, 0);
8+
}

0 commit comments

Comments
 (0)