mirror of
https://github.com/opencv/opencv.git
synced 2026-07-31 00:03:03 +04:00
Merge pull request #25196 from fengyuentau:fp16_bf16_arithm
core: add universal intrinsics for fp16 #25196 Partially resolves the section "Universal intrinsics evolution in OpenCV 5.0" in https://github.com/opencv/opencv/issues/25019. Universal intrinsics for bf16 will be added in a subsequent pull request. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
@@ -182,6 +182,13 @@ template<> inline void EXPECT_COMPARE_EQ_<double>(const double a, const double b
|
||||
EXPECT_DOUBLE_EQ( a, b );
|
||||
}
|
||||
|
||||
#if CV_SIMD_FP16
|
||||
template<> inline void EXPECT_COMPARE_EQ_<__fp16>(const __fp16 a, const __fp16 b)
|
||||
{
|
||||
EXPECT_LT(std::abs(float(a - b)), 0.126);
|
||||
}
|
||||
#endif
|
||||
|
||||
// pack functions do not do saturation when converting from 64-bit types
|
||||
template<typename T, typename W>
|
||||
inline T pack_saturate_cast(W a) { return saturate_cast<T>(a); }
|
||||
@@ -554,6 +561,27 @@ template<typename R> struct TheTest
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Handle accuracy for fp16
|
||||
TheTest & test_div_fp16()
|
||||
{
|
||||
#if CV_SIMD_FP16
|
||||
Data<R> dataA, dataB;
|
||||
dataB.reverse();
|
||||
R a = dataA, b = dataB;
|
||||
|
||||
Data<R> resC = v_div(a, b);
|
||||
for (int i = 0; i < VTraits<R>::vlanes(); ++i)
|
||||
{
|
||||
SCOPED_TRACE(cv::format("i=%d", i));
|
||||
EXPECT_LT(std::abs(float((dataA[i] / dataB[i]) - resC[i])), 2e-4);
|
||||
}
|
||||
#else
|
||||
std::cout << "SKIP: test_div_fp16, CV_SIMD_FP16 is not available" << std::endl;
|
||||
#endif
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TheTest & test_mul_expand()
|
||||
{
|
||||
typedef typename V_RegTraits<R>::w_reg Rx2;
|
||||
@@ -604,11 +632,34 @@ template<typename R> struct TheTest
|
||||
a = v_sub(a, b);
|
||||
|
||||
Data<Ru> resC = v_abs(a);
|
||||
auto R_type_lowest = std::numeric_limits<R_type>::lowest();
|
||||
|
||||
for (int i = 0; i < VTraits<Ru>::vlanes(); ++i)
|
||||
{
|
||||
SCOPED_TRACE(cv::format("i=%d", i));
|
||||
R_type ssub = dataA[i] - dataB[i] < std::numeric_limits<R_type>::lowest() ? std::numeric_limits<R_type>::lowest() : dataA[i] - dataB[i];
|
||||
R_type ssub = (dataA[i] - dataB[i]) < R_type_lowest ? R_type_lowest : dataA[i] - dataB[i];
|
||||
EXPECT_EQ((u_type)std::abs(ssub), resC[i]);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TheTest & test_abs_fp16()
|
||||
{
|
||||
typedef typename V_RegTraits<R>::u_reg Ru; // v_float16x8
|
||||
typedef typename VTraits<Ru>::lane_type u_type; // __fp16
|
||||
typedef typename VTraits<R>::lane_type R_type; // __fp16
|
||||
Data<R> dataA, dataB(10);
|
||||
R a = dataA, b = dataB;
|
||||
a = v_sub(a, b);
|
||||
|
||||
Data<Ru> resC = v_abs(a);
|
||||
R_type R_type_lowest = R_type(-65504); // 0 11110 1111111111
|
||||
|
||||
for (int i = 0; i < VTraits<Ru>::vlanes(); ++i)
|
||||
{
|
||||
SCOPED_TRACE(cv::format("i=%d", i));
|
||||
R_type ssub = (dataA[i] - dataB[i]) < R_type_lowest ? R_type_lowest : dataA[i] - dataB[i];
|
||||
EXPECT_EQ((u_type)std::abs(ssub), resC[i]);
|
||||
}
|
||||
|
||||
@@ -1492,6 +1543,54 @@ template<typename R> struct TheTest
|
||||
return *this;
|
||||
}
|
||||
|
||||
TheTest & test_matmul_fp16()
|
||||
{
|
||||
#if CV_SIMD_FP16
|
||||
Data<R> dataV, data0, data1, data2, data3, data4, data5, data6, data7;
|
||||
data1.reverse();
|
||||
data2 += 2;
|
||||
data3 *= 0.3;
|
||||
data5.reverse();
|
||||
data6 += 1;
|
||||
data7 *= 0.4;
|
||||
R v = dataV, m0 = data0, m1 = data1, m2 = data2, m3 = data3, m4 = data4, m5 = data5, m6 = data6, m7 = data7;
|
||||
|
||||
Data<R> res = v_matmul(v, m0, m1, m2, m3, m4, m5, m6, m7);
|
||||
int i = 0;
|
||||
for (int j = i; j < i + 8; ++j) {
|
||||
SCOPED_TRACE(cv::format("i=%d j=%d", i, j));
|
||||
LaneType val = dataV[i] * data0[j] +
|
||||
dataV[i + 1] * data1[j] +
|
||||
dataV[i + 2] * data2[j] +
|
||||
dataV[i + 3] * data3[j] +
|
||||
dataV[i + 4] * data4[j] +
|
||||
dataV[i + 5] * data5[j] +
|
||||
dataV[i + 6] * data6[j] +
|
||||
dataV[i + 7] * data7[j];
|
||||
EXPECT_COMPARE_EQ(val, res[j]);
|
||||
}
|
||||
|
||||
Data<R> resAdd = v_matmuladd(v, m0, m1, m2, m3, m4, m5, m6, m7);
|
||||
i = 0;
|
||||
for (int j = i; j < i + 8; ++j) {
|
||||
SCOPED_TRACE(cv::format("i=%d j=%d", i, j));
|
||||
LaneType val = dataV[i] * data0[j] +
|
||||
dataV[i + 1] * data1[j] +
|
||||
dataV[i + 2] * data2[j] +
|
||||
dataV[i + 3] * data3[j] +
|
||||
dataV[i + 4] * data4[j] +
|
||||
dataV[i + 5] * data5[j] +
|
||||
dataV[i + 6] * data6[j] +
|
||||
data7[j];
|
||||
EXPECT_COMPARE_EQ(val, resAdd[j]);
|
||||
}
|
||||
#else
|
||||
std::cout << "SKIP: test_matmul_fp16, CV_SIMD_FP16 is not available" << std::endl;
|
||||
#endif
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TheTest & test_transpose()
|
||||
{
|
||||
Data<R> dataA, dataB, dataC, dataD;
|
||||
@@ -1527,6 +1626,41 @@ template<typename R> struct TheTest
|
||||
return *this;
|
||||
}
|
||||
|
||||
TheTest & test_transpose8x8_fp16()
|
||||
{
|
||||
#if CV_SIMD_FP16
|
||||
Data<R> dataA0, dataA1, dataA2, dataA3, dataA4, dataA5, dataA6, dataA7;
|
||||
dataA1 *= 2;
|
||||
dataA2 *= 4;
|
||||
dataA3 *= 6;
|
||||
dataA4 *= 8;
|
||||
dataA5 *= 10;
|
||||
dataA6 *= 12;
|
||||
dataA7 *= 14;
|
||||
|
||||
R a0 = dataA0, a1 = dataA1, a2 = dataA2, a3 = dataA3,
|
||||
a4 = dataA4, a5 = dataA5, a6 = dataA6, a7 = dataA7;
|
||||
R b0, b1, b2, b3, b4, b5, b6, b7;
|
||||
|
||||
v_transpose8x8(a0, a1, a2, a3, a4, a5, a6, a7,
|
||||
b0, b1, b2, b3, b4, b5, b6, b7);
|
||||
Data<R> res0 = b0, res1 = b1, res2 = b2, res3 = b3, res4 = b4, res5 = b5, res6 = b6, res7 = b7;
|
||||
|
||||
const Data<R> ref[] = {dataA0, dataA1, dataA2, dataA3, dataA4, dataA5, dataA6, dataA7};
|
||||
const Data<R> res[] = { res0, res1, res2, res3, res4, res5, res6, res7};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
SCOPED_TRACE(cv::format("i=%d j=%d", i, j));
|
||||
EXPECT_EQ(ref[i][j], res[j][i]);
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::cout << "SKIP: test_transpose8x8_fp16, CV_SIMD_FP16 is not available" << std::endl;
|
||||
#endif
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TheTest & test_reduce_sum4()
|
||||
{
|
||||
Data<R> dataA, dataB, dataC, dataD;
|
||||
@@ -1548,9 +1682,43 @@ template<typename R> struct TheTest
|
||||
return *this;
|
||||
}
|
||||
|
||||
TheTest & test_reduce_sum8()
|
||||
{
|
||||
#if CV_SIMD_FP16
|
||||
Data<R> dataA, dataB, dataC, dataD, dataW, dataX, dataY, dataZ;
|
||||
dataB *= 0.01f;
|
||||
dataC *= 0.001f;
|
||||
dataD *= 0.002f;
|
||||
dataW += 0.1f;
|
||||
dataX *= 0.2f;
|
||||
dataY += 1;
|
||||
dataZ *= 2;
|
||||
|
||||
R a = dataA, b = dataB, c = dataC, d = dataD,
|
||||
w = dataW, x = dataX, y = dataY, z = dataZ;
|
||||
Data<R> res = v_reduce_sum8(a, b, c, d, w, x, y, z);
|
||||
|
||||
for (int i = 0; i < VTraits<R>::vlanes(); i += 8)
|
||||
{
|
||||
SCOPED_TRACE(cv::format("i=%d", i));
|
||||
EXPECT_COMPARE_EQ(dataA.sum(i, 8), res[i]);
|
||||
EXPECT_COMPARE_EQ(dataB.sum(i, 8), res[i + 1]);
|
||||
EXPECT_COMPARE_EQ(dataC.sum(i, 8), res[i + 2]);
|
||||
EXPECT_COMPARE_EQ(dataD.sum(i, 8), res[i + 3]);
|
||||
EXPECT_COMPARE_EQ(dataW.sum(i, 8), res[i + 4]);
|
||||
EXPECT_COMPARE_EQ(dataX.sum(i, 8), res[i + 5]);
|
||||
EXPECT_COMPARE_EQ(dataY.sum(i, 8), res[i + 6]);
|
||||
EXPECT_COMPARE_EQ(dataZ.sum(i, 8), res[i + 7]);
|
||||
}
|
||||
#else
|
||||
std::cout << "SKIP: test_reduce_sum8, CV_SIMD_FP16 is not available" << std::endl;
|
||||
#endif
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TheTest & test_loadstore_fp16_f32()
|
||||
{
|
||||
printf("test_loadstore_fp16_f32 ...\n");
|
||||
AlignedData<v_uint16> data; data.a.clear();
|
||||
data.a.d[0] = 0x3c00; // 1.0
|
||||
data.a.d[VTraits<R>::vlanes() - 1] = (unsigned short)0xc000; // -2.0
|
||||
@@ -1573,22 +1741,21 @@ template<typename R> struct TheTest
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if 0
|
||||
TheTest & test_loadstore_fp16()
|
||||
{
|
||||
printf("test_loadstore_fp16 ...\n");
|
||||
#if CV_SIMD_FP16
|
||||
AlignedData<R> data;
|
||||
AlignedData<R> out;
|
||||
|
||||
// check if addresses are aligned and unaligned respectively
|
||||
EXPECT_EQ((size_t)0, (size_t)&data.a.d % VTraits<R>::max_nlanes);
|
||||
EXPECT_NE((size_t)0, (size_t)&data.u.d % VTraits<R>::max_nlanes);
|
||||
EXPECT_EQ((size_t)0, (size_t)&out.a.d % VTraits<R>::max_nlanes);
|
||||
EXPECT_NE((size_t)0, (size_t)&out.u.d % VTraits<R>::max_nlanes);
|
||||
EXPECT_EQ((size_t)0, (size_t)&data.a.d % (sizeof(typename VTraits<R>::lane_type) * VTraits<R>::vlanes()));
|
||||
EXPECT_NE((size_t)0, (size_t)&data.u.d % (sizeof(typename VTraits<R>::lane_type) * VTraits<R>::vlanes()));
|
||||
EXPECT_EQ((size_t)0, (size_t)&out.a.d % (sizeof(typename VTraits<R>::lane_type) * VTraits<R>::vlanes()));
|
||||
EXPECT_NE((size_t)0, (size_t)&out.u.d % (sizeof(typename VTraits<R>::lane_type) * VTraits<R>::vlanes()));
|
||||
|
||||
// check some initialization methods
|
||||
R r1 = data.u;
|
||||
R r2 = vx_load_expand((const hfloat*)data.a.d);
|
||||
R r2 = vx_load(data.a.d);
|
||||
R r3(r2);
|
||||
EXPECT_EQ(data.u[0], v_get0(r1));
|
||||
EXPECT_EQ(data.a[0], v_get0(r2));
|
||||
@@ -1598,24 +1765,30 @@ template<typename R> struct TheTest
|
||||
out.a.clear();
|
||||
v_store(out.a.d, r1);
|
||||
EXPECT_EQ(data.a, out.a);
|
||||
#else
|
||||
std::cout << "SKIP: test_loadstore_fp16, CV_SIMD_FP16 is not available" << std::endl;
|
||||
#endif
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TheTest & test_float_cvt_fp16()
|
||||
{
|
||||
printf("test_float_cvt_fp16 ...\n");
|
||||
#if CV_SIMD_FP16
|
||||
AlignedData<v_float32> data;
|
||||
|
||||
// check conversion
|
||||
v_float32 r1 = vx_load(data.a.d);
|
||||
v_float16 r2 = v_cvt_f16(r1, vx_setzero_f32());
|
||||
v_float32 r3 = v_cvt_f32(r2);
|
||||
EXPECT_EQ(0x3c00, v_get0(r2));
|
||||
EXPECT_EQ(1, v_get0(r2));
|
||||
EXPECT_EQ(v_get0(r3), v_get0(r1));
|
||||
#else
|
||||
std::cout << "SKIP: test_float_cvt_fp16, CV_SIMD_FP16 is not available" << std::endl;
|
||||
#endif
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void do_check_cmp64(const Data<R>& dataA, const Data<R>& dataB)
|
||||
{
|
||||
@@ -2029,11 +2202,32 @@ void test_hal_intrin_float16()
|
||||
{
|
||||
DUMP_ENTRY(v_float16);
|
||||
#if CV_FP16
|
||||
TheTest<v_float32>()
|
||||
.test_loadstore_fp16_f32()
|
||||
TheTest<v_float32>().test_loadstore_fp16_f32();
|
||||
#if CV_SIMD_FP16
|
||||
TheTest<v_float16>()
|
||||
.test_loadstore_fp16()
|
||||
.test_float_cvt_fp16()
|
||||
.test_interleave()
|
||||
.test_addsub()
|
||||
.test_mul()
|
||||
.test_div_fp16()
|
||||
.test_abs_fp16()
|
||||
.test_cmp()
|
||||
.test_sqrt_abs()
|
||||
.test_min_max()
|
||||
.test_float_absdiff()
|
||||
.test_mask()
|
||||
.test_unpack()
|
||||
.test_float_math()
|
||||
.test_matmul_fp16()
|
||||
.test_transpose8x8_fp16()
|
||||
.test_reduce_sum8()
|
||||
.test_reverse()
|
||||
.test_extract<0>().test_extract<1>().test_extract<4>().test_extract<7>()
|
||||
.test_rotate<0>().test_rotate<1>().test_rotate<4>().test_rotate<7>()
|
||||
.test_extract_highest()
|
||||
.test_broadcast_element<0>().test_broadcast_element<1>()
|
||||
.test_extract_n<0>().test_extract_n<1>()
|
||||
#endif
|
||||
;
|
||||
#else
|
||||
@@ -2041,17 +2235,6 @@ void test_hal_intrin_float16()
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*#if defined(CV_CPU_DISPATCH_MODE_FP16) && CV_CPU_DISPATCH_MODE == FP16
|
||||
void test_hal_intrin_float16()
|
||||
{
|
||||
TheTest<v_float16>()
|
||||
.test_loadstore_fp16()
|
||||
.test_float_cvt_fp16()
|
||||
;
|
||||
}
|
||||
#endif*/
|
||||
|
||||
#endif //CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
|
||||
|
||||
//CV_CPU_OPTIMIZATION_NAMESPACE_END
|
||||
|
||||
Reference in New Issue
Block a user