prefill: the batched PLE post-ops default off for an engine built with CUDA 12.8 on a compute capability 12.0 card (one log line); the variable still wins

An engine built with CUDA 12.8.61 faults in the prompt path on an RTX PRO 6000 (compute capability 12.0, driver 580) with the batched PLE post-ops on, which is the
default: "an illegal memory access" at every prompt length from 94 tokens, first error in the native PLE postops launch (CUDA_LAUNCH_BLOCKING), and the isolated
verify_batch_parity test faults on native_ple_postops_batch_snap with 4 tokens. STRATA_PLE_BATCH=0 runs. The same source built with CUDA 13.0 (the released
cuda13 package) runs the default at 94 to 20K tokens, where the batch is ~23% faster prompts, so only the 12.8 toolchain changes: ple_batch_default(cudart_version, hip,
major, minor) is false only for CUDART_VERSION 12080..12089 on cc 12.0 (not HIP, not another card, not another toolkit), ple_batch_resolve lets STRATA_PLE_BATCH win in both
directions, and prefill.cpp says once when it turned the batch off. Known issue #220 / #224; CMake already warned for sm_120 builds with CUDA < 13.0. Unit test in
arch_defaults_test; docs/OLDER_GPUS.md.
This commit is contained in:
Niko1221 committed 2026-11-02 07:39:06 +01:00
1 parent ee108afef5
commit 78316c8df4
4 files changed
+84 -8

No files matched your search

