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
6 changes: 6 additions & 0 deletions ggml/include/ggml.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,12 @@ extern "C" {
enum ggml_op_hint {
GGML_HINT_NONE = 0,
GGML_HINT_SRC0_IS_HADAMARD = 1,
// marks the reshape that tiles the activation feature axis into [hd, nk, rep]
// for the Hadamard fold; its input must be replicated under tensor parallelism
GGML_HINT_HADAMARD_TILING = 2,
// marks a node that must materialise a full (replicated) copy of a split input,
// i.e. an all-gather across the tensor-parallel devices
GGML_HINT_META_GATHER = 3,
};

// model file types
Expand Down
440 changes: 403 additions & 37 deletions ggml/src/ggml-backend-meta.cpp

Large diffs are not rendered by default.

69 changes: 68 additions & 1 deletion src/llama-context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#include "llama.h"

#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <cmath>
#include <cstring>
#include <limits>
Expand All @@ -38,7 +41,8 @@ static void llama_verify_hadamard_graph(
const llama_hadamard_rotations & rotations,
const llama_hadamard_rotations & inverses) {
auto unwrap = [](const ggml_tensor * t) {
while (t && (t->op == GGML_OP_RESHAPE || t->op == GGML_OP_VIEW)) {
while (t && (t->op == GGML_OP_RESHAPE || t->op == GGML_OP_VIEW ||
(t->op == GGML_OP_DUP && ((const int32_t *) t->op_params)[1] == GGML_HINT_META_GATHER))) {
t = t->src[0];
}
return t;
Expand Down Expand Up @@ -101,6 +105,10 @@ static void llama_verify_hadamard_graph(
}
}

ggml_tensor * g_prism_dbg_hm_in = nullptr;
ggml_tensor * g_prism_dbg_hm_out = nullptr;
ggml_tensor * g_prism_dbg_hm_rope = nullptr;

static llm_graph_type ctx_type_to_graph_type(llama_context_type ctx_type) {
switch (ctx_type) {
case LLAMA_CONTEXT_TYPE_DEFAULT: return LLM_GRAPH_TYPE_DEFAULT;
Expand Down Expand Up @@ -2768,6 +2776,53 @@ ggml_status llama_context::graph_compute(

// fprintf(stderr, "splits: %d\n", ggml_backend_sched_get_n_splits(sched));

// prism-debug: pointer-based dump of the first Hadamard transform (robust to renames)
if (getenv("PRISM_DBG_HM_PTR") != nullptr) {
static int s_hmseq = 0;
const int seq = s_hmseq++;
const char * ddir = getenv("LLAMA_DUMP_DIR");
const ggml_tensor * pick[3] = { g_prism_dbg_hm_in, g_prism_dbg_hm_out, g_prism_dbg_hm_rope };
const char * pname[3] = { "hm_in", "hm_out", "hm_rope" };
for (int k = 0; k < 3; k++) {
ggml_tensor * t = (ggml_tensor *) pick[k];
if (t == nullptr || t->buffer == nullptr) { continue; }
std::vector<char> buf(ggml_nbytes(t));
ggml_backend_tensor_get(t, buf.data(), 0, buf.size());
char path[512];
snprintf(path, sizeof(path), "%s/%s.%d.bin", ddir ? ddir : "/tmp", pname[k], seq);
if (FILE * f = fopen(path, "wb")) { fwrite(buf.data(), 1, buf.size(), f); fclose(f); }
fprintf(stderr, "[PTRDUMP] %s ne=[%lld,%lld] nbytes=%zu\n", pname[k],
(long long) t->ne[0], (long long) t->ne[1], buf.size());
}
}

// prism-debug: dump named graph tensors so layer and tensor mode can be compared numerically
if (const char * dump_names = getenv("LLAMA_DUMP_TENSOR")) {
static int s_dump_seq = 0;
const int seq = s_dump_seq++;
const char * dir_env = getenv("LLAMA_DUMP_DIR");
const std::string dir = dir_env ? dir_env : "/tmp/llmdump";
std::stringstream ss(dump_names);
std::string nm;
while (std::getline(ss, nm, ',')) {
for (int i = 0; i < ggml_graph_n_nodes(gf); i++) {
ggml_tensor * t = ggml_graph_node(gf, i);
if (t->name == nullptr || nm != t->name) {
continue;
}
std::vector<char> buf(ggml_nbytes(t));
ggml_backend_tensor_get(t, buf.data(), 0, buf.size());
const std::string path = dir + "/" + std::string(t->name) + "." + std::to_string(seq) + ".bin";
if (FILE * f = fopen(path.c_str(), "wb")) {
fwrite(buf.data(), 1, buf.size(), f);
fclose(f);
}
fprintf(stderr, "[DUMP] %s seq=%d ne=[%lld,%lld,%lld,%lld] nbytes=%zu\n", nm.c_str(), seq,
(long long) t->ne[0], (long long) t->ne[1], (long long) t->ne[2], (long long) t->ne[3], buf.size());
}
}
}

return status;
}

Expand All @@ -2779,6 +2834,18 @@ llm_graph_cb llama_context::graph_get_cb() const {
ggml_set_name(cur, name);
}

// prism-debug: pin requested tensors as graph outputs so their buffers are not reused
if (const char * dump = getenv("LLAMA_DUMP_TENSOR")) {
std::stringstream ss(dump);
std::string nm;
while (std::getline(ss, nm, ',')) {
if (nm == cur->name) {
ggml_set_output(cur);
break;
}
}
}

// - norm may be automatically assigned to the backend of the previous layer, increasing data transfer between backends
// - force the last op of the layer on the specified backend to avoid running it on the backend of the next layer due to scheduling
// FIXME: fix in ggml_backend_sched
Expand Down
51 changes: 48 additions & 3 deletions src/llama-graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "llama-cparams.h"
#include "llama-sampler.h"

extern "C" bool ggml_backend_buffer_is_meta(ggml_backend_buffer_t buf);

#include "llama-kv-cache.h"
#include "llama-kv-cache-iswa.h"
#include "llama-kv-cache-dsa.h"
Expand All @@ -18,6 +20,9 @@

#include <cassert>
#include <cmath>

// prism: set by llama.cpp when LLAMA_SPLIT_MODE_TENSOR is active
bool g_llama_tensor_split = false;
#include <cstring>
#include <numeric>
#include <sstream>
Expand Down Expand Up @@ -1569,7 +1574,7 @@ ggml_tensor * llm_graph_context::build_lora_mm(
if (t.signs) {
cur_mm = ggml_mul(ctx0, cur_mm, t.signs);
}
cur_mm = llama_mul_mat_hadamard(ctx0, cur_mm, t.rot);
cur_mm = llama_mul_mat_hadamard(ctx0, cur_mm, t.rot, false, w->name);
hadamard_memo[memo_key] = cur_mm;
}
}
Expand Down Expand Up @@ -1599,6 +1604,22 @@ ggml_tensor * llm_graph_context::build_lora_mm(
res = ggml_add(ctx0, res, ab_cur);
}

// prism: under tensor parallelism the matmul is column-parallel (weights split on the
// output axis) producing a split activation; gather it back to a replicated activation,
// which is the form every downstream op (residual add, next matmul, Hadamard) expects.
if (g_llama_tensor_split && getenv("PRISM_NATIVE_SPLIT") == nullptr) {
if (getenv("PRISM_DBG_MM") != nullptr && w->name != nullptr) {
ggml_format_name(res, "dbg_mm_pre_%s", w->name);
ggml_set_output(res);
}
res = ggml_dup(ctx0, res);
((int32_t *) res->op_params)[1] = GGML_HINT_META_GATHER;
if (getenv("PRISM_DBG_MM") != nullptr && w->name != nullptr) {
ggml_format_name(res, "dbg_mm_post_%s", w->name);
ggml_set_output(res);
}
}

return res;
}

Expand Down Expand Up @@ -1629,7 +1650,7 @@ ggml_tensor * llm_graph_context::build_lora_mm_id(
if (t.signs) {
cur_mm = ggml_mul(ctx0, cur_mm, t.signs);
}
cur_mm = llama_mul_mat_hadamard(ctx0, cur_mm, t.rot);
cur_mm = llama_mul_mat_hadamard(ctx0, cur_mm, t.rot, false, w->name);
hadamard_memo[memo_key] = cur_mm;
}
}
Expand Down Expand Up @@ -1665,6 +1686,22 @@ ggml_tensor * llm_graph_context::build_lora_mm_id(
res = ggml_add(ctx0, res, ab_cur);
}

// prism: under tensor parallelism the matmul is column-parallel (weights split on the
// output axis) producing a split activation; gather it back to a replicated activation,
// which is the form every downstream op (residual add, next matmul, Hadamard) expects.
if (g_llama_tensor_split && getenv("PRISM_NATIVE_SPLIT") == nullptr) {
if (getenv("PRISM_DBG_MM") != nullptr && w->name != nullptr) {
ggml_format_name(res, "dbg_mm_pre_%s", w->name);
ggml_set_output(res);
}
res = ggml_dup(ctx0, res);
((int32_t *) res->op_params)[1] = GGML_HINT_META_GATHER;
if (getenv("PRISM_DBG_MM") != nullptr && w->name != nullptr) {
ggml_format_name(res, "dbg_mm_post_%s", w->name);
ggml_set_output(res);
}
}

return res;
}

Expand Down Expand Up @@ -2422,13 +2459,21 @@ ggml_tensor * llm_graph_context::build_inp_embd(ggml_tensor * tok_embd) const {
auto & cur = inps[0];

cur = ggml_get_rows(ctx0, tok_embd, inp->tokens);
cb(cur, "dbg_embed_raw", -1);

// prism: when the embedding lives on the split (meta) device it is split on its row
// axis; gather it back to replicated before the feature-mixing Hadamard inverse.
if (g_llama_tensor_split && ggml_backend_buffer_is_meta(tok_embd->buffer)) {
cur = ggml_dup(ctx0, cur);
((int32_t *) cur->op_params)[1] = GGML_HINT_META_GATHER;
}

// a Hadamard-latent embedding table stores rotated rows; restore the
// primal basis right after the lookup: h = s * (H z)
if (hadamard_inverses) {
const auto it = hadamard_inverses->find(tok_embd);
if (it != hadamard_inverses->end()) {
cur = llama_mul_mat_hadamard(ctx0, cur, it->second.rot);
cur = llama_mul_mat_hadamard(ctx0, cur, it->second.rot, /*mark_gather =*/ false, "embed");
if (it->second.signs) {
cur = ggml_mul(ctx0, cur, it->second.signs);
}
Expand Down
42 changes: 39 additions & 3 deletions src/llama-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "ggml.h" // for ggml_log_level

#include <cstring>
#include <string>
#include <type_traits>
#include <vector>
Expand Down Expand Up @@ -54,21 +55,56 @@ static inline dst_t llama_cast(src_t v) {
}
}

static inline ggml_tensor * llama_mul_mat_hadamard(
ggml_context * ctx,
// prism: true when the model runs with LLAMA_SPLIT_MODE_TENSOR; used to insert the
// activation gather in front of the feature-mixing Hadamard transform.
extern bool g_llama_tensor_split;

// prism-debug: pointers to the first Hadamard transform's input/output, for pointer-based dumping
extern ggml_tensor * g_prism_dbg_hm_in;
extern ggml_tensor * g_prism_dbg_hm_out;
extern ggml_tensor * g_prism_dbg_hm_rope;

static inline ggml_tensor * llama_mul_mat_hadamard( ggml_context * ctx,
ggml_tensor * cur,
ggml_tensor * rot) {
ggml_tensor * rot,
bool mark_gather = false,
const char * dbg_tag = nullptr) {
const auto n = rot->ne[0];

ggml_tensor * res;

char dbg_name_in[64] = {0};
char dbg_name_out[64] = {0};
const bool dbg_hm = dbg_tag != nullptr && getenv("PRISM_DBG_HM") != nullptr;
if (dbg_hm) {
snprintf(dbg_name_in, sizeof(dbg_name_in), "dbg_hm_in_%s", dbg_tag);
snprintf(dbg_name_out, sizeof(dbg_name_out), "dbg_hm_mm_%s", dbg_tag);
}

if (!ggml_is_contiguous(cur)) {
res = ggml_cont_2d(ctx, cur, n, ggml_nelements(cur)/n);
} else {
res = ggml_reshape_2d(ctx, cur, n, ggml_nelements(cur)/n);
}
if (dbg_hm) {
ggml_set_name(res, dbg_name_in);
ggml_set_output(res);
g_prism_dbg_hm_in = res;
g_prism_dbg_hm_rope = rot;
}
// prism: the rotation reshapes the feature axis into [block, nblocks]. When this is a
// weight-fold rotation (not an attention rotation) mark it so the meta backend
// replicates the activation instead of requiring a single-axis split here.
if (mark_gather) {
((int32_t *) res->op_params)[1] = GGML_HINT_HADAMARD_TILING;
}
res = ggml_mul_mat(ctx, rot, res);
ggml_mul_mat_set_hint(res, GGML_HINT_SRC0_IS_HADAMARD);
if (dbg_hm) {
ggml_set_name(res, dbg_name_out);
ggml_set_output(res);
g_prism_dbg_hm_out = res;
}
res = ggml_reshape_4d(ctx, res, cur->ne[0], cur->ne[1], cur->ne[2], cur->ne[3]);

return res;
Expand Down
Loading