Execution Plans (glass-defaults.cuh)#
glass::recommend() turns the measured implementation ladder into one
constexpr value. It returns:
the measured implementation family and execution scope for this shape; and
a ready-to-use, legal launch packing.
The packing fields are defaults, not a claim that every caller’s optimal block size was measured. Applications may retune them within the explicit operation’s documented launch contract.
The decision is host-side or code-generation-time. A thread, warp, block, and
NVIDIA block implementation require different CUDA launches; recommend()
does not dispatch a device call for you.
The contract#
enum class glass::family { native, nvidia };
enum class glass::scope { thread, warp, block };
enum class glass::dependency_set { native_only, mathdx };
struct glass::execution_plan {
family implementation;
scope execution_scope;
uint32_t block_threads;
uint32_t problems_per_block;
uint32_t shared_bytes;
};
template <glass::op Op, typename T, uint32_t... Dims>
constexpr glass::execution_plan glass::recommend(
glass::dependency_set dependencies = glass::dependency_set::native_only,
uint32_t sm = GLASS_TARGET_SM);
Shape arguments follow the operation’s mathematical order:
Square ladder operations:
recommend<op, T, N>()Rectangular GEMV:
recommend<op::gemv, T, M, N>()Rectangular GEMM:
recommend<op::gemm, T, M, N, K>()
native_only is deliberately the default. Pass dependency_set::mathdx
to admit measured glass::nvidia::block and glass::nvidia::thread
candidates. Each measured architecture carries a paired native-only table from
the same capture, rather than approximating vendor-winning cells with a size
heuristic. The result is independent of header include order.
Using a plan#
constexpr auto plan = glass::recommend<glass::op::potrf, float, N>(
glass::dependency_set::mathdx);
if constexpr (plan.implementation == glass::family::nvidia &&
plan.execution_scope == glass::scope::thread) {
// one problem per thread
// glass::nvidia::thread::potrf<float, N>(...)
} else if constexpr (plan.implementation == glass::family::nvidia) {
// one problem per block; query the explicit wrapper's exact requirements
// glass::nvidia::block::potrf<float, N>(...)
} else if constexpr (plan.execution_scope == glass::scope::warp) {
// one problem per warp
// glass::warp::potrf<float, N>(...)
} else if constexpr (plan.execution_scope == glass::scope::thread) {
// one problem per thread
// glass::thread::potrf<float, N>(...)
} else {
// one problem per block
// glass::block::potrf<float, N>(...)
}
For native plans, block_threads and problems_per_block are complete
launch guidance. NVIDIA block descriptors own shape-specific thread and shared
memory requirements; those fields use
execution_plan::dynamic_requirement when the explicit backend query must be
consulted.
GLASS_TARGET_SM#
GLASS_TARGET_SM selects the measured architecture table and the MathDx
descriptor architecture from one build setting. It defaults to the shipped
sm_120 seed. Define it explicitly when targeting another GPU, for example
-DGLASS_TARGET_SM=870. The historical SMS macro remains an input alias
for existing build systems.
The shipped tables currently cover sm_120 and sm_87. Unmeasured architectures
use conservative generic choices until bench/tune.py adds a measured table.
examples/08_backend_picker.cu is a complete native-only launcher.
This launch-level plan is separate from glass::dispatch_body(). The latter
selects a measured thread-0, warp-0, or full-block implementation inside the
fixed block-scope contract of bare glass::op calls. See
Namespaces, suffixes, and flags.