+5
View File
@@ -99,6 +99,11 @@ A model that shares a V100 with an RTX 30 / 40 card runs both on the CUDA 12 eng
50 card (sm_120) in a CUDA 12 engine is a warning, not a stop: engines built with 12.8 crashed on long prompts there (#220,
#224). Keep the RTX 50 card on its own model (`--gpu N`), where it runs the CUDA 13 engine.
An engine you build yourself with CUDA 12.8 for an RTX 50 / RTX PRO Blackwell card (compute capability 12.0) now starts with the batched PLE post-ops of the
prompt path **off** and says so once (`STRATA_PLE_BATCH=1` turns them on anyway; CMake warns at configure time as before). That is the one place the
default differs by toolkit: the same source built with CUDA 13.0 or newer (the released cuda13 engine) keeps them on, which is about 23% faster prompts, and no
other card or toolkit changes. Use CUDA 13.0 or newer for an RTX 50 card.
### Building it yourself
```sh
+16
View File
@@ -39,6 +39,22 @@ inline bool sm120_series() {
/// the device (raw cudaDevAttrComputeCapabilityMajor / Minor) is a 12.0 card, or emulates one (STRATA_EMULATE_CC=120)
inline bool is_sm120(int major, int minor) { return cc_major_of(major) == 12 && cc_minor_of(minor) == 0; }
/// STRATA_PLE_BATCH (the prompt path's batched PLE post-ops) when the user set nothing. ON everywhere except one toolchain: an engine built with CUDA 12.8
/// (CUDART_VERSION 12080..12089) running on a compute capability 12.0 card faults in the prompt path with it ("an illegal memory access", first error in the
/// native PLE postops launch; RTX PRO 6000, driver 580, srv118; issues #220, #224; CMake already warns for a CUDA < 13.0 sm_120 build). The same source built
/// with CUDA 13.0 (the released cuda13 package) and 13.2 runs it at every prompt length, and it is worth ~23% of the prompt speed there, so it stays on there.
/// `cudart_version`: CUDART_VERSION of the toolkit the engine was built with (0 in a HIP build); `hip`: a HIP build (a gfx12 card reports 12.0 as well).
inline bool ple_batch_default(int cudart_version, bool hip, int major, int minor) {
const bool cuda_12_8 = !hip && cudart_version >= 12080 && cudart_version < 12090;
return !(cuda_12_8 && is_sm120(major, minor));
}
/// The decision: STRATA_PLE_BATCH wins in both directions (text that reads as 0 is off, anything else on, as before); unset is ple_batch_default.
inline bool ple_batch_resolve(const char* env, int cudart_version, bool hip, int major, int minor) {
if (env != nullptr) return std::atoi(env) != 0;
return ple_batch_default(cudart_version, hip, major, minor);
}
/// the default of a bitwise switch of the set: on for a 12.0 card unless STRATA_SM120_PERF=0
inline bool sm120_default(int major, int minor) { return is_sm120(major, minor) && sm120_series(); }
+32
View File
@@ -537,5 +537,37 @@ int main() {
CHECK(!prefill_layer_major_resolve(nullptr, false, 2, true));
CHECK(!prefill_layer_major_resolve(nullptr, true, 2));
}
{ // STRATA_PLE_BATCH: on everywhere but an engine built with CUDA 12.8 on a compute capability 12.0 card (issues #220, #224)
using strata::ple_batch_default;
using strata::ple_batch_resolve;
const int rt128 = 12080, rt129 = 12090, rt130 = 13000, rt132 = 13020, rt127 = 12070;
// the one case that is off: CUDA 12.8 build, cc 12.0
CHECK(!ple_batch_default(rt128, false, 12, 0));
CHECK(!ple_batch_default(12089, false, 12, 0)); // any 12.8.x
// every other toolkit keeps it on on the same card
CHECK(ple_batch_default(rt130, false, 12, 0)); // the released cuda13 package
CHECK(ple_batch_default(rt132, false, 12, 0));
CHECK(ple_batch_default(rt129, false, 12, 0));
CHECK(ple_batch_default(rt127, false, 12, 0));
// every other card keeps it on with 12.8
CHECK(ple_batch_default(rt128, false, 8, 6)); // sm_86
CHECK(ple_batch_default(rt128, false, 8, 9));
CHECK(ple_batch_default(rt128, false, 7, 5));
CHECK(ple_batch_default(rt128, false, 12, 1)); // GB10 (sm_121) is not the 12.0 card
CHECK(ple_batch_default(rt128, false, 9, 0));
// a HIP build never turns it off (a gfx12 card reports 12.0 as well; there is no CUDART there)
CHECK(ple_batch_default(0, true, 12, 0));
CHECK(ple_batch_default(rt128, true, 12, 0));
// the variable wins both ways, on any toolkit and card
CHECK(ple_batch_resolve("1", rt128, false, 12, 0)); // on anyway
CHECK(!ple_batch_resolve("0", rt130, false, 12, 0));
CHECK(!ple_batch_resolve("0", rt128, false, 8, 6));
CHECK(ple_batch_resolve("2", rt128, false, 12, 0));
CHECK(!ple_batch_resolve("off", rt130, false, 8, 6)); // text that reads as 0 is off (atoi), as before
// unset: the rule
CHECK(!ple_batch_resolve(nullptr, rt128, false, 12, 0));
CHECK(ple_batch_resolve(nullptr, rt130, false, 12, 0));
CHECK(ple_batch_resolve(nullptr, rt128, false, 8, 6));
}
return fails ? 1 : 0;
}
+31 -8
View File
@@ -53,6 +53,35 @@
#include "strata/core/peer_experts.hpp"
#include "strata/core/expert_nibble.hpp"
#include "strata/core/sm120_series.hpp"
namespace {
// STRATA_PLE_BATCH for the prompt path: the user's variable wins; unset, it is on except for an engine built with CUDA 12.8 on a compute capability 12.0 card
// (sm120_series.hpp ple_batch_default), which says so once.
bool ple_batch_enabled() {
#if defined(CUDART_VERSION) && !defined(STRATA_USE_HIP) && !defined(__HIPCC__)
constexpr int kRt = CUDART_VERSION;
constexpr bool kHip = false;
#else
constexpr int kRt = 0;
constexpr bool kHip = true;
#endif
static const char* env = std::getenv("STRATA_PLE_BATCH");
int dev = 0, major = 0, minor = 0;
if (env == nullptr && !kHip) {
if (cudaGetDevice(&dev) == cudaSuccess) {
cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, dev);
cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, dev);
}
(void) cudaGetLastError();
}
const bool on = strata::ple_batch_resolve(env, kRt, kHip, major, minor);
static std::atomic<bool> said{false};
if (!on && env == nullptr && !said.exchange(true))
std::fprintf(stderr, "strata prefill: the batched PLE post-ops are off by default here: this engine was built with CUDA 12.8, whose build faults in the prompt path on a "
"compute capability 12.0 card (issues #220, #224). Use an engine built with CUDA 13.0 or newer; STRATA_PLE_BATCH=1 turns it on anyway.\n");
return on;
}
} // namespace
#include "strata/core/defaults_026.hpp"
#include "strata/prefill/kernels.hpp"
#include "strata/prefill/kva.hpp" // STRATA_KVA: the optional approximate-prefill plan (research, off by default)
@@ -3249,10 +3278,7 @@ bool Prefill::run_impl(const int64_t* tokens, int64_t n, int64_t pos0, std::stri
const bool ple_on = ss.ple.ready() && LB <= 1 && 1 < LE;
// the PLE block batched over the chunk: the pinned postops and a BF16 or GGUF-native key (else token by token);
// STRATA_PLE_BATCH=0 keeps the per-token block (the A/B)
static const bool ple_batch_env = [] {
const char* v = std::getenv("STRATA_PLE_BATCH");
return v == nullptr || std::atoi(v) != 0;
}();
const bool ple_batch_env = ple_batch_enabled();
const bool ple_batch = ple_on && ple_batch_env && strata::kernels::ple_native_postops_enabled() &&
(ss.ple.w.key_bf16 != nullptr || ss.ple.w.key_native_data != nullptr) &&
m.region_bytes / ((uint64_t) (3 * strata::kernels::NG_HC_DIM + N + 4) * 4 + (uint64_t) N * 2 + 4096) >= 64;
@@ -6607,10 +6633,7 @@ bool Prefill::lm_run(const std::vector<Seg>& segs, int depth, const std::vector<
if (m.ids_identity) mmq::iota(m.ids_identity, m.T * K, m.cs);
// ---- the PLE rows, read per chunk on a thread as run_impl does (the same state machine, in chunk order)
const bool ple_on = m.ss->ple.ready();
static const bool ple_batch_env = [] {
const char* v = std::getenv("STRATA_PLE_BATCH");
return v == nullptr || std::atoi(v) != 0;
}();
const bool ple_batch_env = ple_batch_enabled();
const bool ple_batch = ple_on && ple_batch_env && strata::kernels::ple_native_postops_enabled() &&
(m.ss->ple.w.key_bf16 != nullptr || m.ss->ple.w.key_native_data != nullptr) &&
m.region_bytes / ((uint64_t) (3 * strata::kernels::NG_HC_DIM + N + 4) * 4 + (uint64_t) N * 2 + 4096) >= 64;