Skip to content
Merged
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
28 changes: 28 additions & 0 deletions app/forward_serve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@
#include <atomic>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <fcntl.h>
#include <sys/file.h>
#include <fstream>
#include <mutex>
#include <stdexcept>
Expand Down Expand Up @@ -273,6 +276,31 @@ int run_forward_serve(int argc, char** argv) {
e.endoftext = e.tok->token_id("<|endoftext|>");

e.fwd = std::make_unique<Q4nxNpuForward>();

// Serialise NPU access across processes. The device exposes a single hardware
// context, so a second concurrent `1bit serve --device npu` fails inside
// DRM_IOCTL_AMDXDNA_CREATE_HWCTX with EINVAL and the whole serve exits 1 --
// which is what happened when several models were served at once. The
// per-process ELF runner (npu_engine_universal) has always taken this lock;
// the serve route did not, so it could not be serialised against it or
// against a sibling serve. Held for the life of the process (fd not closed),
// exactly like the runner.
if (!std::getenv("NPU_NO_DEVICE_LOCK")) {
// flock needs only a read fd, so the file stays 0644 and other users can
// still open it; O_NOFOLLOW refuses a symlink planted in /tmp.
const char* lk = "/tmp/1bit-npu-device.lock";
int lfd = ::open(lk, O_CREAT | O_RDONLY | O_NOFOLLOW | O_CLOEXEC, 0644);
if (lfd >= 0) {
if (::flock(lfd, LOCK_EX | LOCK_NB) != 0) {
std::fprintf(stderr, "[npu] waiting for the exclusive device lock (%s)...\n", lk);
if (::flock(lfd, LOCK_EX) != 0)
std::fprintf(stderr, "[npu] device lock unavailable; continuing unlocked\n");
else
std::fprintf(stderr, "[npu] device lock acquired\n");
}
}
}

xrt::device dev(0);
if (!e.fwd->init(dev, model.c_str(), kernels, /*use_elf=*/true))
throw std::runtime_error(std::string("forward init failed: ") + e.fwd->last_error());
Expand Down
28 changes: 28 additions & 0 deletions app/serve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1746,6 +1746,34 @@ int run_serve(int argc, char** argv) {
// GGUF-on-NPU (docs/npu.md, "Open"): repack the GGUF into a Q4NX model
// directory, then serve it through the model-generic forward in process.
const std::string dir = repack_gguf(o.model);
// Forward -> lane routing: if a fused-lane kernel set exists for this
// model's shape (the fast-lane artifacts layer_ctx1.elf + layer.pdi),
// serve it on the NPU fast lane (one launch per layer) instead of the
// per-op forward (~100+ launches per token). Opt-in via
// ONEBIT_NPU_LANE_DIR: the repack layout must equal the lane kernels'
// layout, and it does not yet (the repack is patched for the forward's
// per-op ELFs), so this must not fire by accident. Without it, the
// forward runs (the unchanged fallback).
{
std::string lane;
if (const char* env = std::getenv("ONEBIT_NPU_LANE_DIR")) lane = env;
if (!lane.empty() && fs::exists(fs::path(lane) / "layer_ctx1.elf")) {
if (!fs::exists(fs::path(dir) / "npu")) {
std::error_code ec;
fs::create_directory_symlink(lane, fs::path(dir) / "npu", ec);
if (ec) std::fprintf(stderr, "1bit serve: lane routing skipped (%s)\n", ec.message().c_str());
}
if (fs::exists(fs::path(dir) / "npu" / "layer_ctx1.elf")) {
std::fprintf(stderr, "1bit serve: routing %s to the NPU fast lane (fused layer) from %s\n",
o.model.c_str(), lane.c_str());
std::vector<std::string> args = {"-m", dir, "-p", std::to_string(o.port), "--host", o.host};
if (!o.alias.empty()) { args.push_back("--alias"); args.push_back(o.alias); }
std::vector<char*> av;
for (auto& s : args) av.push_back(s.data());
return run_unified(int(av.size()), av.data());
}
}
}
// Resolve the full-ELF / xclbin directory the forward loads. An empty value
// makes it look for "/full_i8_*.elf", so it is an error, not a default.
std::string kernels;
Expand Down
2 changes: 1 addition & 1 deletion npu/forward/include/q4nx_forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class Q4nxNpuForward {
std::vector<float> w_kb_, w_vb_; // [NH][N][K], N padded for k_b
int kb_rows_ = 0; // k_b's padded row count (256)
std::vector<std::vector<float>> mla_kn_, mla_kr_, mla_v_; // per-layer MLA caches
std::vector<float> moe_router_, moe_router_b_, share_g_, share_u_, share_d_;
std::vector<float> moe_router_, moe_router_b_, share_g_, share_u_, share_d_, share_router_;
std::vector<float> dense_g_, dense_u_, dense_d_; // leading dense layer only
// Routed experts stay packed until selected: per layer the Q4NX pools them as
// [n_expert][tiles][5120].
Expand Down
Loading
Loading