sparkle/native/ext/pool/owned_pool.cpp
fiser_jun 4745f264b2
2026-08-04 14:34:00 +08:00

90 lines
3.9 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// [5] Route 3 Phase 1 底座C++ 拥有的池 buffer。
// 用 mx::allocator::malloc 分配 buffer、no-op deleter 建叶子 mx.arrayC++ 经 g_owned_bufs
// 持有 Buffer 句柄,进程内不释放)。因 C++ 独占持有、MLX 只读,该 buffer 永不被 MLX
// donation/迁移spike 已证),故侧区/demand 的后台异步 pread 可安全直写、消费侧读同一块。
#include "owned_pool.h"
static std::mutex g_owned_mutex;
static std::vector<mx::allocator::Buffer> g_owned_bufs;
static mx::Dtype dtype_from_str(const std::string& s) {
if (s == "uint32") return mx::uint32;
if (s == "uint16") return mx::uint16;
if (s == "uint8") return mx::uint8;
if (s == "int32") return mx::int32;
if (s == "int16") return mx::int16;
if (s == "bfloat16") return mx::bfloat16;
if (s == "float16") return mx::float16;
if (s == "float32") return mx::float32;
throw std::runtime_error("pool_owned_zeros: unsupported dtype " + s);
}
mx::array pool_owned_zeros(const std::vector<int>& shape, const std::string& dtype) {
mx::Dtype dt = dtype_from_str(dtype);
mx::Shape shp(shape.begin(), shape.end());
size_t n = 1;
for (int d : shape) n *= static_cast<size_t>(d);
size_t nbytes = n * static_cast<size_t>(mx::size_of(dt));
auto buf = mx::allocator::malloc(nbytes);
std::memset(buf.raw_ptr(), 0, nbytes);
{
std::lock_guard<std::mutex> lk(g_owned_mutex);
g_owned_bufs.push_back(buf); // C++ 持有,保证进程内不被释放
}
return mx::array(buf, shp, dt, [](mx::allocator::Buffer) {});
}
// demand 真实区落池:把一批已加载专家段 memcpy 进 owned 池行(无 MLX scatter → pool buffer 永不重绑)。
// pool_list[i]:第 i 个 key 的池数组srcs_flat 长 K*mkey-major[k0行0,k0行1,...,k1行0,...]
// slots[j]:第 j 个专家的目标物理行。CPU 端直写,调用点须保证此刻池未被 GPU 并发读该行。
void pool_write_rows(const std::vector<mx::array>& pool_list,
const std::vector<mx::array>& srcs_flat,
const std::vector<int>& slots) {
int K = static_cast<int>(pool_list.size());
int m = static_cast<int>(slots.size());
if (m == 0) return;
if (static_cast<int>(srcs_flat.size()) != K * m)
throw std::runtime_error("pool_write_rows: srcs_flat 长度须 == K*m");
for (int i = 0; i < K; ++i) {
mx::array p = pool_list[i];
p.eval();
uint8_t* base = p.data<uint8_t>();
for (int j = 0; j < m; ++j) {
mx::array s = srcs_flat[static_cast<size_t>(i) * m + j];
s.eval();
size_t nb = s.nbytes();
std::memcpy(base + static_cast<size_t>(slots[j]) * nb, s.data<uint8_t>(), nb);
}
}
}
// 同上,但源是每 key 预堆叠的 (m,*shape) 整块 stacked_list[i],按行 memcpy 到 slots[j]。
void pool_write_stacked(const std::vector<mx::array>& pool_list,
const std::vector<mx::array>& stacked_list,
const std::vector<int>& slots) {
int K = static_cast<int>(pool_list.size());
int m = static_cast<int>(slots.size());
if (m == 0) return;
for (int i = 0; i < K; ++i) {
mx::array p = pool_list[i];
p.eval();
mx::array st = stacked_list[i];
st.eval();
uint8_t* base = p.data<uint8_t>();
const uint8_t* src = st.data<uint8_t>();
size_t rownb = st.nbytes() / static_cast<size_t>(m); // stacked 为 (m,*shape) → 每行字节
for (int j = 0; j < m; ++j)
std::memcpy(base + static_cast<size_t>(slots[j]) * rownb,
src + static_cast<size_t>(j) * rownb, rownb);
}
}
// 临时诊断:返回某 mx.array 底层 buffer 的原始数据指针uintptr。用于对拍
// 「C++ 侧区 memcpy 写入的 buffer 指针」与「Python consume/verify 读到的 pool buffer 指针」
// 是否同一块——若不同即证明 MLX 在两者之间重分配了池 bufferraw 写入被落单。
uintptr_t array_data_ptr(const mx::array& a) {
mx::array b = a;
b.eval();
return reinterpret_cast<uintptr_t>(b.data<uint8_t>());
}