9aa2807769
* hexagon: introduce op request batching and rewrite buffer managment The host now prepares batches of requests and dispatches them via a single dspqueue message. Buffers are mapped explicitly by NPU while processing batches. * hex-dma: disable l2 bypass since to work around new issue due to no flushes between Ops * hex-utils: add explicit l2flush and l2clear helpers * hex-opreq: use fine-grain per tensor l2 management * hex-opreq: avoid redundant invalidates for tensors we already flushed * hex-opreq: update debug messages * htp-opreq: reuse ops_context * hex-opreq: do not flush or invalidate cache lines beyond buffer boundry * hex-opreq: fix errors in log message * Revert "hex-opreq: do not flush or invalidate cache lines beyond buffer boundry" This reverts commit 8b7f0a55a750a6430ce4eb1874c7feb3d720056d. * hexagon: limit l2 flushes to 1MB which covers l2 cache * hex-opreq: limit cache flush to 4MB Looks like 4MB cont. vitual space should cover the 1MB cache. * hexagon: drop cache flush size to 2MB * hex-opreq: start reworking opreq packing * hex-opreq: introduce new way of packing opbatch where tensors are stored separately * hex-opreq: add a simple fastrpc call to force unmap all buffers * hex-l2flush: somehow 2MB does not seem robust, also cleanup step size to use line-size * hex-opreq: bump opreq batch size to 256 * hex-mm: place src1 spad at the top of vtcm for easy reuse * hex-ops: introduce internal types and disable src1 reuse for now Nothing new just formalizing the repack / qyn.quant types we've been using. * htp-opreq: use tensor pointers instead of copies * hex-opreq: introduce more robust way for tracking vtcm/spad reuse This removes the SKIP_QUANTIZE flag that became fragile with the addition of HMX and other ops. * hex-cumsum: fix error post opreq merge * hex-opreq: move request batch handling into the session Prepping everything for using dspqueue buffers and doing that inside the session is much cleaner. * hex-mm: yet another fix for src1 reuse when we're mixing hmx/hvx * hex-bufs: introduce pinned mmapings and use non-pinned ones for model buffers * hex-buf: add support for allocating shared/pinned buffer for opreqs * hex-opbatch: make opbatches configurable * hex-naming: better name for ggml_hexagon_shared_buffer * hex-naming: add session->c_name() helper * hex-opbatch: start using shm but still copy for now * hex-opbatch: use shared buffer for packing opbatch * hex-opbatch: beter naming for opbatch related classes and code * hex-opbatch: reuse batched tensors with same data/dims/strides * hex-opbatch: update logging * hex-opbatch: add support for vmem limit for op batching * hex-opbatch: update htp side to properly support dynamic mmap/unmap * hex-opbatch: add OB and OQ params for run-completion script and fix the asserts in batch processing * hex-opbatch: fixed src1 handling in act ops * hex-act: fix empty src1 handling in swiglu and friends Simplify preamble macro while at it * hex-mm: minor fix vtcm and dma handling in matmul cleaning up some left-overs from merges * hex-opbatch: allocate extra 1KB for dspqueue overhead * hexagon: fix softmax for non-aligned tensors and cleanup vtcm alloc * hex-mm: properly handle hmx_disabled flag * hex-ops: update comments * hex-ops: add debug output for get/set-rows * hex-mmap: optimize un/mapping of buffers * hex-opreq: global cache flush and invalidate beyond 128KB threshold * hex-ops: add super simple opfilter regex for debugging If an Op matches the regex hex backend will reject it. * hex-opbatch: wireup newer ops missed in merge and update main switch to detect this in future * hexagon: improved vtcm acquision to remove inter-op overhead Fully compatible with QNN-HTP coex * hex-mm: fixed hvx fallback path * hex-mm: lower the vmem threshold a bit further to ~3GB * hexagon: update debug & error logs This also fixes an issue with newer llvm merging repack and non-repack functions. We use those pointer to distinguish between buffer types. * hexagon: move ops context into main context Just a cleanup. We don't need separate contexts at this point. * hex-opbatch: cleanup naming and headers for opbatch and related descriptors * hex-fa: it's now better to enable FA during TG to reduce graph splits * hexagon: remove GGML_HEXAGON_EXPERIMENTAL env var It's no longer useful. Please use more flexible GGML_HEXAGON_OPFILTER to disable Ops if needed for debugging or validation. * hexagon: fixed editorconfig check * Update ggml/src/ggml-hexagon/ggml-hexagon.cpp Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com> --------- Co-authored-by: Trivikram Reddy <tamarnat@qti.qualcomm.com> Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com>
149 lines
4.8 KiB
C
149 lines
4.8 KiB
C
#pragma clang diagnostic ignored "-Wunused-variable"
|
|
#pragma clang diagnostic ignored "-Wunused-function"
|
|
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
|
|
|
#include <HAP_farf.h>
|
|
#include <HAP_perf.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "hvx-utils.h"
|
|
|
|
#define GGML_COMMON_DECL_C
|
|
#include "ggml-common.h"
|
|
#include "htp-ctx.h"
|
|
#include "htp-ops.h"
|
|
#include "htp-ops.h"
|
|
|
|
struct htp_repeat_context {
|
|
struct htp_ops_context * octx;
|
|
|
|
uint32_t nr0;
|
|
uint32_t nr1;
|
|
uint32_t nr2;
|
|
uint32_t nr3;
|
|
|
|
uint32_t nrows_per_thread;
|
|
uint32_t total_dst_rows; // ne1 * ne2 * ne3
|
|
|
|
size_t type_size;
|
|
};
|
|
|
|
static void repeat_job_per_thread(unsigned int nth, unsigned int ith, void * data) {
|
|
const struct htp_repeat_context * rctx = (const struct htp_repeat_context *) data;
|
|
struct htp_ops_context * octx = rctx->octx;
|
|
const struct htp_tensor * src = octx->src[0];
|
|
const struct htp_tensor * dst = octx->dst;
|
|
|
|
const uint32_t ne00 = src->ne[0];
|
|
const uint32_t ne01 = src->ne[1];
|
|
const uint32_t ne02 = src->ne[2];
|
|
const uint32_t ne03 = src->ne[3];
|
|
|
|
const uint32_t nb00 = src->nb[0];
|
|
const uint32_t nb01 = src->nb[1];
|
|
const uint32_t nb02 = src->nb[2];
|
|
const uint32_t nb03 = src->nb[3];
|
|
|
|
const uint32_t ne0 = dst->ne[0];
|
|
const uint32_t ne1 = dst->ne[1];
|
|
const uint32_t ne2 = dst->ne[2];
|
|
const uint32_t ne3 = dst->ne[3];
|
|
|
|
const uint32_t nb0 = dst->nb[0];
|
|
const uint32_t nb1 = dst->nb[1];
|
|
const uint32_t nb2 = dst->nb[2];
|
|
const uint32_t nb3 = dst->nb[3];
|
|
|
|
const uint32_t nr0 = rctx->nr0;
|
|
const uint32_t nr1 = rctx->nr1;
|
|
const uint32_t nr2 = rctx->nr2;
|
|
const uint32_t nr3 = rctx->nr3;
|
|
|
|
const size_t row_bytes = ne00 * rctx->type_size;
|
|
|
|
const uint32_t row_start = rctx->nrows_per_thread * ith;
|
|
const uint32_t row_end = MIN(row_start + rctx->nrows_per_thread, rctx->total_dst_rows);
|
|
|
|
uint64_t t1, t2;
|
|
t1 = HAP_perf_get_qtimer_count();
|
|
|
|
for (uint32_t dst_row = row_start; dst_row < row_end; dst_row++) {
|
|
// Decompose flat dst row index into (i1, i2, i3)
|
|
const uint32_t i1 = dst_row % ne1;
|
|
const uint32_t i2 = (dst_row / ne1) % ne2;
|
|
const uint32_t i3 = dst_row / (ne1 * ne2);
|
|
|
|
// Map to source indices (tiling)
|
|
const uint32_t k1 = i1 % ne01;
|
|
const uint32_t k2 = i2 % ne02;
|
|
const uint32_t k3 = i3 % ne03;
|
|
|
|
const uint8_t * src_row = (const uint8_t *) src->data + k1 * nb01 + k2 * nb02 + k3 * nb03;
|
|
uint8_t * dst_base = (uint8_t *) dst->data + i1 * nb1 + i2 * nb2 + i3 * nb3;
|
|
|
|
// Tile along dimension 0
|
|
for (uint32_t i0 = 0; i0 < nr0; i0++) {
|
|
uint8_t * dst_ptr = dst_base + i0 * ne00 * nb0;
|
|
memcpy(dst_ptr, src_row, row_bytes);
|
|
}
|
|
}
|
|
|
|
t2 = HAP_perf_get_qtimer_count();
|
|
|
|
FARF(HIGH, "repeat %d/%d: (%ux%ux%ux%u) -> (%ux%ux%ux%u) rows %u:%u usec %u\n",
|
|
ith, nth, src->ne[0], src->ne[1], src->ne[2], src->ne[3],
|
|
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
|
row_start, row_end, (unsigned) HAP_perf_qtimer_count_to_us(t2 - t1));
|
|
}
|
|
|
|
int op_repeat(struct htp_ops_context * octx) {
|
|
const struct htp_tensor * src0 = octx->src[0];
|
|
const struct htp_tensor * dst = octx->dst;
|
|
|
|
// Validate that dst dims are multiples of src dims
|
|
if (dst->ne[0] % src0->ne[0] != 0 ||
|
|
dst->ne[1] % src0->ne[1] != 0 ||
|
|
dst->ne[2] % src0->ne[2] != 0 ||
|
|
dst->ne[3] % src0->ne[3] != 0) {
|
|
FARF(ERROR, "repeat: dst dims must be multiples of src dims\n");
|
|
return HTP_STATUS_INVAL_PARAMS;
|
|
}
|
|
|
|
size_t type_size;
|
|
switch (src0->type) {
|
|
case HTP_TYPE_F32: type_size = 4; break;
|
|
case HTP_TYPE_F16: type_size = 2; break;
|
|
default:
|
|
FARF(ERROR, "repeat: unsupported type %u\n", src0->type);
|
|
return HTP_STATUS_NO_SUPPORT;
|
|
}
|
|
|
|
const uint32_t total_dst_rows = dst->ne[1] * dst->ne[2] * dst->ne[3];
|
|
const uint32_t n_threads = MIN(octx->n_threads, total_dst_rows);
|
|
|
|
if (octx->flags & HTP_OPFLAGS_SKIP_COMPUTE) {
|
|
return HTP_STATUS_OK;
|
|
}
|
|
|
|
struct htp_repeat_context rctx = {
|
|
.octx = octx,
|
|
.nr0 = dst->ne[0] / src0->ne[0],
|
|
.nr1 = dst->ne[1] / src0->ne[1],
|
|
.nr2 = dst->ne[2] / src0->ne[2],
|
|
.nr3 = dst->ne[3] / src0->ne[3],
|
|
.nrows_per_thread = (total_dst_rows + n_threads - 1) / n_threads,
|
|
.total_dst_rows = total_dst_rows,
|
|
.type_size = type_size,
|
|
};
|
|
|
|
FARF(HIGH, "repeat: (%ux%ux%ux%u) -> (%ux%ux%ux%u) nr=(%u,%u,%u,%u)\n",
|
|
src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3],
|
|
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
|
rctx.nr0, rctx.nr1, rctx.nr2, rctx.nr3);
|
|
|
|
worker_pool_run_func(octx->ctx->worker_pool, repeat_job_per_thread, &rctx, n_threads);
|
|
|
|
return HTP_STATUS_OK;
|
|
}
|