Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/strata/kernels/ngram.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ enum class PleIo { Direct, Mmap };

struct PleIoOptions {
PleIo mode = PleIo::Direct;
uint32_t max_inflight = 64; ///< outstanding SSD reads (decode needs 16; prefill chunks use more)
uint32_t max_inflight = 256; ///< outstanding SSD reads (decode needs 16; a prompt chunk saturates the SSD at 256)
uint64_t cache_rows = 1u << 20; ///< bounded row cache: 1,048,576 rows x 90 B ~ 95 MB; 0 disables
bool io_thread = true; ///< reads submitted by a worker thread, not the caller
/// Mmap mode only (`--ple-io ram`): lock the whole mapped table in RAM at open, so no SSD read ever sits on
Expand Down
37 changes: 28 additions & 9 deletions src/prefill/prefill.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1107,26 +1107,43 @@ bool Prefill::run(const int64_t* tokens, int64_t n, int64_t pos0, std::string& e
}
}
if (hand_in_ == nullptr) gr_broadcast(m.emb, m.R, T, m.cs);
// ---- the PLE rows of the whole chunk, one batched SSD request (read ahead on a thread, see ple_gather)
if (ple_on) {
// ---- the PLE rows of the whole chunk, one batched SSD request on a thread (see ple_gather). A later chunk's
// were read ahead during the previous chunk; the first chunk's are read beside layer 0 - they are needed from
// layer 1 on, and gathering them here first left the GPU idle for the whole read (~0.4 s of a 32K prompt)
if (ple_on && !ple_next.valid())
ple_next = std::async(std::launch::async, [&ple_gather, &ple_next_err, c0, b = ple_buf] {
return ple_gather(c0, b, ple_next_err);
});
bool ple_pending = ple_on;
// the chunk's rows onto the device just before layer 1 reads them, and the next chunk's gather started
auto ple_land = [&]() -> bool {
if (!ple_pending) return true;
ple_pending = false;
const auto tp = Clock::now();
if (!ple_next.valid()) {
if (!ple_gather(c0, ple_buf, err)) return false;
} else if (!ple_next.get()) {
if (!ple_next.get()) {
err = ple_next_err;
return false;
}
cudaMemcpyAsync(m.ple_emb, m.ple_emb_host[ple_buf], (size_t) T * N * 4, cudaMemcpyHostToDevice, m.cs);
cudaEventRecord(m.ple_copied[ple_buf], m.cs);
if (cudaMemcpyAsync(m.ple_emb, m.ple_emb_host[ple_buf], (size_t) T * N * 4, cudaMemcpyHostToDevice, m.cs) !=
cudaSuccess ||
cudaEventRecord(m.ple_copied[ple_buf], m.cs) != cudaSuccess) {
err = std::string("prefill: the PLE rows' upload failed: ") + cudaGetErrorString(cudaGetLastError());
return false;
}
if (c0 + m.T < n) {
cudaEventSynchronize(m.ple_copied[ple_buf ^ 1]); // the other buffer's upload (a chunk ago) is done
// the other buffer's upload (a chunk ago) is done before the SSD thread refills it
if (cudaEventSynchronize(m.ple_copied[ple_buf ^ 1]) != cudaSuccess) {
err = std::string("prefill: the PLE rows' upload failed: ") + cudaGetErrorString(cudaGetLastError());
return false;
}
ple_next = std::async(std::launch::async, [&ple_gather, &ple_next_err, c1 = c0 + m.T, b = ple_buf ^ 1] {
return ple_gather(c1, b, ple_next_err);
});
}
ple_buf ^= 1;
stats_.ms_ple += ms_since(tp);
}
return true;
};
for (int64_t t = 0; t < T; ++t) { prev[0] = prev[1]; prev[1] = (int32_t) tokens[c0 + t]; }
// ---- the QSA step records of every position in the chunk
for (int64_t t = 0; t < T; ++t) strata::kernels::qsa_step_fill(m.steps_host.data() + t * strata::kernels::kStepCount, p0 + t, s);
Expand Down Expand Up @@ -1262,6 +1279,7 @@ bool Prefill::run(const int64_t* tokens, int64_t n, int64_t pos0, std::string& e
core::progress_beat(); // the serve watchdog: a prompt chunk of 8192 tokens is still moving
core::progress_at("reading the prompt (batched): layer", l, p0); // #251: a stall names layer and chunk
const core::LayerView v(*m.wt, l);
if (l == std::max<int64_t>(LB, 1) && !ple_land()) return false; // the PLE rows, read from layer 1 on
// ---- the PLE block at layer 1, token by token (its conv reads the previous tokens' rows)
if (l == 1 && ple_on && ple_batch) {
// the whole chunk at once, in sub-batches carved from the idle scratch region: the key and value
Expand Down Expand Up @@ -1854,6 +1872,7 @@ bool Prefill::run(const int64_t* tokens, int64_t n, int64_t pos0, std::string& e
strata::kernels::cvec_apply(m.R, l, T, D, nullptr, 0, nullptr, 0, false, m.cs);
}
}
if (!ple_land()) return false; // a stage that ends before layer 1: the rows land anyway, the next gather starts
if (issuer.joinable()) {
issuer.join();
stats_.ms_experts_host += iss_ms;
Expand Down
4 changes: 2 additions & 2 deletions src/program/generate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ struct Options {
/// Plan v0.3 P2: how the n-gram table is read. Direct (default) = unbuffered SSD reads, table never in RAM.
std::string ple_io = "direct";
int64_t ple_row_cache = 1 << 20; ///< bounded row cache (rows of 90 B); 0 disables
int ple_inflight = 64;
int ple_inflight = 256; // the prompt path reads a chunk's rows at once: 64 left the SSD half idle (32K: 303 -> 189 ms)
double ple_delay_us = 0; ///< fault injection: every row read completes no earlier than this
bool ple_sync_submit = false; ///< A/B arm: submit reads on the token thread, no I/O worker
std::string kv = "fp16"; ///< plan v0.3 P7: KV storage, fp16 (default) or int8 (half the VRAM)
Expand Down Expand Up @@ -427,7 +427,7 @@ void usage() {
" reads, the table never enters RAM or the file cache; mmap: A/B arm;\n"
" ram: mmap with the whole table locked in RAM at start (Linux/macOS)\n"
" --ple-row-cache N bounded cache of fetched rows, 90 B each (default 1048576; 0 = off)\n"
" --ple-inflight N outstanding SSD reads (default 64)\n"
" --ple-inflight N outstanding SSD reads (default 256)\n"
" --ple-delay-us U fault injection: each row read completes no earlier than U us\n"
" --ple-sync-submit A/B arm: submit table reads on the token thread (default: an I/O thread)\n"
" --kv fp16|int8 KV storage (plan v0.3 P7): int8 codes + fp16 scale per 64 values, half the\n"
Expand Down