perf: accelerate longhaul prompt loading

This commit is contained in:
Owen Qwen
2026-07-29 12:44:50 -05:00
parent c2baa66534
commit fdd9fcf85e
7 changed files with 525 additions and 55 deletions
+242 -49
View File
@@ -14,94 +14,279 @@ llama_longhaul_cache::llama_longhaul_cache(
uint32_t n_layers) :
files(std::move(files)),
sources(std::move(sources)),
sources_by_layer(n_layers),
layers(n_layers),
n_slots(n_slots),
n_experts(n_experts) {
for (auto & file : this->files) {
file->set_no_cache();
}
for (const auto & source : this->sources) {
sources_by_layer.at(source.layer).push_back(&source);
}
for (auto & layer : layers) {
layer.expert_ids.assign(n_slots, -1);
layer.expert_slots.assign(n_experts, -1);
layer.last_used.assign(n_slots, 0);
}
requested.resize(n_experts);
requested_experts.reserve(n_slots);
missing_experts.reserve(n_slots);
available_slots.reserve(n_slots);
load_plan.reserve(n_slots);
id_buffer.reserve(n_slots);
const unsigned int n_threads = std::max(1u, std::min(4u, std::thread::hardware_concurrency()));
io_workers.reserve(n_threads);
for (unsigned int i = 0; i < n_threads; ++i) {
io_workers.emplace_back(&llama_longhaul_cache::io_worker, this);
}
}
llama_longhaul_cache::~llama_longhaul_cache() {
LLAMA_LOG_INFO("%s: hits = %" PRIu64 ", misses = %" PRIu64 ", read = %.2f MiB\n",
__func__, n_hits, n_misses, bytes_read / 1024.0 / 1024.0);
{
std::lock_guard<std::mutex> lock(io_mutex);
io_stopping = true;
}
io_ready.notify_all();
for (auto & worker : io_workers) {
worker.join();
}
LLAMA_LOG_INFO(
"%s: batches = %" PRIu64 ", ids = %" PRIu64 ", unique = %" PRIu64
", duplicates = %" PRIu64 ", hits = %" PRIu64 ", misses = %" PRIu64
", read = %.2f MiB, io = %.2f ms, remap = %.2f ms\n",
__func__, n_batches, n_ids, n_unique, n_duplicates, n_hits, n_misses,
bytes_read / 1024.0 / 1024.0, io_wall_us / 1000.0, remap_us / 1000.0);
}
int llama_longhaul_cache::find_slot(int layer, int32_t expert_id) {
auto & state = layers.at(layer);
for (size_t i = 0; i < n_slots; ++i) {
if (state.expert_ids[i] == expert_id) {
state.last_used[i] = ++tick;
++n_hits;
return i;
}
bool llama_longhaul_cache::source_is_direct(
const llama_model_loader::longhaul_source & source) const {
if (ggml_backend_buffer_is_host(source.tensor->buffer)) {
return true;
}
size_t slot = 0;
for (size_t i = 0; i < n_slots; ++i) {
if (state.expert_ids[i] == -1) {
slot = i;
break;
}
if (state.last_used[i] < state.last_used[slot]) {
slot = i;
}
}
if (!load_slot(layer, slot, expert_id)) {
return -1;
}
state.expert_ids[slot] = expert_id;
state.last_used[slot] = ++tick;
++n_misses;
return slot;
const char * name = ggml_backend_buft_name(ggml_backend_buffer_get_type(source.tensor->buffer));
return name != nullptr &&
std::strncmp(name, "MTL", 3) == 0 &&
std::strstr(name, "_Private") == nullptr;
}
bool llama_longhaul_cache::load_slot(int layer, int slot, int32_t expert_id) {
try {
std::vector<uint8_t> buffer;
for (const auto & source : sources) {
if (source.layer != layer) {
void llama_longhaul_cache::io_worker() {
while (true) {
io_job * job = nullptr;
{
std::unique_lock<std::mutex> lock(io_mutex);
io_ready.wait(lock, [&] { return io_stopping || !io_queue.empty(); });
if (io_stopping && io_queue.empty()) {
return;
}
job = io_queue.front();
io_queue.pop_front();
}
try {
const auto & source = *job->source;
void * destination =
static_cast<uint8_t *>(source.tensor->data) + size_t(job->slot) * source.tensor->nb[2];
files.at(source.file_idx)->read_at(
destination, source.expert_size,
source.offset + size_t(job->expert_id) * source.expert_size);
job->ok = true;
} catch (const std::exception & e) {
job->error = e.what();
}
{
std::lock_guard<std::mutex> lock(io_mutex);
--io_pending;
}
io_done.notify_one();
}
}
bool llama_longhaul_cache::load_plan_sources() {
const size_t n_direct_sources = std::count_if(
sources_by_layer.at(locked_layer).begin(),
sources_by_layer.at(locked_layer).end(),
[&](const auto * source) { return source_is_direct(*source); });
const size_t n_jobs = load_plan.size() * n_direct_sources;
io_jobs.clear();
io_jobs.reserve(n_jobs);
for (const auto & item : load_plan) {
for (const auto * source : sources_by_layer.at(locked_layer)) {
if (source_is_direct(*source)) {
io_jobs.push_back({ source, item.first, item.second, false, {} });
}
}
}
if (!io_jobs.empty()) {
std::lock_guard<std::mutex> lock(io_mutex);
io_pending = io_jobs.size();
for (auto & job : io_jobs) {
io_queue.push_back(&job);
}
io_ready.notify_all();
}
bool staged_ok = true;
for (const auto & item : load_plan) {
const int32_t expert_id = item.first;
const int slot = item.second;
for (const auto * source : sources_by_layer.at(locked_layer)) {
if (source_is_direct(*source)) {
continue;
}
buffer.resize(source.expert_size);
files.at(source.file_idx)->read_at(
buffer.data(), buffer.size(), source.offset + size_t(expert_id) * source.expert_size);
ggml_backend_tensor_set(source.tensor, buffer.data(), size_t(slot) * source.tensor->nb[2], buffer.size());
bytes_read += buffer.size();
read_buffer.resize(source->expert_size);
try {
files.at(source->file_idx)->read_at(
read_buffer.data(), read_buffer.size(),
source->offset + size_t(expert_id) * source->expert_size);
ggml_backend_tensor_set(
source->tensor, read_buffer.data(),
size_t(slot) * source->tensor->nb[2], read_buffer.size());
bytes_read += read_buffer.size();
} catch (const std::exception & e) {
last_error = e.what();
staged_ok = false;
break;
}
}
return true;
} catch (const std::exception & e) {
last_error = e.what();
return false;
if (!staged_ok) {
break;
}
}
if (!io_jobs.empty()) {
std::unique_lock<std::mutex> lock(io_mutex);
io_done.wait(lock, [&] { return io_pending == 0; });
}
for (const auto & job : io_jobs) {
if (!job.ok) {
last_error = job.error;
return false;
}
bytes_read += job.source->expert_size;
}
return staged_ok;
}
void llama_longhaul_cache::invalidate_plan(int layer) {
auto & state = layers.at(layer);
for (const auto & item : load_plan) {
const int slot = item.second;
const int32_t old_expert = state.expert_ids.at(slot);
if (old_expert >= 0) {
state.expert_slots.at(old_expert) = -1;
}
state.expert_ids.at(slot) = -1;
state.last_used.at(slot) = 0;
}
}
bool llama_longhaul_cache::remap(int layer, ggml_tensor * ids) {
const int64_t t_start_us = ggml_time_us();
last_error.clear();
mutex.lock();
locked = true;
locked_layer = layer;
std::vector<int32_t> values(ggml_nbytes(ids) / sizeof(int32_t));
ggml_backend_tensor_get(ids, values.data(), 0, ggml_nbytes(ids));
for (int32_t & id : values) {
const size_t n_values = ggml_nbytes(ids) / sizeof(int32_t);
id_buffer.resize(n_values);
ggml_backend_tensor_get(ids, id_buffer.data(), 0, ggml_nbytes(ids));
std::fill(requested.begin(), requested.end(), 0);
requested_experts.clear();
missing_experts.clear();
available_slots.clear();
load_plan.clear();
auto & state = layers.at(layer);
for (const int32_t id : id_buffer) {
if (id < 0 || id >= (int32_t) n_experts) {
last_error = "router produced an out-of-range expert ID";
release(layer);
return false;
}
const int slot = find_slot(layer, id);
if (slot < 0) {
release(layer);
return false;
if (!requested.at(id)) {
requested.at(id) = 1;
requested_experts.push_back(id);
if (state.expert_slots.at(id) < 0) {
missing_experts.push_back(id);
}
}
}
if (requested_experts.size() > n_slots) {
last_error = "router requested more unique experts than the longhaul cache can hold";
release(layer);
return false;
}
for (size_t slot = 0; slot < n_slots; ++slot) {
const int32_t expert_id = state.expert_ids.at(slot);
if (expert_id < 0 || !requested.at(expert_id)) {
available_slots.push_back(slot);
}
}
std::stable_sort(available_slots.begin(), available_slots.end(), [&](int32_t a, int32_t b) {
const bool a_empty = state.expert_ids.at(a) < 0;
const bool b_empty = state.expert_ids.at(b) < 0;
if (a_empty != b_empty) {
return a_empty;
}
return state.last_used.at(a) < state.last_used.at(b);
});
if (missing_experts.size() > available_slots.size()) {
last_error = "longhaul could not reserve enough cache slots for the routed expert batch";
release(layer);
return false;
}
for (size_t i = 0; i < missing_experts.size(); ++i) {
load_plan.emplace_back(missing_experts.at(i), available_slots.at(i));
}
const int64_t io_start_us = ggml_time_us();
if (!load_plan_sources()) {
invalidate_plan(layer);
release(layer);
return false;
}
io_wall_us += ggml_time_us() - io_start_us;
for (const auto & item : load_plan) {
const int32_t expert_id = item.first;
const int slot = item.second;
const int32_t old_expert = state.expert_ids.at(slot);
if (old_expert >= 0) {
state.expert_slots.at(old_expert) = -1;
}
state.expert_ids.at(slot) = expert_id;
state.expert_slots.at(expert_id) = slot;
}
for (int32_t & id : id_buffer) {
const int slot = state.expert_slots.at(id);
GGML_ASSERT(slot >= 0);
state.last_used.at(slot) = ++tick;
id = slot;
}
ggml_backend_tensor_set(ids, values.data(), 0, ggml_nbytes(ids));
ggml_backend_tensor_set(ids, id_buffer.data(), 0, ggml_nbytes(ids));
++n_batches;
n_ids += n_values;
n_unique += requested_experts.size();
n_duplicates += n_values - requested_experts.size();
n_misses += missing_experts.size();
n_hits += n_values - missing_experts.size();
remap_us += ggml_time_us() - t_start_us;
return true;
}
@@ -128,3 +313,11 @@ const std::string & llama_longhaul_cache::error() const {
bool llama_longhaul_cache::failed() const {
return !last_error.empty();
}
uint64_t llama_longhaul_cache::misses() const {
return n_misses;
}
uint64_t llama_longhaul_cache::bytes_read_count() const {
return bytes_read;
}