mirror of
https://github.com/Niko1221/Strata.git
synced 2026-11-07 18:35:04 -10:00
#646 on 0.1.39b: the layer split and the CPU path as before, the resident plan for 8-token windows
- layer split: commit stays synchronous on a split (set_commit_async(!multi_gpu), the `|| next_ != nullptr`), no pre-staging of the next stage's inputs, kDrafterMib 1000 - the split's sizing and order as in 0.1.39 (untested here: one GPU); - the CPU expert kernels keep cpu_avx512_ok() (F/BW/VL/VNNI/VBMI): #646's AVX-512BW-only check would send a CPU without VNNI/VBMI to kernels that use them; - resident_plan_kernel: one thread per window entry up to 128 (64 lost a 7- or 8-token window's last entries on the all-resident path); - the host keeps zeroing the GPU's rows under STRATA_VERIFY_DEVICE_PLAN=1 (copy_or_zero_from_mapped copies every row there). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
3a64d02a5e
commit
deee4473e8
5 files changed
+21
-14
No files matched your search
@@ -2100,7 +2100,10 @@ void expert_pool_dispatch_multi(ExpertDispatch& d, const float* x_f, const int32
|
||||
}
|
||||
}
|
||||
static const bool dec_batch = [] { const char* v = std::getenv("STRATA_DEC_BATCH"); return v == nullptr || std::atoi(v) != 0; }();
|
||||
const bool gpu_zeroes_hits = (d.plan != nullptr && n <= kMaxWindowEntries && n <= d.plan->cap && dec_batch);
|
||||
// the GPU's copy_rows_from_mapped zeroes the rows it computed itself (dec_batch); STRATA_VERIFY_DEVICE_PLAN's
|
||||
// copy_or_zero_from_mapped copies every row, so there the host still zeroes them
|
||||
static const bool device_plan = [] { const char* v = std::getenv("STRATA_VERIFY_DEVICE_PLAN"); return v != nullptr && std::atoi(v) != 0; }();
|
||||
const bool gpu_zeroes_hits = (d.plan != nullptr && n <= kMaxWindowEntries && n <= d.plan->cap && dec_batch && !device_plan);
|
||||
bool any_cpu = false;
|
||||
for (int64_t i = 0; i < n; ++i)
|
||||
if (kind[i] < 0) { any_cpu = true; break; }
|
||||
|
||||
+2
-2
@@ -1423,7 +1423,7 @@ bool Verifier::run(int T, const int32_t* tokens, int64_t pos0, PoolMultiFn pool,
|
||||
ms_wait += std::chrono::duration<double, std::milli>(b - a).count();
|
||||
ms_pool += ms_since(b);
|
||||
}
|
||||
if (next_ != nullptr) next_->stage_inputs(T, tokens, pos0);
|
||||
// (#646 staged the next stage's inputs here; 0.1.39b keeps the layer split's order: each stage stages its own)
|
||||
progress_at("verify window: waiting for the GPU to finish the window (flags A/B/M raised)", (int64_t) T);
|
||||
// #267: a window the GPU never finishes (a spin kernel that never sees its flag) holds the host here; the stall
|
||||
// watchdog then releases every verifier's GPU waits (release_live_verifiers) before it ends the engine, so no
|
||||
@@ -1642,7 +1642,7 @@ bool Verifier::commit(int n_keep, std::string& err) {
|
||||
// set_commit_async: no wait here - the next window runs on the same stream after it, and the drafter (its own
|
||||
// stream) reads only this window's final rows and its own K/V. h_commit_ is next written after the next window's
|
||||
// results are read, i.e. after this graph has run. Everything else waits on commit_done_ (wait_commit).
|
||||
if (!g_commit_async) {
|
||||
if (!g_commit_async || next_ != nullptr) {
|
||||
const cudaError_t se = cudaStreamSynchronize(cs_);
|
||||
if (se != cudaSuccess) { err = std::string("verify: commit: ") + cudaGetErrorString(se); return false; }
|
||||
} else {
|
||||
|
||||
@@ -83,7 +83,7 @@ void native_gu_rows(const NativeFmt& f, const uint8_t* blob, const void* const*
|
||||
// first, then the AVX-2 one (Zen 2/3, Intel 12th-14th gen). STRATA_NO_IQ512 drops an AVX-512 CPU to the
|
||||
// AVX-2 kernel, STRATA_NO_IQ256 drops the AVX-2 kernel; ggml-cpu's single-token vec_dot is reached only with
|
||||
// both set (and on a CPU without AVX-512, STRATA_NO_IQ512 changes nothing).
|
||||
static const bool avx512 = cpu_avx512bw_ok() && std::getenv("STRATA_NO_IQ512") == nullptr;
|
||||
static const bool avx512 = cpu_avx512_ok() && std::getenv("STRATA_NO_IQ512") == nullptr;
|
||||
static const bool avx2 = std::getenv("STRATA_NO_IQ256") == nullptr;
|
||||
// #152: from how many tokens the multi-token kernels run (ggml's vec_dot below that). The default 2 is the
|
||||
// measured-fastest rule, but a token's expert rows then round differently alone than in a group, so greedy output
|
||||
@@ -101,7 +101,7 @@ void native_gu_rows(const NativeFmt& f, const uint8_t* blob, const void* const*
|
||||
// A format with only an AVX-2 kernel (IQ4_XS, #415) takes it on AVX-2 CPUs only: an AVX-512 CPU keeps ggml-cpu for
|
||||
// it, as before (its rows would round differently). Each kernel only for the formats it implements: falling
|
||||
// through an empty switch would leave ff unwritten instead of falling back to ggml-cpu.
|
||||
static const bool cpu512 = cpu_avx512bw_ok();
|
||||
static const bool cpu512 = cpu_avx512_ok();
|
||||
if (nt >= mt_min && (iq512_supported(f.gu_type) || (!cpu512 && iq256_supported(f.gu_type)))) {
|
||||
if (avx512 && iq512_supported(f.gu_type)) {
|
||||
iq512_gu_rows(f.gu_type, blob, f.gu_row, f.up_off, (int) f.n_embd, act, nt, ff, r0, r1);
|
||||
|
||||
@@ -500,15 +500,19 @@ __global__ void wait_flag_ge_kernel(const volatile uint32_t* flag, uint32_t valu
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
__global__ void resident_plan_kernel(const int32_t* __restrict__ ids, int n, int k, const int32_t* __restrict__ res,
|
||||
// one thread per window entry: up to kVerifyMaxT tokens x 10 routed experts (80), so 128 (#646 had 64: a window of
|
||||
// 7+ tokens lost its last entries)
|
||||
constexpr int kResidentPlanMax = 128;
|
||||
static_assert(kVerifyMaxT * 10 <= kResidentPlanMax, "resident_plan: one thread per entry");
|
||||
__global__ void __launch_bounds__(kResidentPlanMax) resident_plan_kernel(const int32_t* __restrict__ ids, int n, int k, const int32_t* __restrict__ res,
|
||||
int n_expert, const uint8_t* cache_base, const unsigned long long* slot_off,
|
||||
long long blob, int32_t* __restrict__ pl, long long capx, uint32_t* skip,
|
||||
uint32_t ring) {
|
||||
__shared__ int32_t s_ids[64];
|
||||
__shared__ unsigned long long s_ptr[64];
|
||||
__shared__ int32_t s_first[64];
|
||||
__shared__ int32_t s_cnt[64];
|
||||
__shared__ int32_t s_gstart[64];
|
||||
__shared__ int32_t s_ids[kResidentPlanMax];
|
||||
__shared__ unsigned long long s_ptr[kResidentPlanMax];
|
||||
__shared__ int32_t s_first[kResidentPlanMax];
|
||||
__shared__ int32_t s_cnt[kResidentPlanMax];
|
||||
__shared__ int32_t s_gstart[kResidentPlanMax];
|
||||
__shared__ int s_bad;
|
||||
const int tid = threadIdx.x;
|
||||
if (tid == 0) s_bad = 0;
|
||||
@@ -609,7 +613,7 @@ __global__ void copy_or_zero_kernel(float4* __restrict__ dst, const volatile flo
|
||||
void resident_plan(const int32_t* ids, int n_entries, int k, const int32_t* res_layer, int n_expert,
|
||||
const uint8_t* cache_base, const unsigned long long* slot_off, long long blob, int32_t* plan,
|
||||
long long capx, uint32_t* skip, uint32_t ring, void* stream) {
|
||||
resident_plan_kernel<<<1, 64, 0, (cudaStream_t) stream>>>(ids, n_entries, k, res_layer, n_expert, cache_base, slot_off,
|
||||
resident_plan_kernel<<<1, kResidentPlanMax, 0, (cudaStream_t) stream>>>(ids, n_entries, k, res_layer, n_expert, cache_base, slot_off,
|
||||
blob, plan, capx, skip, ring);
|
||||
check("resident_plan");
|
||||
}
|
||||
|
||||
@@ -2505,7 +2505,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
}
|
||||
strata::core::Verifier::set_commit_async(!split_same); // see Verifier::set_commit_async
|
||||
strata::core::Verifier::set_commit_async(!multi_gpu); // see Verifier::set_commit_async
|
||||
// ---- layer split across GPUs: each later stage's own copy of the dense weights, its session and (the last) the
|
||||
// head, made on its device before the host arena is mapped (as the drafter below, for the same WDDM reason)
|
||||
std::vector<std::unique_ptr<GpuStage>> stages;
|
||||
@@ -2680,7 +2680,7 @@ int main(int argc, char** argv) {
|
||||
// those allocations are already made before a stage's cache is sized, so what has to be held back here is
|
||||
// the windows and - only on the stage that carries them - the drafter and the head.
|
||||
const int64_t kWindowMib = 96; // the verify windows; 75 MiB measured, rounded up
|
||||
const int64_t kDrafterMib = 1320; // the MTP drafter (839 MiB) + the native Q5_K head (466 MiB), on the last stage only
|
||||
const int64_t kDrafterMib = 1000; // the MTP drafter (839 MiB) + the head, on the last stage only
|
||||
// #340: with the own prompt buffers chosen by the split's rule (not asked for with --no-prefill-borrow) the
|
||||
// boundary is searched as the borrowing configuration would (no reserve): the reserve then only makes the caches
|
||||
// smaller, which measured cost no decode (K=28 on 9070 XT + R9700: 58.4 tok/s own vs 58.5 borrowing), while a
|
||||
|
||||
Reference in new issue
Block a user