mirror of
https://github.com/Tencent/ncnn.git
synced 2026-10-17 04:41:47 +08:00
60bfa40044
* enable gqa * update perf test
207 lines
6.2 KiB
C++
207 lines
6.2 KiB
C++
// Copyright 2026 Tencent
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
#include "perfutil.h"
|
|
|
|
#include "benchmark.h"
|
|
#include "modelbin.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#define SESSION_WARMUP_COUNT 2
|
|
#define SESSION_RUN_COUNT 5
|
|
|
|
class KVCachePerfAllocator : public ncnn::Allocator
|
|
{
|
|
public:
|
|
KVCachePerfAllocator()
|
|
: allocation_count(0)
|
|
{
|
|
}
|
|
|
|
virtual void* fastMalloc(size_t size)
|
|
{
|
|
void* ptr = ncnn::fastMalloc(size);
|
|
if (ptr)
|
|
allocation_count++;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
virtual void fastFree(void* ptr)
|
|
{
|
|
ncnn::fastFree(ptr);
|
|
}
|
|
|
|
int allocation_count;
|
|
};
|
|
|
|
struct SessionResult
|
|
{
|
|
double time;
|
|
int relocation_count;
|
|
int allocation_count;
|
|
};
|
|
|
|
static void print_session_result(const char* device, const char* cache_type, int head_dim, int num_heads, int num_groups, int prefill_seqlen, int decode_steps, const SessionResult* results)
|
|
{
|
|
double times[SESSION_RUN_COUNT];
|
|
double time_avg = 0.0;
|
|
for (int i = 0; i < SESSION_RUN_COUNT; i++)
|
|
{
|
|
times[i] = results[i].time / decode_steps;
|
|
time_avg += times[i];
|
|
}
|
|
|
|
for (int i = 1; i < SESSION_RUN_COUNT; i++)
|
|
{
|
|
double key = times[i];
|
|
int j = i - 1;
|
|
while (j >= 0 && times[j] > key)
|
|
{
|
|
times[j + 1] = times[j];
|
|
j--;
|
|
}
|
|
times[j + 1] = key;
|
|
}
|
|
time_avg /= SESSION_RUN_COUNT;
|
|
|
|
fprintf(stdout, "SDPA %-3s %-12s head_dim=%d heads=%d groups=%d prefill=%d steps=%d min=%.3f max=%.3f avg=%.3f median=%.3f ms/step reloc=%d",
|
|
device, cache_type, head_dim, num_heads, num_groups, prefill_seqlen, decode_steps,
|
|
times[0], times[SESSION_RUN_COUNT - 1], time_avg, times[SESSION_RUN_COUNT / 2],
|
|
results[SESSION_RUN_COUNT - 1].relocation_count);
|
|
|
|
if (results[SESSION_RUN_COUNT - 1].allocation_count >= 0)
|
|
fprintf(stdout, " kvcache_alloc=%d", results[SESSION_RUN_COUNT - 1].allocation_count);
|
|
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
static int run_sdpa_cpu_session(ncnn::Layer* op, const ncnn::Option& opt, KVCachePerfAllocator& allocator, int head_dim, int num_heads, int num_groups, int prefill_seqlen, int decode_steps, SessionResult& result)
|
|
{
|
|
const int allocation_count = allocator.allocation_count;
|
|
|
|
std::vector<ncnn::Mat> prefill_bottoms(5);
|
|
prefill_bottoms[0] = PerfMat(head_dim, prefill_seqlen, num_heads);
|
|
prefill_bottoms[1] = PerfMat(head_dim, prefill_seqlen, num_groups);
|
|
prefill_bottoms[2] = PerfMat(head_dim, prefill_seqlen, num_groups);
|
|
|
|
std::vector<ncnn::Mat> prefill_tops(3);
|
|
int ret = op->forward(prefill_bottoms, prefill_tops, opt);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ncnn::Mat key_cache = prefill_tops[1];
|
|
ncnn::Mat value_cache = prefill_tops[2];
|
|
prefill_tops[1].release();
|
|
prefill_tops[2].release();
|
|
|
|
ncnn::Mat query = PerfMat(head_dim, 1, num_heads);
|
|
ncnn::Mat current_key = PerfMat(head_dim, 1, num_groups);
|
|
ncnn::Mat current_value = PerfMat(head_dim, 1, num_groups);
|
|
|
|
result.relocation_count = 0;
|
|
const double time_start = ncnn::get_current_time();
|
|
for (int i = 0; i < decode_steps; i++)
|
|
{
|
|
void* key_data = key_cache.data;
|
|
|
|
std::vector<ncnn::Mat> bottom_blobs(5);
|
|
bottom_blobs[0] = query;
|
|
bottom_blobs[1] = current_key;
|
|
bottom_blobs[2] = current_value;
|
|
bottom_blobs[3] = key_cache;
|
|
bottom_blobs[4] = value_cache;
|
|
key_cache.release();
|
|
value_cache.release();
|
|
|
|
std::vector<ncnn::Mat> top_blobs(3);
|
|
ret = op->forward(bottom_blobs, top_blobs, opt);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
if (top_blobs[1].data != key_data)
|
|
result.relocation_count++;
|
|
|
|
key_cache = top_blobs[1];
|
|
value_cache = top_blobs[2];
|
|
top_blobs[1].release();
|
|
top_blobs[2].release();
|
|
bottom_blobs[3].release();
|
|
bottom_blobs[4].release();
|
|
}
|
|
result.time = ncnn::get_current_time() - time_start;
|
|
result.allocation_count = opt.kvcache_allocator ? allocator.allocation_count - allocation_count : -1;
|
|
|
|
key_cache.release();
|
|
value_cache.release();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void perf_sdpa_kvcache_cpu(int head_dim, int num_heads, int num_groups, int prefill_seqlen, int decode_steps, int max_seqlen_hint)
|
|
{
|
|
KVCachePerfAllocator allocator;
|
|
|
|
ncnn::Option opt;
|
|
opt.kvcache_allocator = max_seqlen_hint >= 0 ? &allocator : 0;
|
|
opt.kvcache_max_seqlen_hint = max_seqlen_hint > 0 ? max_seqlen_hint : 0;
|
|
|
|
ncnn::Layer* op = ncnn::create_layer_cpu("SDPA");
|
|
if (!op)
|
|
return;
|
|
|
|
ncnn::ParamDict pd;
|
|
pd.set(7, 1);
|
|
op->load_param(pd);
|
|
op->load_model(ncnn::ModelBinFromMatArray(0));
|
|
if (op->create_pipeline(opt) != 0)
|
|
{
|
|
delete op;
|
|
return;
|
|
}
|
|
|
|
SessionResult result;
|
|
int ret = 0;
|
|
for (int i = 0; ret == 0 && i < SESSION_WARMUP_COUNT; i++)
|
|
{
|
|
ret = run_sdpa_cpu_session(op, opt, allocator, head_dim, num_heads, num_groups, prefill_seqlen, decode_steps, result);
|
|
}
|
|
|
|
SessionResult results[SESSION_RUN_COUNT];
|
|
int run_count = 0;
|
|
for (; ret == 0 && run_count < SESSION_RUN_COUNT; run_count++)
|
|
{
|
|
ret = run_sdpa_cpu_session(op, opt, allocator, head_dim, num_heads, num_groups, prefill_seqlen, decode_steps, results[run_count]);
|
|
}
|
|
|
|
if (run_count == SESSION_RUN_COUNT)
|
|
{
|
|
const char* cache_type = "default";
|
|
if (opt.kvcache_allocator)
|
|
cache_type = max_seqlen_hint > 0 ? "allocator-hint" : "allocator";
|
|
print_session_result("cpu", cache_type, head_dim, num_heads, num_groups, prefill_seqlen, decode_steps, results);
|
|
}
|
|
|
|
op->destroy_pipeline(opt);
|
|
delete op;
|
|
}
|
|
|
|
static void perf_sdpa_kvcache(int head_dim, int num_heads, int num_groups, int prefill_seqlen, int decode_steps)
|
|
{
|
|
const int max_seqlen_hint = prefill_seqlen + decode_steps;
|
|
|
|
perf_sdpa_kvcache_cpu(head_dim, num_heads, num_groups, prefill_seqlen, decode_steps, -1);
|
|
perf_sdpa_kvcache_cpu(head_dim, num_heads, num_groups, prefill_seqlen, decode_steps, 0);
|
|
perf_sdpa_kvcache_cpu(head_dim, num_heads, num_groups, prefill_seqlen, decode_steps, max_seqlen_hint);
|
|
}
|
|
|
|
int main()
|
|
{
|
|
perf_sdpa_kvcache(64, 8, 8, 128, 512);
|
|
perf_sdpa_kvcache(128, 32, 8, 128, 512);
|
|
perf_sdpa_kvcache(128, 32, 1, 128, 512);
|
|
|
|
return 0;
|
|
}
|