Dense Q8_0 GEMV: one row per block where measured faster (gfx1201: n_in <= 4096 with n_out >= 4096; sm_86: n_out >= 2048), the same bits as two rows

This commit is contained in:
Niko1221 committed 2026-10-12 23:08:31 +02:00
1 parent 5ef9f4c812
commit 945fee2365
1 file changed
+46 -1
+46 -1
View File
@@ -27,6 +27,7 @@
#include "strata/kernels/dp4a.hpp"
#include "strata/kernels/q8_1_finite.hpp"
#include "strata/kernels/iq_kernels.hpp"
#include "strata/kernels/gfx_arch.hpp"
#include "s26_tsum.cuh"
#if !defined(__HIPCC__)
#include "q8_1_il.cuh"
@@ -42,6 +43,7 @@
#include <cstring>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <string>
#include <unordered_map>
#include <cstdio>
@@ -1258,6 +1260,49 @@ __global__ void native_mmvq_multi_kernel(const typename F::Block* __restrict__ w
}
}
// MODEL-UPDATE: the dense Q8_0 projections of Unsloth's files (all of attention, SSM and the shared expert are Q8_0
// there) take one row per block where that was measured faster - the same per-row sums and reduction as two rows per
// block (each row its own partials), so every output is the same bits. q8_gemv_bench, 2-4 columns:
// gfx1201 (R9700): n_in <= 4096 with n_out >= 4096 (qkv 2560->10240 73-79% -> 83-84% of the 644 GB/s, gate 2560->6144
// 51-68% -> 76-78%, q 2560->12288 74-80% -> 85-86%); 6144->2560 is slower there (79% -> 55-59%) and keeps two rows.
// sm_86 (RTX A4000): every n_out >= 2048 (+1-4% at 2-4 columns, 6144->2560 at 6 columns 70% -> 76%).
// Other cards keep two rows until measured (STRATA_NO_MMVQ_ROWS2=1 forces one row everywhere; =0 keeps two everywhere).
int dev_class() { // 1 = gfx12, 2 = sm_86, 0 = the rest; per device
static int cls[16]; // 0 unknown, else class + 1
int dev = 0;
if (cudaGetDevice(&dev) != cudaSuccess || dev < 0 || dev >= 16) return 0;
if (cls[dev] == 0) {
int c = 0;
#if defined(__HIPCC__)
cudaDeviceProp p;
if (cudaGetDeviceProperties(&p, dev) == cudaSuccess && strata::kernels::gfx_arch_is_gfx12(p.gcnArchName)) c = 1;
#else
int major = 0, minor = 0;
if (cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, dev) == cudaSuccess &&
cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, dev) == cudaSuccess && major == 8 && minor == 6)
c = 2;
#endif
(void) cudaGetLastError();
cls[dev] = c + 1;
}
return cls[dev] - 1;
}
template<typename F>
bool q8_0_rows1(int n_in, int n_out) {
if constexpr (!std::is_same_v<F, SmallTraits<Q80Block, 8>>) {
(void) n_in; (void) n_out;
return false;
} else {
static const int env = [] {
const char* v = std::getenv("STRATA_NO_MMVQ_ROWS2");
return v == nullptr || v[0] == '\0' ? -1 : v[0] != '0';
}();
if (env == 0) return false;
const int c = dev_class();
return (c == 1 && n_in <= 4096 && n_out >= 4096) || (c == 2 && n_out >= 2048);
}
}
template<typename F, int NCOLS>
void launch_multi_n(const void* weights, const void* x_q8_1, float* y, int n_in, int n_out, cudaStream_t s) {
const auto* w = static_cast<const typename F::Block*>(weights);
@@ -1279,7 +1324,7 @@ void launch_multi_n(const void* weights, const void* x_q8_1, float* y, int n_in,
const char* v = std::getenv("STRATA_NO_MMVQ_ROWS2");
return v != nullptr && v[0] != '\0' && v[0] != '0';
}();
if (rows1 && n_in / F::DIV >= F::BPI) {
if ((rows1 || q8_0_rows1<F>(n_in, n_out)) && n_in / F::DIV >= F::BPI) {
if (s26_tsum_on()) launch_pdl(native_mmvq_multi_kernel<F, NCOLS, WARPS, 1, true, false>, dim3(unsigned(n_out)), threads, 0, s, w, x, y, n_in, n_out, no_w2, no_y2);
else launch_pdl(native_mmvq_multi_kernel<F, NCOLS, WARPS, 1, false, false>, dim3(unsigned(n_out)), threads, 0, s, w, x, y, n_in, n_out, no_w2, no_y2);
return;