mirror of
https://github.com/Tencent/ncnn.git
synced 2026-10-19 06:21:39 +08:00
f6f734f44d
* vulkan: add int8 kernels for core layers Implement Vulkan int8 paths for convolution, convolutiondepthwise, innerproduct, and gemm with scalar fallback and integer dot product shader branches. Add goal.md and enable existing int8 unit tests for Vulkan coverage. * enable int8 gpu benchmark
316 lines
10 KiB
C++
316 lines
10 KiB
C++
// Copyright 2024 Tencent
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
#include "testutil.h"
|
|
|
|
#if NCNN_INT8
|
|
static void RandomizeA(ncnn::Mat& m, int transA, float absmax)
|
|
{
|
|
if (transA == 0)
|
|
{
|
|
const int h = m.dims == 3 ? m.c : m.h;
|
|
for (int i = 0; i < h; i++)
|
|
{
|
|
float* p = m.dims == 3 ? m.channel(i) : m.row(i);
|
|
float randabsmax = RandomFloat(absmax * 0.5f, absmax);
|
|
randabsmax = ncnn::float16_to_float32(ncnn::float32_to_float16(randabsmax));
|
|
randabsmax = ncnn::bfloat16_to_float32(ncnn::float32_to_bfloat16(randabsmax));
|
|
|
|
for (int j = 0; j < m.w; j++)
|
|
{
|
|
p[j] = RandomFloat(-randabsmax, randabsmax);
|
|
}
|
|
|
|
// set random a and b
|
|
p[RandomInt(0, m.w - 1)] = -randabsmax;
|
|
p[RandomInt(0, m.w - 1)] = randabsmax;
|
|
|
|
// drop 0.45 ~ 0.55
|
|
for (int j = 0; j < m.w; j++)
|
|
{
|
|
float v = p[j] * (127.f / randabsmax);
|
|
float vv = fabs(v - (int)v);
|
|
|
|
float hp = ncnn::float16_to_float32(ncnn::float32_to_float16(p[j]));
|
|
float hv = hp * (127.f / randabsmax);
|
|
float hvv = fabs(hv - (int)hv);
|
|
|
|
float bp = ncnn::bfloat16_to_float32(ncnn::float32_to_bfloat16(p[j]));
|
|
float bv = bp * (127.f / randabsmax);
|
|
float bvv = fabs(bv - (int)bv);
|
|
|
|
while ((vv > 0.45f && vv < 0.55f) || (hvv > 0.45f && hvv < 0.55f) || (bvv > 0.45f && bvv < 0.55f))
|
|
{
|
|
p[j] = RandomFloat(-randabsmax, randabsmax);
|
|
v = p[j] * (127.f / randabsmax);
|
|
vv = fabs(v - (int)v);
|
|
|
|
hp = ncnn::float16_to_float32(ncnn::float32_to_float16(p[j]));
|
|
hv = hp * (127.f / randabsmax);
|
|
hvv = fabs(hv - (int)hv);
|
|
|
|
bp = ncnn::bfloat16_to_float32(ncnn::float32_to_bfloat16(p[j]));
|
|
bv = bp * (127.f / randabsmax);
|
|
bvv = fabs(bv - (int)bv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // if (transA == 1)
|
|
{
|
|
std::vector<float> randabsmaxes(m.w);
|
|
for (int j = 0; j < m.w; j++)
|
|
{
|
|
float randabsmax = RandomFloat(absmax * 0.5f, absmax);
|
|
randabsmax = ncnn::float16_to_float32(ncnn::float32_to_float16(randabsmax));
|
|
randabsmax = ncnn::bfloat16_to_float32(ncnn::float32_to_bfloat16(randabsmax));
|
|
randabsmaxes[j] = randabsmax;
|
|
}
|
|
|
|
const int h = m.dims == 3 ? m.c : m.h;
|
|
for (int i = 0; i < h; i++)
|
|
{
|
|
float* p = m.dims == 3 ? m.channel(i) : m.row(i);
|
|
for (int j = 0; j < m.w; j++)
|
|
{
|
|
const float randabsmax = randabsmaxes[j];
|
|
p[j] = RandomFloat(-randabsmax, randabsmax);
|
|
}
|
|
|
|
// drop 0.45 ~ 0.55
|
|
for (int j = 0; j < m.w; j++)
|
|
{
|
|
const float randabsmax = randabsmaxes[j];
|
|
float v = p[j] * (127.f / randabsmax);
|
|
float vv = fabs(v - (int)v);
|
|
|
|
float hp = ncnn::float16_to_float32(ncnn::float32_to_float16(p[j]));
|
|
float hv = hp * (127.f / randabsmax);
|
|
float hvv = fabs(hv - (int)hv);
|
|
|
|
float bp = ncnn::bfloat16_to_float32(ncnn::float32_to_bfloat16(p[j]));
|
|
float bv = bp * (127.f / randabsmax);
|
|
float bvv = fabs(bv - (int)bv);
|
|
|
|
while ((vv > 0.45f && vv < 0.55f) || (hvv > 0.45f && hvv < 0.55f) || (bvv > 0.45f && bvv < 0.55f))
|
|
{
|
|
p[j] = RandomFloat(-randabsmax, randabsmax);
|
|
v = p[j] * (127.f / randabsmax);
|
|
vv = fabs(v - (int)v);
|
|
|
|
hp = ncnn::float16_to_float32(ncnn::float32_to_float16(p[j]));
|
|
hv = hp * (127.f / randabsmax);
|
|
hvv = fabs(hv - (int)hv);
|
|
|
|
bp = ncnn::bfloat16_to_float32(ncnn::float32_to_bfloat16(p[j]));
|
|
bv = bp * (127.f / randabsmax);
|
|
bvv = fabs(bv - (int)bv);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int j = 0; j < m.w; j++)
|
|
{
|
|
const int randi0 = RandomInt(0, h - 1);
|
|
const int randi1 = RandomInt(0, h - 1);
|
|
float* p0 = m.dims == 3 ? m.channel(randi0) : m.row(randi0);
|
|
float* p1 = m.dims == 3 ? m.channel(randi1) : m.row(randi1);
|
|
|
|
const float randabsmax = randabsmaxes[j];
|
|
|
|
// set random a and b
|
|
p0[j] = -randabsmax;
|
|
p1[j] = randabsmax;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void RandomizeB(ncnn::Mat& m, float absmax)
|
|
{
|
|
absmax = ncnn::float16_to_float32(ncnn::float32_to_float16(absmax));
|
|
absmax = ncnn::bfloat16_to_float32(ncnn::float32_to_bfloat16(absmax));
|
|
|
|
const int h = m.dims == 3 ? m.c : m.h;
|
|
float* p = m;
|
|
for (int i = 0; i < h; i++)
|
|
{
|
|
float* p = m.dims == 3 ? m.channel(i) : m.row(i);
|
|
for (int j = 0; j < m.w; j++)
|
|
{
|
|
p[j] = RandomFloat(-absmax, absmax);
|
|
|
|
// drop 0.45 ~ 0.55
|
|
float v = p[j] * (127.f / absmax);
|
|
float vv = fabs(v - (int)v);
|
|
|
|
float hp = ncnn::float16_to_float32(ncnn::float32_to_float16(p[j]));
|
|
float hv = hp * (127.f / absmax);
|
|
float hvv = fabs(hv - (int)hv);
|
|
|
|
float bp = ncnn::bfloat16_to_float32(ncnn::float32_to_bfloat16(p[j]));
|
|
float bv = bp * (127.f / absmax);
|
|
float bvv = fabs(bv - (int)bv);
|
|
|
|
while ((vv > 0.45f && vv < 0.55f) || (hvv > 0.45f && hvv < 0.55f) || (bvv > 0.45f && bvv < 0.55f))
|
|
{
|
|
p[j] = RandomFloat(-absmax, absmax);
|
|
v = p[j] * (127.f / absmax);
|
|
vv = fabs(v - (int)v);
|
|
|
|
hp = ncnn::float16_to_float32(ncnn::float32_to_float16(p[j]));
|
|
hv = hp * (127.f / absmax);
|
|
hvv = fabs(hv - (int)hv);
|
|
|
|
bp = ncnn::bfloat16_to_float32(ncnn::float32_to_bfloat16(p[j]));
|
|
bv = bp * (127.f / absmax);
|
|
bvv = fabs(bv - (int)bv);
|
|
}
|
|
}
|
|
}
|
|
|
|
// set random a and b
|
|
if (m.dims == 3)
|
|
{
|
|
m.channel(RandomInt(0, h - 1))[RandomInt(0, m.w - 1)] = -absmax;
|
|
m.channel(RandomInt(0, h - 1))[RandomInt(0, m.w - 1)] = absmax;
|
|
}
|
|
else
|
|
{
|
|
m.row(RandomInt(0, h - 1))[RandomInt(0, m.w - 1)] = -absmax;
|
|
m.row(RandomInt(0, h - 1))[RandomInt(0, m.w - 1)] = absmax;
|
|
}
|
|
}
|
|
|
|
static int test_gemm_int8(int M, int N, int K, int TILE_M, int TILE_N, int TILE_K, float alpha, int transA, int transB, int output_transpose)
|
|
{
|
|
ncnn::ParamDict pd;
|
|
pd.set(0, alpha);
|
|
pd.set(1, 1.f); // beta
|
|
pd.set(2, transA);
|
|
pd.set(3, transB);
|
|
pd.set(14, output_transpose);
|
|
pd.set(18, 2); // int8_scale_term
|
|
|
|
pd.set(20, TILE_M);
|
|
pd.set(21, TILE_N);
|
|
pd.set(22, TILE_K);
|
|
|
|
std::vector<ncnn::Mat> weights(0);
|
|
|
|
std::vector<ncnn::Mat> a(2);
|
|
a[0] = transA ? ncnn::Mat(M, K) : ncnn::Mat(K, M);
|
|
a[1] = transB ? ncnn::Mat(K, N) : ncnn::Mat(N, K);
|
|
|
|
RandomizeA(a[0], transA, 10.f);
|
|
RandomizeB(a[1], 10.f);
|
|
|
|
ncnn::Option opt;
|
|
opt.num_threads = 1;
|
|
opt.use_packing_layout = true;
|
|
opt.use_fp16_packed = false;
|
|
opt.use_fp16_storage = false;
|
|
opt.use_fp16_arithmetic = false;
|
|
opt.use_bf16_packed = false;
|
|
opt.use_bf16_storage = false;
|
|
|
|
int ret = test_layer_opt("Gemm", pd, weights, opt, a);
|
|
if (ret != 0)
|
|
{
|
|
fprintf(stderr, "test_gemm_int8 failed M=%d N=%d K=%d TILE_M=%d TILE_N=%d TILE_K=%d alpha=%f transA=%d transB=%d output_transpose=%d\n", M, N, K, TILE_M, TILE_N, TILE_K, alpha, transA, transB, output_transpose);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int test_gemm_0(int M, int N, int K, int TILE_M, int TILE_N, int TILE_K)
|
|
{
|
|
return 0
|
|
|| test_gemm_int8(M, N, K, TILE_M, TILE_N, TILE_K, 2.1f, 0, 0, 0)
|
|
|| test_gemm_int8(M, N, K, TILE_M, TILE_N, TILE_K, 3.1f, 0, 1, 0)
|
|
|| test_gemm_int8(M, N, K, TILE_M, TILE_N, TILE_K, 4.1f, 1, 0, 0)
|
|
|| test_gemm_int8(M, N, K, TILE_M, TILE_N, TILE_K, 5.1f, 1, 1, 0)
|
|
|| test_gemm_int8(M, N, K, TILE_M, TILE_N, TILE_K, 2.1f, 0, 0, 1)
|
|
|| test_gemm_int8(M, N, K, TILE_M, TILE_N, TILE_K, 3.1f, 0, 1, 1)
|
|
|| test_gemm_int8(M, N, K, TILE_M, TILE_N, TILE_K, 4.1f, 1, 0, 1)
|
|
|| test_gemm_int8(M, N, K, TILE_M, TILE_N, TILE_K, 5.1f, 1, 1, 1);
|
|
}
|
|
#endif // NCNN_INT8
|
|
|
|
int main()
|
|
{
|
|
SRAND(7767517);
|
|
|
|
#if NCNN_INT8
|
|
int mnk[][3] = {
|
|
{1, 1, 1},
|
|
{2, 2, 2},
|
|
{3, 3, 3},
|
|
{4, 4, 4},
|
|
{5, 5, 5},
|
|
{6, 6, 6},
|
|
{7, 7, 7},
|
|
{8, 8, 8},
|
|
{15, 15, 15},
|
|
{16, 16, 16},
|
|
{24, 24, 24},
|
|
{31, 31, 31},
|
|
{31, 32, 31},
|
|
{32, 31, 32},
|
|
{32, 32, 32},
|
|
{20, 32, 20},
|
|
{40, 40, 40},
|
|
{47, 47, 47},
|
|
{48, 48, 48},
|
|
{52, 52, 52},
|
|
{63, 64, 63},
|
|
{64, 63, 64},
|
|
{64, 64, 64}
|
|
};
|
|
|
|
int tile_mnk[][3] = {
|
|
{1, 1, 1},
|
|
{2, 2, 2},
|
|
{4, 4, 4},
|
|
{8, 8, 8},
|
|
{12, 12, 12},
|
|
{16, 16, 16},
|
|
{20, 20, 20},
|
|
{24, 24, 24},
|
|
{28, 28, 28}
|
|
};
|
|
|
|
int mnk_count = sizeof(mnk) / sizeof(int) / 3;
|
|
int tile_mnk_count = sizeof(tile_mnk) / sizeof(int) / 3;
|
|
|
|
for (int i = 0; i < mnk_count; i++)
|
|
{
|
|
int M = mnk[i][0];
|
|
int N = mnk[i][1];
|
|
int K = mnk[i][2];
|
|
|
|
for (int j = 0; j < tile_mnk_count; j++)
|
|
{
|
|
int TILE_M = tile_mnk[j][0];
|
|
int TILE_N = tile_mnk[j][1];
|
|
int TILE_K = tile_mnk[j][2];
|
|
|
|
if (TILE_M >= M && TILE_N >= N && TILE_K >= K)
|
|
continue;
|
|
|
|
int ret = test_gemm_0(M, N, K, TILE_M, TILE_N, TILE_K);
|
|
if (ret != 0)
|
|
return ret;
|
|
}
|
|
|
|
// test no tiling
|
|
int ret = test_gemm_0(M, N, K, 100, 100, 100);
|
|
if (ret != 0)
|
|
return ret;
|
|
}
|
|
#else
|
|
// test nothing for non-int8 build
|
|
#endif
|
|
|
|
return 0;
|
|
}
|