426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
568 lines
21 KiB
C++
568 lines
21 KiB
C++
// Vulkan graph-dispatch smoke for the shipped Eliza-1 attention-score ops.
|
|
//
|
|
// This is intentionally not a standalone SPIR-V test. It links against the
|
|
// patched fork's libggml-vulkan and drives real GGML graphs containing:
|
|
// - GGML_OP_ATTN_SCORE_QJL
|
|
// - GGML_OP_ATTN_SCORE_TBQ (TBQ3, TBQ4, TBQ3_TCQ)
|
|
// - GGML_OP_ATTN_SCORE_POLAR (use_qjl=0 and use_qjl=1)
|
|
//
|
|
// PASS means ggml-vulkan advertises support for the graph op, selected the
|
|
// shipped eliza Vulkan pipeline, and the numeric output matches the reference.
|
|
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "ggml.h"
|
|
#include "ggml-alloc.h"
|
|
#include "ggml-backend.h"
|
|
#include "ggml-vulkan.h"
|
|
|
|
extern "C" {
|
|
#include "../reference/turbo_kernels.h"
|
|
#include "qjl_polar_ref.h"
|
|
}
|
|
|
|
namespace {
|
|
|
|
constexpr int HEAD_DIM = 128;
|
|
constexpr int QJL_PROJ_DIM = 256;
|
|
constexpr int N_HEADS = 4;
|
|
constexpr int N_KV_HEADS = 2;
|
|
constexpr int N_TOKENS = 8;
|
|
constexpr float TOL = 1e-3f;
|
|
|
|
static std::string lower_ascii(const char * s) {
|
|
std::string out = s ? s : "";
|
|
for (char & c : out) {
|
|
if (c >= 'A' && c <= 'Z') c = (char) (c - 'A' + 'a');
|
|
}
|
|
return out;
|
|
}
|
|
|
|
static bool software_vulkan_allowed() {
|
|
const char * value = std::getenv("ELIZA_ALLOW_SOFTWARE_VULKAN");
|
|
return value && std::strcmp(value, "1") == 0;
|
|
}
|
|
|
|
static bool looks_like_software_vulkan_device(const char * name) {
|
|
const std::string device = lower_ascii(name);
|
|
return device.find("llvmpipe") != std::string::npos ||
|
|
device.find("lavapipe") != std::string::npos ||
|
|
device.find("swiftshader") != std::string::npos ||
|
|
device.find("software rasterizer") != std::string::npos;
|
|
}
|
|
|
|
struct block_qjl1_256_smoke {
|
|
uint8_t qs[ELIZA_QJL_PACKED_BYTES];
|
|
uint16_t norm_bf16;
|
|
};
|
|
|
|
static float bf16_to_f32(uint16_t v) {
|
|
uint32_t u = ((uint32_t) v) << 16;
|
|
float out;
|
|
std::memcpy(&out, &u, sizeof(out));
|
|
return out;
|
|
}
|
|
|
|
static void fill_k_rows(std::vector<float> & k_rows) {
|
|
for (int row = 0; row < N_TOKENS * N_KV_HEADS; ++row) {
|
|
for (int i = 0; i < HEAD_DIM; ++i) {
|
|
k_rows[row * HEAD_DIM + i] =
|
|
0.6f * std::sin(0.017f * (float) (row * HEAD_DIM + i)) +
|
|
0.2f * std::cos(0.071f * (float) (i + 3 * row));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void fill_q_heads(std::vector<float> & q_heads) {
|
|
for (int h = 0; h < N_HEADS; ++h) {
|
|
for (int i = 0; i < HEAD_DIM; ++i) {
|
|
q_heads[h * HEAD_DIM + i] =
|
|
std::cos(0.031f * (float) (h * HEAD_DIM + i)) -
|
|
0.3f * std::sin(0.047f * (float) i);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void fill_qjl_sketch(std::vector<float> & q_sketch) {
|
|
for (int h = 0; h < N_HEADS; ++h) {
|
|
for (int j = 0; j < QJL_PROJ_DIM; ++j) {
|
|
q_sketch[h * QJL_PROJ_DIM + j] =
|
|
std::cos(0.031f * (float) (h * QJL_PROJ_DIM + j)) -
|
|
0.3f * std::sin(0.047f * (float) j);
|
|
}
|
|
}
|
|
}
|
|
|
|
static float qjl_ref_score(
|
|
const float * q_sketch,
|
|
const block_qjl1_256_smoke * blocks,
|
|
int h_q,
|
|
int token) {
|
|
const int gqa = N_HEADS / N_KV_HEADS;
|
|
const int h_k = h_q / gqa;
|
|
const block_qjl1_256_smoke * blk = blocks + h_k * N_TOKENS + token;
|
|
const float * q = q_sketch + h_q * QJL_PROJ_DIM;
|
|
|
|
float acc = 0.0f;
|
|
for (int j = 0; j < QJL_PROJ_DIM; ++j) {
|
|
const uint8_t bits = blk->qs[j >> 3];
|
|
const bool sign = ((bits >> (j & 7)) & 1u) != 0;
|
|
acc += sign ? q[j] : -q[j];
|
|
}
|
|
constexpr float scale = 1.2533141373155003f / (float) QJL_PROJ_DIM;
|
|
return scale * bf16_to_f32(blk->norm_bf16) * acc;
|
|
}
|
|
|
|
static bool check_scores(
|
|
const char * label,
|
|
const std::vector<float> & got,
|
|
const std::vector<float> & expected,
|
|
float * max_err_out) {
|
|
float max_err = 0.0f;
|
|
for (int h = 0; h < N_HEADS; ++h) {
|
|
for (int t = 0; t < N_TOKENS; ++t) {
|
|
const int idx = h * N_TOKENS + t;
|
|
const float err = std::fabs(expected[idx] - got[idx]);
|
|
if (!std::isfinite(got[idx]) || err > TOL) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] %s FAIL h=%d t=%d expected=%+.6f got=%+.6f diff=%.3e\n",
|
|
label, h, t, expected[idx], got[idx], err);
|
|
return false;
|
|
}
|
|
if (err > max_err) max_err = err;
|
|
}
|
|
}
|
|
*max_err_out = max_err;
|
|
return true;
|
|
}
|
|
|
|
static bool compute_graph(
|
|
ggml_context * ctx,
|
|
ggml_cgraph * gf,
|
|
ggml_tensor * q,
|
|
const void * q_data,
|
|
size_t q_bytes,
|
|
ggml_tensor * pk,
|
|
const void * pk_data,
|
|
size_t pk_bytes,
|
|
ggml_tensor * scores,
|
|
std::vector<float> & got) {
|
|
ggml_backend_t backend = ggml_backend_vk_init(0);
|
|
if (!backend) {
|
|
std::fprintf(stderr, "[vulkan_dispatch_smoke] ggml_backend_vk_init failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!ggml_backend_supports_op(backend, scores)) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] ggml-vulkan does not advertise support for %s with packed K type=%d\n",
|
|
ggml_op_name(scores->op), (int) pk->type);
|
|
ggml_backend_free(backend);
|
|
return false;
|
|
}
|
|
|
|
ggml_backend_buffer_t buf = ggml_backend_alloc_ctx_tensors(ctx, backend);
|
|
if (!buf) {
|
|
std::fprintf(stderr, "[vulkan_dispatch_smoke] alloc_ctx_tensors failed\n");
|
|
ggml_backend_free(backend);
|
|
return false;
|
|
}
|
|
|
|
ggml_backend_tensor_set(q, q_data, 0, q_bytes);
|
|
ggml_backend_tensor_set(pk, pk_data, 0, pk_bytes);
|
|
|
|
const ggml_status status = ggml_backend_graph_compute(backend, gf);
|
|
if (status != GGML_STATUS_SUCCESS) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] graph_compute returned status=%d\n",
|
|
(int) status);
|
|
ggml_backend_buffer_free(buf);
|
|
ggml_backend_free(backend);
|
|
return false;
|
|
}
|
|
|
|
got.assign(N_HEADS * N_TOKENS, 0.0f);
|
|
ggml_backend_tensor_get(scores, got.data(), 0, got.size() * sizeof(float));
|
|
|
|
ggml_backend_buffer_free(buf);
|
|
ggml_backend_free(backend);
|
|
return true;
|
|
}
|
|
|
|
static bool run_qjl_smoke(float * max_err_out) {
|
|
const size_t row_size = ggml_row_size(GGML_TYPE_QJL1_256, HEAD_DIM);
|
|
if (row_size != sizeof(block_qjl1_256_smoke)) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] QJL row size mismatch: ggml=%zu local=%zu\n",
|
|
row_size, sizeof(block_qjl1_256_smoke));
|
|
return false;
|
|
}
|
|
|
|
std::vector<float> k_rows(N_TOKENS * N_KV_HEADS * HEAD_DIM);
|
|
std::vector<float> q_sketch(N_HEADS * QJL_PROJ_DIM);
|
|
fill_k_rows(k_rows);
|
|
fill_qjl_sketch(q_sketch);
|
|
|
|
std::vector<uint8_t> packed(row_size * N_TOKENS * N_KV_HEADS);
|
|
const size_t written = ggml_quantize_chunk(
|
|
GGML_TYPE_QJL1_256,
|
|
k_rows.data(),
|
|
packed.data(),
|
|
/*start=*/0,
|
|
/*nrows=*/N_TOKENS * N_KV_HEADS,
|
|
/*n_per_row=*/HEAD_DIM,
|
|
/*imatrix=*/nullptr);
|
|
if (written != packed.size()) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] ggml_quantize_chunk(QJL) wrote %zu bytes, expected %zu\n",
|
|
written, packed.size());
|
|
return false;
|
|
}
|
|
|
|
std::vector<float> expected(N_HEADS * N_TOKENS);
|
|
const auto * blocks = reinterpret_cast<const block_qjl1_256_smoke *>(packed.data());
|
|
for (int h = 0; h < N_HEADS; ++h) {
|
|
for (int t = 0; t < N_TOKENS; ++t) {
|
|
expected[h * N_TOKENS + t] = qjl_ref_score(q_sketch.data(), blocks, h, t);
|
|
}
|
|
}
|
|
|
|
ggml_context * ctx = ggml_init({ 16 * 1024 * 1024, nullptr, true });
|
|
if (!ctx) return false;
|
|
ggml_tensor * q = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, QJL_PROJ_DIM, N_HEADS, 1, 1);
|
|
ggml_tensor * pk = ggml_new_tensor_4d(ctx, GGML_TYPE_QJL1_256, HEAD_DIM, N_TOKENS, N_KV_HEADS, 1);
|
|
ggml_tensor * scores = ggml_attn_score_qjl(ctx, q, pk, N_KV_HEADS);
|
|
ggml_cgraph * gf = ggml_new_graph(ctx);
|
|
ggml_build_forward_expand(gf, scores);
|
|
|
|
std::vector<float> got;
|
|
const bool ok = compute_graph(ctx, gf, q, q_sketch.data(), q_sketch.size() * sizeof(float),
|
|
pk, packed.data(), packed.size(), scores, got) &&
|
|
check_scores("QJL", got, expected, max_err_out);
|
|
ggml_free(ctx);
|
|
return ok;
|
|
}
|
|
|
|
template <typename Block>
|
|
static bool run_tbq_smoke(
|
|
const char * label,
|
|
ggml_type type,
|
|
void (*quantize)(const float *, Block *),
|
|
float (*dot)(const float *, const Block *),
|
|
int blocks_per_row,
|
|
float * max_err_out) {
|
|
const size_t row_size = ggml_row_size(type, HEAD_DIM);
|
|
if (row_size != sizeof(Block) * (size_t) blocks_per_row) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] %s row size mismatch: ggml=%zu local=%zu\n",
|
|
label, row_size, sizeof(Block) * (size_t) blocks_per_row);
|
|
return false;
|
|
}
|
|
|
|
std::vector<float> k_rows(N_TOKENS * N_KV_HEADS * HEAD_DIM);
|
|
std::vector<float> q_heads(N_HEADS * HEAD_DIM);
|
|
fill_k_rows(k_rows);
|
|
fill_q_heads(q_heads);
|
|
|
|
std::vector<Block> blocks(N_TOKENS * N_KV_HEADS * blocks_per_row);
|
|
for (int row = 0; row < N_TOKENS * N_KV_HEADS; ++row) {
|
|
quantize(k_rows.data() + row * HEAD_DIM, blocks.data() + row * blocks_per_row);
|
|
}
|
|
|
|
std::vector<float> expected(N_HEADS * N_TOKENS);
|
|
const int gqa = N_HEADS / N_KV_HEADS;
|
|
for (int h = 0; h < N_HEADS; ++h) {
|
|
const int h_k = h / gqa;
|
|
for (int t = 0; t < N_TOKENS; ++t) {
|
|
expected[h * N_TOKENS + t] =
|
|
dot(q_heads.data() + h * HEAD_DIM,
|
|
blocks.data() + (h_k * N_TOKENS + t) * blocks_per_row);
|
|
}
|
|
}
|
|
|
|
ggml_context * ctx = ggml_init({ 16 * 1024 * 1024, nullptr, true });
|
|
if (!ctx) return false;
|
|
ggml_tensor * q = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, HEAD_DIM, N_HEADS, 1, 1);
|
|
ggml_tensor * pk = ggml_new_tensor_4d(ctx, type, HEAD_DIM, N_TOKENS, N_KV_HEADS, 1);
|
|
ggml_tensor * scores = ggml_attn_score_tbq(ctx, q, pk, N_KV_HEADS);
|
|
ggml_cgraph * gf = ggml_new_graph(ctx);
|
|
ggml_build_forward_expand(gf, scores);
|
|
|
|
std::vector<float> got;
|
|
const bool ok = compute_graph(ctx, gf, q, q_heads.data(), q_heads.size() * sizeof(float),
|
|
pk, blocks.data(), blocks.size() * sizeof(Block), scores, got) &&
|
|
check_scores(label, got, expected, max_err_out);
|
|
ggml_free(ctx);
|
|
return ok;
|
|
}
|
|
|
|
static void quantize_turbo3_adapter(const float * src, eliza_block_turbo3_0 * dst) {
|
|
eliza_quantize_turbo3_group(src, dst);
|
|
}
|
|
|
|
static float dot_turbo3_adapter(const float * q, const eliza_block_turbo3_0 * k) {
|
|
return eliza_dot_q_turbo3(q, k);
|
|
}
|
|
|
|
static void quantize_turbo4_adapter(const float * src, eliza_block_turbo4_0 * dst) {
|
|
eliza_quantize_turbo4_block(src, dst);
|
|
}
|
|
|
|
static float dot_turbo4_adapter(const float * q, const eliza_block_turbo4_0 * k) {
|
|
return eliza_dot_q_turbo4(q, k);
|
|
}
|
|
|
|
static void quantize_turbo3_tcq_adapter(const float * src, eliza_block_turbo3_tcq * dst) {
|
|
eliza_quantize_turbo3_tcq_block(src, dst);
|
|
}
|
|
|
|
static float dot_turbo3_tcq_adapter(const float * q, const eliza_block_turbo3_tcq * k) {
|
|
return eliza_dot_q_turbo3_tcq(q, k);
|
|
}
|
|
|
|
static bool run_polar_smoke(bool use_qjl, float * max_err_out) {
|
|
const char * label = use_qjl ? "PolarQuant(use_qjl=1)" : "PolarQuant(use_qjl=0)";
|
|
const size_t row_size = ggml_row_size(GGML_TYPE_Q4_POLAR, HEAD_DIM);
|
|
if (row_size != sizeof(eliza_block_q4_polar)) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] %s row size mismatch: ggml=%zu local=%zu\n",
|
|
label, row_size, sizeof(eliza_block_q4_polar));
|
|
return false;
|
|
}
|
|
|
|
std::vector<float> k_rows(N_TOKENS * N_KV_HEADS * HEAD_DIM);
|
|
std::vector<float> q_heads(N_HEADS * HEAD_DIM);
|
|
fill_k_rows(k_rows);
|
|
fill_q_heads(q_heads);
|
|
|
|
std::vector<eliza_block_q4_polar> blocks(N_TOKENS * N_KV_HEADS);
|
|
for (int row = 0; row < N_TOKENS * N_KV_HEADS; ++row) {
|
|
eliza_polar_quantize_row(
|
|
k_rows.data() + row * HEAD_DIM,
|
|
blocks.data() + row,
|
|
HEAD_DIM,
|
|
use_qjl ? 1 : 0);
|
|
}
|
|
|
|
std::vector<float> expected(N_HEADS * N_TOKENS);
|
|
const int gqa = N_HEADS / N_KV_HEADS;
|
|
for (int h = 0; h < N_HEADS; ++h) {
|
|
const int h_k = h / gqa;
|
|
for (int t = 0; t < N_TOKENS; ++t) {
|
|
eliza_polar_mul_mv(
|
|
blocks.data() + h_k * N_TOKENS + t,
|
|
q_heads.data() + h * HEAD_DIM,
|
|
1,
|
|
use_qjl ? 1 : 0,
|
|
expected.data() + h * N_TOKENS + t);
|
|
}
|
|
}
|
|
|
|
ggml_context * ctx = ggml_init({ 16 * 1024 * 1024, nullptr, true });
|
|
if (!ctx) return false;
|
|
ggml_tensor * q = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, HEAD_DIM, N_HEADS, 1, 1);
|
|
ggml_tensor * pk = ggml_new_tensor_4d(ctx, GGML_TYPE_Q4_POLAR, HEAD_DIM, N_TOKENS, N_KV_HEADS, 1);
|
|
ggml_tensor * scores = ggml_attn_score_polar(ctx, q, pk, N_KV_HEADS, use_qjl);
|
|
ggml_cgraph * gf = ggml_new_graph(ctx);
|
|
ggml_build_forward_expand(gf, scores);
|
|
|
|
std::vector<float> got;
|
|
const bool ok = compute_graph(ctx, gf, q, q_heads.data(), q_heads.size() * sizeof(float),
|
|
pk, blocks.data(), blocks.size() * sizeof(eliza_block_q4_polar), scores, got) &&
|
|
check_scores(label, got, expected, max_err_out);
|
|
ggml_free(ctx);
|
|
return ok;
|
|
}
|
|
|
|
// GGML_OP_FUSED_ATTN_QJL_TBQ — fused QJL-K score + TBQ3-V mix, online softmax.
|
|
// Numeric comparison against eliza_fused_attn_qjl_tbq3() (the backend-neutral C
|
|
// reference; bit-exact to the fork's CPU op). Output is [head_dim=128, n_heads]
|
|
// fp32 for q_pos = 0. Self-contained graph build (compute_graph above is
|
|
// score-shaped); mirrors the same backend-init / supports-op / compute flow.
|
|
static bool run_fused_attn_smoke(float * max_err_out) {
|
|
constexpr int PROJ_DIM = ELIZA_QJL_PROJECTION_DIM; // 256
|
|
static_assert(PROJ_DIM == QJL_PROJ_DIM, "QJL sketch dim mismatch");
|
|
const float sm_scale = 1.0f / std::sqrt((float) HEAD_DIM);
|
|
|
|
const size_t k_row_size = ggml_row_size(GGML_TYPE_QJL1_256, HEAD_DIM);
|
|
const size_t v_row_size = ggml_row_size(GGML_TYPE_TBQ3_0, HEAD_DIM);
|
|
if (k_row_size != sizeof(eliza_block_qjl1_256) ||
|
|
v_row_size != sizeof(eliza_block_tbq3_0) * ELIZA_FUSED_TBQ_PER_TOKEN) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] FusedAttn row-size mismatch: k ggml=%zu local=%zu, v ggml=%zu local=%zu*%d\n",
|
|
k_row_size, sizeof(eliza_block_qjl1_256), v_row_size,
|
|
sizeof(eliza_block_tbq3_0), ELIZA_FUSED_TBQ_PER_TOKEN);
|
|
return false;
|
|
}
|
|
|
|
std::vector<float> k_rows(N_TOKENS * N_KV_HEADS * HEAD_DIM);
|
|
std::vector<float> v_rows(N_TOKENS * N_KV_HEADS * HEAD_DIM);
|
|
std::vector<float> q_sketch(N_HEADS * PROJ_DIM);
|
|
fill_k_rows(k_rows);
|
|
for (int row = 0; row < N_TOKENS * N_KV_HEADS; ++row) {
|
|
for (int i = 0; i < HEAD_DIM; ++i) {
|
|
v_rows[row * HEAD_DIM + i] =
|
|
0.45f * std::cos(0.013f * (float) (row * HEAD_DIM + i)) -
|
|
0.25f * std::sin(0.059f * (float) (i + 5 * row));
|
|
}
|
|
}
|
|
for (int h = 0; h < N_HEADS; ++h) {
|
|
for (int j = 0; j < PROJ_DIM; ++j) {
|
|
q_sketch[h * PROJ_DIM + j] =
|
|
std::cos(0.021f * (float) (h * PROJ_DIM + j)) -
|
|
0.27f * std::sin(0.043f * (float) j);
|
|
}
|
|
}
|
|
|
|
std::vector<uint8_t> packed_k(k_row_size * N_TOKENS * N_KV_HEADS);
|
|
std::vector<uint8_t> packed_v(v_row_size * N_TOKENS * N_KV_HEADS);
|
|
const size_t wk = ggml_quantize_chunk(GGML_TYPE_QJL1_256, k_rows.data(), packed_k.data(),
|
|
0, N_TOKENS * N_KV_HEADS, HEAD_DIM, nullptr);
|
|
const size_t wv = ggml_quantize_chunk(GGML_TYPE_TBQ3_0, v_rows.data(), packed_v.data(),
|
|
0, N_TOKENS * N_KV_HEADS, HEAD_DIM, nullptr);
|
|
if (wk != packed_k.size() || wv != packed_v.size()) {
|
|
std::fprintf(stderr, "[vulkan_dispatch_smoke] FusedAttn quantize wrote k=%zu/%zu v=%zu/%zu\n",
|
|
wk, packed_k.size(), wv, packed_v.size());
|
|
return false;
|
|
}
|
|
|
|
std::vector<float> expected(N_HEADS * HEAD_DIM);
|
|
eliza_fused_attn_qjl_tbq3(
|
|
q_sketch.data(),
|
|
reinterpret_cast<const eliza_block_qjl1_256 *>(packed_k.data()),
|
|
reinterpret_cast<const eliza_block_tbq3_0 *>(packed_v.data()),
|
|
N_HEADS, N_KV_HEADS, N_TOKENS, sm_scale, expected.data());
|
|
|
|
ggml_context * ctx = ggml_init({ 16 * 1024 * 1024, nullptr, true });
|
|
if (!ctx) return false;
|
|
ggml_tensor * q = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, PROJ_DIM, N_HEADS, 1, 1);
|
|
ggml_tensor * pk = ggml_new_tensor_4d(ctx, GGML_TYPE_QJL1_256, HEAD_DIM, N_TOKENS, N_KV_HEADS, 1);
|
|
ggml_tensor * pv = ggml_new_tensor_4d(ctx, GGML_TYPE_TBQ3_0, HEAD_DIM, N_TOKENS, N_KV_HEADS, 1);
|
|
ggml_tensor * out = ggml_fused_attn_qjl_tbq(ctx, q, pk, pv, N_KV_HEADS, sm_scale);
|
|
ggml_cgraph * gf = ggml_new_graph(ctx);
|
|
ggml_build_forward_expand(gf, out);
|
|
|
|
ggml_backend_t backend = ggml_backend_vk_init(0);
|
|
if (!backend) { ggml_free(ctx); return false; }
|
|
if (!ggml_backend_supports_op(backend, out)) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] ggml-vulkan does not advertise support for GGML_OP_FUSED_ATTN_QJL_TBQ\n");
|
|
ggml_backend_free(backend);
|
|
ggml_free(ctx);
|
|
return false;
|
|
}
|
|
ggml_backend_buffer_t buf = ggml_backend_alloc_ctx_tensors(ctx, backend);
|
|
if (!buf) { ggml_backend_free(backend); ggml_free(ctx); return false; }
|
|
ggml_backend_tensor_set(q, q_sketch.data(), 0, q_sketch.size() * sizeof(float));
|
|
ggml_backend_tensor_set(pk, packed_k.data(), 0, packed_k.size());
|
|
ggml_backend_tensor_set(pv, packed_v.data(), 0, packed_v.size());
|
|
const ggml_status st = ggml_backend_graph_compute(backend, gf);
|
|
bool ok = st == GGML_STATUS_SUCCESS;
|
|
if (!ok) {
|
|
std::fprintf(stderr, "[vulkan_dispatch_smoke] FusedAttn graph_compute status=%d\n", (int) st);
|
|
}
|
|
std::vector<float> got(N_HEADS * HEAD_DIM, 0.0f);
|
|
if (ok) ggml_backend_tensor_get(out, got.data(), 0, got.size() * sizeof(float));
|
|
ggml_backend_buffer_free(buf);
|
|
ggml_backend_free(backend);
|
|
ggml_free(ctx);
|
|
if (!ok) return false;
|
|
|
|
float max_err = 0.0f;
|
|
for (int h = 0; h < N_HEADS; ++h) {
|
|
for (int d = 0; d < HEAD_DIM; ++d) {
|
|
const int idx = h * HEAD_DIM + d;
|
|
const float err = std::fabs(expected[idx] - got[idx]);
|
|
if (!std::isfinite(got[idx]) || err > TOL) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] FusedAttn FAIL h=%d d=%d expected=%+.6f got=%+.6f diff=%.3e\n",
|
|
h, d, expected[idx], got[idx], err);
|
|
return false;
|
|
}
|
|
if (err > max_err) max_err = err;
|
|
}
|
|
}
|
|
*max_err_out = max_err;
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
const int device_count = ggml_backend_vk_get_device_count();
|
|
if (device_count <= 0) {
|
|
std::fprintf(stderr, "[vulkan_dispatch_smoke] no Vulkan devices visible to ggml-vulkan\n");
|
|
return 1;
|
|
}
|
|
char desc[256] = {};
|
|
ggml_backend_vk_get_device_description(0, desc, sizeof(desc));
|
|
std::printf("[vulkan_dispatch_smoke] device=%s\n", desc);
|
|
if (!software_vulkan_allowed() && looks_like_software_vulkan_device(desc)) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] refusing software Vulkan device '%s'. "
|
|
"Set ELIZA_ALLOW_SOFTWARE_VULKAN=1 for diagnostics only.\n",
|
|
desc);
|
|
return 2;
|
|
}
|
|
|
|
struct Case {
|
|
const char * label;
|
|
bool (*run)(float *);
|
|
int count;
|
|
};
|
|
|
|
const Case cases[] = {
|
|
{ "GGML_OP_ATTN_SCORE_QJL", run_qjl_smoke, N_HEADS * N_TOKENS },
|
|
{ "GGML_OP_ATTN_SCORE_TBQ/turbo3", [](float * e) {
|
|
return run_tbq_smoke<eliza_block_turbo3_0>(
|
|
"TurboQuant3", GGML_TYPE_TBQ3_0,
|
|
quantize_turbo3_adapter, dot_turbo3_adapter, 4, e);
|
|
}, N_HEADS * N_TOKENS },
|
|
{ "GGML_OP_ATTN_SCORE_TBQ/turbo4", [](float * e) {
|
|
return run_tbq_smoke<eliza_block_turbo4_0>(
|
|
"TurboQuant4", GGML_TYPE_TBQ4_0,
|
|
quantize_turbo4_adapter, dot_turbo4_adapter, 4, e);
|
|
}, N_HEADS * N_TOKENS },
|
|
{ "GGML_OP_ATTN_SCORE_TBQ/turbo3_tcq", [](float * e) {
|
|
return run_tbq_smoke<eliza_block_turbo3_tcq>(
|
|
"TurboQuant3_TCQ", GGML_TYPE_TBQ3_TCQ,
|
|
quantize_turbo3_tcq_adapter, dot_turbo3_tcq_adapter, 1, e);
|
|
}, N_HEADS * N_TOKENS },
|
|
{ "GGML_OP_ATTN_SCORE_POLAR/use_qjl=0", [](float * e) {
|
|
return run_polar_smoke(false, e);
|
|
}, N_HEADS * N_TOKENS },
|
|
{ "GGML_OP_ATTN_SCORE_POLAR/use_qjl=1", [](float * e) {
|
|
return run_polar_smoke(true, e);
|
|
}, N_HEADS * N_TOKENS },
|
|
{ "GGML_OP_FUSED_ATTN_QJL_TBQ", run_fused_attn_smoke, N_HEADS * HEAD_DIM },
|
|
};
|
|
|
|
int failures = 0;
|
|
for (const Case & c : cases) {
|
|
float max_err = 0.0f;
|
|
if (!c.run(&max_err)) {
|
|
std::fprintf(stderr, "[vulkan_dispatch_smoke] FAIL %s\n", c.label);
|
|
++failures;
|
|
continue;
|
|
}
|
|
std::printf("[vulkan_dispatch_smoke] PASS %s: %d outputs, max diff %.3e\n",
|
|
c.label, c.count, max_err);
|
|
}
|
|
|
|
if (failures != 0) {
|
|
std::fprintf(stderr,
|
|
"[vulkan_dispatch_smoke] FAIL Vulkan dispatch suite: %d graph route(s) failed\n",
|
|
failures);
|
|
return 1;
|
|
}
|
|
|
|
std::printf("[vulkan_dispatch_smoke] PASS Vulkan dispatch suite: %zu graph routes\n",
|
|
sizeof(cases) / sizeof(cases[0]));
|
|
return 0;
|
|
}
|