1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-26 05:43:05 +04:00
Files
opencv/modules/core/perf/opencl/perf_matop.cpp
T
Alexander Alekhin 40533dbf69 Merge pull request #24918 from opencv-pushbot:gitee/alalek/core_convertfp16_replacement
core(OpenCL): optimize convertTo() with CV_16F (convertFp16() replacement) #24918

relates #24909
relates #24917
relates #24892

Performance changes:

- [x] 12700K (1 thread) + Intel iGPU

|Name of Test|noOCL|convertFp16|convertTo BASE|convertTo PATCH|
|---|:-:|:-:|:-:|:-:|
|ConvertFP16FP32MatMat::OCL_Core|3.130|3.152|3.127|3.136|
|ConvertFP16FP32MatUMat::OCL_Core|3.030|3.996|3.007|2.671|
|ConvertFP16FP32UMatMat::OCL_Core|3.010|3.101|3.056|2.854|
|ConvertFP16FP32UMatUMat::OCL_Core|3.016|3.298|2.072|2.061|
|ConvertFP32FP16MatMat::OCL_Core|2.697|2.652|2.723|2.721|
|ConvertFP32FP16MatUMat::OCL_Core|2.752|4.268|2.662|2.947|
|ConvertFP32FP16UMatMat::OCL_Core|2.706|2.601|2.603|2.528|
|ConvertFP32FP16UMatUMat::OCL_Core|2.704|3.215|1.999|1.988|

Patched version is not worse than convertFp16 and convertTo baseline (except MatUMat 32->16, baseline uses CPU code+dst buffer map).
There are still gaps against noOpenCL(CPU only) mode due to T-API implementation issues (unnecessary synchronization).


- [x] 12700K + AMD dGPU

|Name of Test|noOCL|convertFp16 dGPU|convertTo BASE dGPU|convertTo PATCH dGPU|
|---|:-:|:-:|:-:|:-:|
|ConvertFP16FP32MatMat::OCL_Core|3.130|3.133|3.172|3.087|
|ConvertFP16FP32MatUMat::OCL_Core|3.030|1.713|9.559|1.729|
|ConvertFP16FP32UMatMat::OCL_Core|3.010|6.515|6.309|4.452|
|ConvertFP16FP32UMatUMat::OCL_Core|3.016|0.242|23.597|0.170|
|ConvertFP32FP16MatMat::OCL_Core|2.697|2.641|2.713|2.689|
|ConvertFP32FP16MatUMat::OCL_Core|2.752|4.076|6.483|4.191|
|ConvertFP32FP16UMatMat::OCL_Core|2.706|9.042|16.481|1.834|
|ConvertFP32FP16UMatUMat::OCL_Core|2.704|0.229|15.730|0.176|

convertTo-baseline can't compile OpenCL kernel for FP16 properly - FIXED.
dGPU has much more power, so results are x16-17 better than single cpu core. 
Patched version is not worse than convertFp16 and convertTo baseline.
There are still gaps against noOpenCL(CPU only) mode due to T-API implementation issues (unnecessary synchronization) and required memory transfers.

Co-authored-by: Alexander Alekhin <alexander.a.alekhin@gmail.com>
2024-01-26 12:56:52 +03:00

469 lines
12 KiB
C++

// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
#include "../perf_precomp.hpp"
#include "opencv2/ts/ocl_perf.hpp"
#ifdef HAVE_OPENCL
namespace opencv_test {
namespace ocl {
///////////// SetTo ////////////////////////
typedef Size_MatType SetToFixture;
OCL_PERF_TEST_P(SetToFixture, SetTo,
::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES))
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
const Scalar s = Scalar::all(17);
checkDeviceMaxMemoryAllocSize(srcSize, type);
UMat src(srcSize, type);
declare.in(src, WARMUP_RNG).out(src);
OCL_TEST_CYCLE() src.setTo(s);
SANITY_CHECK(src);
}
///////////// SetTo with mask ////////////////////////
typedef Size_MatType SetToFixture;
OCL_PERF_TEST_P(SetToFixture, SetToWithMask,
::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES))
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
const Scalar s = Scalar::all(17);
checkDeviceMaxMemoryAllocSize(srcSize, type);
UMat src(srcSize, type), mask(srcSize, CV_8UC1);
declare.in(src, mask, WARMUP_RNG).out(src);
OCL_TEST_CYCLE() src.setTo(s, mask);
SANITY_CHECK(src);
}
///////////// ConvertTo ////////////////////////
typedef Size_MatType ConvertToFixture;
OCL_PERF_TEST_P(ConvertToFixture, ConvertTo,
::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES))
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params), ddepth = CV_MAT_DEPTH(type) == CV_8U ? CV_32F : CV_8U,
cn = CV_MAT_CN(type), dtype = CV_MAKE_TYPE(ddepth, cn);
checkDeviceMaxMemoryAllocSize(srcSize, type);
checkDeviceMaxMemoryAllocSize(srcSize, dtype);
UMat src(srcSize, type), dst(srcSize, dtype);
declare.in(src, WARMUP_RNG).out(dst);
OCL_TEST_CYCLE() src.convertTo(dst, dtype);
SANITY_CHECK(dst);
}
//#define RUN_CONVERTFP16
static Size convertFP16_srcSize(4000, 4000);
OCL_PERF_TEST(Core, ConvertFP32FP16MatMat)
{
const Size srcSize = convertFP16_srcSize;
const int type = CV_32F;
const int dtype = CV_16F;
checkDeviceMaxMemoryAllocSize(srcSize, type);
checkDeviceMaxMemoryAllocSize(srcSize, dtype);
Mat src(srcSize, type);
Mat dst(srcSize, dtype);
declare.in(src, WARMUP_RNG).out(dst);
#ifdef RUN_CONVERTFP16
OCL_TEST_CYCLE() convertFp16(src, dst);
#else
OCL_TEST_CYCLE() src.convertTo(dst, dtype);
#endif
SANITY_CHECK_NOTHING();
}
OCL_PERF_TEST(Core, ConvertFP32FP16MatUMat)
{
const Size srcSize = convertFP16_srcSize;
const int type = CV_32F;
const int dtype = CV_16F;
checkDeviceMaxMemoryAllocSize(srcSize, type);
checkDeviceMaxMemoryAllocSize(srcSize, dtype);
Mat src(srcSize, type);
UMat dst(srcSize, dtype);
declare.in(src, WARMUP_RNG).out(dst);
#ifdef RUN_CONVERTFP16
OCL_TEST_CYCLE() convertFp16(src, dst);
#else
OCL_TEST_CYCLE() src.convertTo(dst, dtype);
#endif
SANITY_CHECK_NOTHING();
}
OCL_PERF_TEST(Core, ConvertFP32FP16UMatMat)
{
const Size srcSize = convertFP16_srcSize;
const int type = CV_32F;
const int dtype = CV_16F;
checkDeviceMaxMemoryAllocSize(srcSize, type);
checkDeviceMaxMemoryAllocSize(srcSize, dtype);
UMat src(srcSize, type);
Mat dst(srcSize, dtype);
declare.in(src, WARMUP_RNG).out(dst);
#ifdef RUN_CONVERTFP16
OCL_TEST_CYCLE() convertFp16(src, dst);
#else
OCL_TEST_CYCLE() src.convertTo(dst, dtype);
#endif
SANITY_CHECK_NOTHING();
}
OCL_PERF_TEST(Core, ConvertFP32FP16UMatUMat)
{
const Size srcSize = convertFP16_srcSize;
const int type = CV_32F;
const int dtype = CV_16F;
checkDeviceMaxMemoryAllocSize(srcSize, type);
checkDeviceMaxMemoryAllocSize(srcSize, dtype);
UMat src(srcSize, type);
UMat dst(srcSize, dtype);
declare.in(src, WARMUP_RNG).out(dst);
#ifdef RUN_CONVERTFP16
OCL_TEST_CYCLE() convertFp16(src, dst);
#else
OCL_TEST_CYCLE() src.convertTo(dst, dtype);
#endif
SANITY_CHECK_NOTHING();
}
OCL_PERF_TEST(Core, ConvertFP16FP32MatMat)
{
const Size srcSize = convertFP16_srcSize;
const int type = CV_16F;
const int dtype = CV_32F;
checkDeviceMaxMemoryAllocSize(srcSize, type);
checkDeviceMaxMemoryAllocSize(srcSize, dtype);
Mat src(srcSize, type);
Mat dst(srcSize, dtype);
declare.in(src, WARMUP_RNG).out(dst);
#ifdef RUN_CONVERTFP16
OCL_TEST_CYCLE() convertFp16(src, dst);
#else
OCL_TEST_CYCLE() src.convertTo(dst, dtype);
#endif
SANITY_CHECK_NOTHING();
}
OCL_PERF_TEST(Core, ConvertFP16FP32MatUMat)
{
const Size srcSize = convertFP16_srcSize;
const int type = CV_16F;
const int dtype = CV_32F;
checkDeviceMaxMemoryAllocSize(srcSize, type);
checkDeviceMaxMemoryAllocSize(srcSize, dtype);
Mat src(srcSize, type);
UMat dst(srcSize, dtype);
declare.in(src, WARMUP_RNG).out(dst);
#ifdef RUN_CONVERTFP16
OCL_TEST_CYCLE() convertFp16(src, dst);
#else
OCL_TEST_CYCLE() src.convertTo(dst, dtype);
#endif
SANITY_CHECK_NOTHING();
}
OCL_PERF_TEST(Core, ConvertFP16FP32UMatMat)
{
const Size srcSize = convertFP16_srcSize;
const int type = CV_16F;
const int dtype = CV_32F;
checkDeviceMaxMemoryAllocSize(srcSize, type);
checkDeviceMaxMemoryAllocSize(srcSize, dtype);
UMat src(srcSize, type);
Mat dst(srcSize, dtype);
declare.in(src, WARMUP_RNG).out(dst);
#ifdef RUN_CONVERTFP16
OCL_TEST_CYCLE() convertFp16(src, dst);
#else
OCL_TEST_CYCLE() src.convertTo(dst, dtype);
#endif
SANITY_CHECK_NOTHING();
}
OCL_PERF_TEST(Core, ConvertFP16FP32UMatUMat)
{
const Size srcSize = convertFP16_srcSize;
const int type = CV_16F;
const int dtype = CV_32F;
checkDeviceMaxMemoryAllocSize(srcSize, type);
checkDeviceMaxMemoryAllocSize(srcSize, dtype);
UMat src(srcSize, type);
UMat dst(srcSize, dtype);
declare.in(src, WARMUP_RNG).out(dst);
#ifdef RUN_CONVERTFP16
OCL_TEST_CYCLE() convertFp16(src, dst);
#else
OCL_TEST_CYCLE() src.convertTo(dst, dtype);
#endif
SANITY_CHECK_NOTHING();
}
///////////// CopyTo ////////////////////////
typedef Size_MatType CopyToFixture;
OCL_PERF_TEST_P(CopyToFixture, CopyTo,
::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES))
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
UMat src(srcSize, type), dst(srcSize, type);
declare.in(src, WARMUP_RNG).out(dst);
OCL_TEST_CYCLE() src.copyTo(dst);
SANITY_CHECK(dst);
}
///////////// CopyTo with mask ////////////////////////
typedef Size_MatType CopyToFixture;
OCL_PERF_TEST_P(CopyToFixture, CopyToWithMask,
::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES))
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
UMat src(srcSize, type), dst(srcSize, type), mask(srcSize, CV_8UC1);
declare.in(src, mask, WARMUP_RNG).out(dst);
OCL_TEST_CYCLE() src.copyTo(dst, mask);
SANITY_CHECK(dst);
}
OCL_PERF_TEST_P(CopyToFixture, CopyToWithMaskUninit,
::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), OCL_TEST_TYPES))
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
UMat src(srcSize, type), dst, mask(srcSize, CV_8UC1);
declare.in(src, mask, WARMUP_RNG);
for ( ; next(); )
{
dst.release();
startTimer();
src.copyTo(dst, mask);
cvtest::ocl::perf::safeFinish();
stopTimer();
}
SANITY_CHECK(dst);
}
enum ROIType {
ROI_FULL,
ROI_2_RECT,
ROI_2_TOP, // contiguous memory block
ROI_2_LEFT,
ROI_4,
ROI_16,
};
static Rect getROI(enum ROIType t, const Size& sz)
{
switch (t)
{
case ROI_FULL: return Rect(0, 0, sz.width, sz.height);
case ROI_2_RECT: return Rect(0, 0, sz.width * 71 / 100, sz.height * 71 / 100); // 71 = sqrt(1/2) * 100
case ROI_2_TOP: return Rect(0, 0, sz.width, sz.height / 2); // 71 = sqrt(1/2) * 100
case ROI_2_LEFT: return Rect(0, 0, sz.width / 2, sz.height); // 71 = sqrt(1/2) * 100
case ROI_4: return Rect(0, 0, sz.width / 2, sz.height / 2);
case ROI_16: return Rect(0, 0, sz.width / 4, sz.height / 4);
}
CV_Assert(false);
}
typedef TestBaseWithParam< tuple<cv::Size, MatType, ROIType> > OpenCLBuffer;
static inline void PrintTo(const tuple<cv::Size, MatType, enum ROIType>& v, std::ostream* os)
{
*os << "(" << get<0>(v) << ", " << typeToString(get<1>(v)) << ", ";
enum ROIType roiType = get<2>(v);
if (roiType == ROI_FULL)
*os << "ROI_100_FULL";
else if (roiType == ROI_2_RECT)
*os << "ROI_050_RECT_HALF";
else if (roiType == ROI_2_TOP)
*os << "ROI_050_TOP_HALF";
else if (roiType == ROI_2_LEFT)
*os << "ROI_050_LEFT_HALF";
else if (roiType == ROI_4)
*os << "ROI_025_1/4";
else
*os << "ROI_012_1/16";
*os << ")";
}
PERF_TEST_P_(OpenCLBuffer, cpu_write)
{
const Size srcSize = get<0>(GetParam());
const int type = get<1>(GetParam());
const Rect roi = getROI(get<2>(GetParam()), srcSize);
checkDeviceMaxMemoryAllocSize(srcSize, type);
UMat src(srcSize, type);
declare.in(src(roi), WARMUP_NONE);
OCL_TEST_CYCLE()
{
Mat m = src(roi).getMat(ACCESS_WRITE);
m.setTo(Scalar(1, 2, 3, 4));
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(OpenCLBuffer, cpu_read)
{
const Size srcSize = get<0>(GetParam());
const int type = get<1>(GetParam());
const Rect roi = getROI(get<2>(GetParam()), srcSize);
checkDeviceMaxMemoryAllocSize(srcSize, type);
UMat src(srcSize, type, Scalar(1, 2, 3, 4));
declare.in(src(roi), WARMUP_NONE);
OCL_TEST_CYCLE()
{
unsigned counter = 0;
Mat m = src(roi).getMat(ACCESS_READ);
for (int y = 0; y < m.rows; y++)
{
uchar* ptr = m.ptr(y);
size_t width_bytes = m.cols * m.elemSize();
for (size_t x_bytes = 0; x_bytes < width_bytes; x_bytes++)
counter += (unsigned)(ptr[x_bytes]);
}
(void)counter; // To avoid -Wunused-but-set-variable
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(OpenCLBuffer, cpu_update)
{
const Size srcSize = get<0>(GetParam());
const int type = get<1>(GetParam());
const Rect roi = getROI(get<2>(GetParam()), srcSize);
checkDeviceMaxMemoryAllocSize(srcSize, type);
UMat src(srcSize, type, Scalar(1, 2, 3, 4));
declare.in(src(roi), WARMUP_NONE);
OCL_TEST_CYCLE()
{
Mat m = src(roi).getMat(ACCESS_READ | ACCESS_WRITE);
for (int y = 0; y < m.rows; y++)
{
uchar* ptr = m.ptr(y);
size_t width_bytes = m.cols * m.elemSize();
for (size_t x_bytes = 0; x_bytes < width_bytes; x_bytes++)
ptr[x_bytes] += 1;
}
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(/*FULL*/, OpenCLBuffer,
testing::Combine(
testing::Values(szVGA, sz720p, sz1080p, sz2160p),
testing::Values(CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4),
testing::Values(ROI_FULL)
)
);
INSTANTIATE_TEST_CASE_P(ROI, OpenCLBuffer,
testing::Combine(
testing::Values(sz1080p, sz2160p),
testing::Values(CV_8UC1),
testing::Values(ROI_16, ROI_4, ROI_2_RECT, ROI_2_LEFT, ROI_2_TOP, ROI_FULL)
)
);
} } // namespace opencv_test::ocl
#endif // HAVE_OPENCL