Skip to content

Commit 2922738

Browse files
insoowalalek
authored andcommitted
Merge pull request opencv#8104 from insoow:master
Gemm kernels for Intel GPU (opencv#8104) * Fix an issue with Kernel object reset release when consecutive Kernel::run calls Kernel::run launch OCL gpu kernels and set a event callback function to decreate the ref count of UMat or remove UMat when the lauched workloads are completed. However, for some OCL kernels requires multiple call of Kernel::run function with some kernel parameter changes (e.g., input and output buffer offset) to get the final computation result. In the case, the current implementation requires unnecessary synchronization and cleanupMat. This fix requires the user to specify whether there will be more work or not. If there is no remaining computation, the Kernel::run will reset the kernel object Signed-off-by: Woo, Insoo <insoo.woo@intel.com> * GEMM kernel optimization for Intel GEN The optimized kernels uses cl_intel_subgroups extension for better performance. Note: This optimized kernels will be part of ISAAC in a code generation way under MIT license. Signed-off-by: Woo, Insoo <insoo.woo@intel.com> * Fix API compatibility error This patch fixes a OCV API compatibility error. The error was reported due to the interface changes of Kernel::run. To resolve the issue, An overloaded function of Kernel::run is added. It take a flag indicating whether there are more work to be done with the kernel object without releasing resources related to it. Signed-off-by: Woo, Insoo <insoo.woo@intel.com> * Renaming intel_gpu_gemm.cpp to intel_gpu_gemm.inl.hpp Signed-off-by: Woo, Insoo <insoo.woo@intel.com> * Revert "Fix API compatibility error" This reverts commit 2ef427d. Conflicts: modules/core/src/intel_gpu_gemm.inl.hpp * Revert "Fix an issue with Kernel object reset release when consecutive Kernel::run calls" This reverts commit cc7f9f5. * Fix the case of uninitialization D When C is null and beta is non-zero, D is used without initialization. This resloves the issue Signed-off-by: Woo, Insoo <insoo.woo@intel.com> * fix potential output error due to 0 * nan Signed-off-by: Woo, Insoo <insoo.woo@intel.com> * whitespace fix, eliminate non-ASCII symbols * fix build warning
1 parent cea0e94 commit 2922738

File tree

5 files changed

+1316
-39
lines changed

5 files changed

+1316
-39
lines changed

modules/core/include/opencv2/core/ocl.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ class CV_EXPORTS Device
160160
uint imagePitchAlignment() const;
161161
uint imageBaseAddressAlignment() const;
162162

163+
bool intelSubgroupsSupport() const;
164+
163165
size_t image2DMaxWidth() const;
164166
size_t image2DMaxHeight() const;
165167

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright 2015-2017 Philippe Tillet
3+
* Copyright (c) 2017, Intel Corporation
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining
6+
* a copy of this software and associated documentation files
7+
* (the "Software"), to deal in the Software without restriction,
8+
* including without limitation the rights to use, copy, modify, merge,
9+
* publish, distribute, sublicense, and/or sell copies of the Software,
10+
* and to permit persons to whom the Software is furnished to do so,
11+
* subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be
14+
* included in all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23+
*/
24+
25+
#ifdef HAVE_OPENCL
26+
27+
#include <sstream>
28+
#include "precomp.hpp"
29+
#include "opencl_kernels_core.hpp"
30+
#include "opencv2/core/opencl/runtime/opencl_clamdblas.hpp"
31+
#include "opencv2/core/opencl/runtime/opencl_core.hpp"
32+
33+
namespace cv
34+
{
35+
36+
static bool intel_gpu_gemm(
37+
UMat A, Size sizeA,
38+
UMat B, Size sizeB,
39+
UMat D, Size sizeD,
40+
double alpha, double beta,
41+
bool atrans, bool btrans)
42+
{
43+
CV_UNUSED(sizeB);
44+
45+
int M = sizeD.height, N = sizeD.width, K = ((atrans)? sizeA.height : sizeA.width);
46+
47+
std::string kernelName;
48+
bool ret = true;
49+
50+
size_t lx = 8, ly = 4;
51+
size_t dx = 4, dy = 8;
52+
53+
if(!atrans && !btrans)
54+
{
55+
56+
if (M % 32 == 0 && N % 32 == 0 && K % 16 == 0)
57+
{
58+
kernelName = "intelblas_gemm_buffer_NN_sp";
59+
}
60+
else
61+
{
62+
kernelName = "intelblas_gemm_buffer_NN";
63+
}
64+
}
65+
else if(atrans && !btrans)
66+
{
67+
kernelName = "intelblas_gemm_buffer_TN";
68+
}
69+
else if(!atrans && btrans)
70+
{
71+
kernelName = "intelblas_gemm_buffer_NT";
72+
ly = 16;
73+
dx = 1;
74+
}
75+
else
76+
{
77+
kernelName = "intelblas_gemm_buffer_TT";
78+
}
79+
80+
const size_t gx = (size_t)(N + dx - 1) / dx;
81+
const size_t gy = (size_t)(M + dy - 1) / dy;
82+
83+
size_t local[] = {lx, ly, 1};
84+
size_t global[] = {(gx + lx - 1) / lx * lx, (gy + ly - 1) / ly * ly, 1};
85+
86+
int stride = (M * N < 1024 * 1024) ? 10000000 : 256;
87+
88+
ocl::Queue q;
89+
String errmsg;
90+
const ocl::Program program = ocl::Context::getDefault().getProg(ocl::core::intel_gemm_oclsrc, "", errmsg);
91+
92+
if(!atrans && btrans)
93+
{
94+
ocl::Kernel k(kernelName.c_str(), program);
95+
if (k.empty())
96+
{
97+
return false;
98+
}
99+
100+
k.args(ocl::KernelArg::PtrReadOnly(A),
101+
(int) (A.offset / sizeof(float)),
102+
ocl::KernelArg::PtrReadOnly(B),
103+
(int) (B.offset / sizeof(float)),
104+
ocl::KernelArg::PtrWriteOnly(D),
105+
(int) (D.offset / sizeof(float)),
106+
M, N, K,
107+
(float)alpha,
108+
(float)beta,
109+
(int)(A.step / sizeof(float)),
110+
(int)(B.step / sizeof(float)),
111+
(int)(D.step / sizeof(float)),
112+
(int) 0, // 14 start_index
113+
stride);
114+
115+
ret = k.run(2, global, local, false, q);
116+
}
117+
else
118+
{
119+
for(int start_index = 0; start_index < K; start_index += stride)
120+
{
121+
ocl::Kernel k(kernelName.c_str(), program);
122+
k.args(ocl::KernelArg::PtrReadOnly(A),
123+
(int) (A.offset / sizeof(float)),
124+
ocl::KernelArg::PtrReadOnly(B),
125+
(int) (B.offset / sizeof(float)),
126+
ocl::KernelArg::PtrWriteOnly(D),
127+
(int) (D.offset / sizeof(float)),
128+
M, N, K,
129+
(float)alpha,
130+
(float)beta,
131+
(int)(A.step / sizeof(float)),
132+
(int)(B.step / sizeof(float)),
133+
(int)(D.step / sizeof(float)),
134+
(int) start_index, // 14 start_index
135+
stride);
136+
137+
ret = k.run(2, global, local, false, q);
138+
if (!ret) return ret;
139+
}
140+
}
141+
142+
return ret;
143+
}
144+
145+
} // namespace cv
146+
147+
#endif

modules/core/src/matmul.cpp

Lines changed: 67 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@
4141
//
4242
//M*/
4343

44+
#include <sstream>
4445
#include "precomp.hpp"
4546
#include "opencl_kernels_core.hpp"
4647
#include "opencv2/core/opencl/runtime/opencl_clamdblas.hpp"
48+
#include "opencv2/core/opencl/runtime/opencl_core.hpp"
49+
#include "intel_gpu_gemm.inl.hpp"
4750

4851
namespace cv
4952
{
@@ -787,7 +790,6 @@ static bool ocl_gemm_amdblas( InputArray matA, InputArray matB, double alpha,
787790
#endif
788791

789792
#ifdef HAVE_OPENCL
790-
791793
static bool ocl_gemm( InputArray matA, InputArray matB, double alpha,
792794
InputArray matC, double beta, OutputArray matD, int flags )
793795
{
@@ -806,62 +808,88 @@ static bool ocl_gemm( InputArray matA, InputArray matB, double alpha,
806808
Size sizeA = matA.size(), sizeB = matB.size(), sizeC = haveC ? matC.size() : Size(0, 0);
807809
bool atrans = (flags & GEMM_1_T) != 0, btrans = (flags & GEMM_2_T) != 0, ctrans = (flags & GEMM_3_T) != 0;
808810

809-
if (atrans)
810-
sizeA = Size(sizeA.height, sizeA.width);
811-
if (btrans)
812-
sizeB = Size(sizeB.height, sizeB.width);
813-
if (haveC && ctrans)
814-
sizeC = Size(sizeC.height, sizeC.width);
815-
816-
Size sizeD(sizeB.width, sizeA.height);
817-
818811
CV_Assert( !haveC || matC.type() == type );
819-
CV_Assert( sizeA.width == sizeB.height && (!haveC || sizeC == sizeD) );
820-
821-
int max_wg_size = (int)dev.maxWorkGroupSize();
822-
int block_size = (max_wg_size / (32*cn) < 32) ? (max_wg_size / (16*cn) < 16) ? (max_wg_size / (8*cn) < 8) ? 1 : 8 : 16 : 32;
823812

813+
Size sizeD(((btrans)? sizeB.height : sizeB.width),
814+
((atrans)? sizeA.width : sizeA.height));
824815
matD.create(sizeD, type);
825816

826817
UMat A = matA.getUMat(), B = matB.getUMat(), D = matD.getUMat();
827818

828-
if (atrans)
829-
A = A.t();
830819

831-
if (btrans)
832-
B = B.t();
820+
if (!dev.intelSubgroupsSupport() || (depth == CV_64F) || cn != 1)
821+
{
822+
String opts;
833823

834-
if (haveC)
835-
ctrans ? transpose(matC, D) : matC.copyTo(D);
824+
if (atrans)
825+
sizeA = Size(sizeA.height, sizeA.width);
826+
if (btrans)
827+
sizeB = Size(sizeB.height, sizeB.width);
828+
if (haveC && ctrans)
829+
sizeC = Size(sizeC.height, sizeC.width);
830+
831+
CV_Assert( sizeA.width == sizeB.height && (!haveC || sizeC == sizeD) );
832+
833+
int max_wg_size = (int)dev.maxWorkGroupSize();
834+
int block_size = (max_wg_size / (32*cn) < 32) ? (max_wg_size / (16*cn) < 16) ? (max_wg_size / (8*cn) < 8) ? 1 : 8 : 16 : 32;
836835

837-
int vectorWidths[] = { 4, 4, 2, 2, 1, 4, cn, -1 };
838-
int kercn = ocl::checkOptimalVectorWidth(vectorWidths, B, D);
836+
if (atrans)
837+
A = A.t();
839838

840-
String opts = format("-D T=%s -D T1=%s -D WT=%s -D cn=%d -D kercn=%d -D LOCAL_SIZE=%d %s %s %s",
839+
if (btrans)
840+
B = B.t();
841+
842+
if (haveC)
843+
ctrans ? transpose(matC, D) : matC.copyTo(D);
844+
845+
int vectorWidths[] = { 4, 4, 2, 2, 1, 4, cn, -1 };
846+
int kercn = ocl::checkOptimalVectorWidth(vectorWidths, B, D);
847+
848+
opts += format(" -D T=%s -D T1=%s -D WT=%s -D cn=%d -D kercn=%d -D LOCAL_SIZE=%d %s %s %s",
841849
ocl::typeToStr(type), ocl::typeToStr(depth), ocl::typeToStr(CV_MAKETYPE(depth, kercn)),
842850
cn, kercn, block_size,
843851
(sizeA.width % block_size !=0) ? "-D NO_MULT" : "",
844852
haveC ? "-D HAVE_C" : "",
845853
doubleSupport ? " -D DOUBLE_SUPPORT" : "");
846854

847-
ocl::Kernel k("gemm", cv::ocl::core::gemm_oclsrc, opts);
848-
if (k.empty())
849-
return false;
855+
ocl::Kernel k("gemm", cv::ocl::core::gemm_oclsrc, opts);
856+
if (k.empty())
857+
return false;
858+
859+
if (depth == CV_64F)
860+
k.args(ocl::KernelArg::ReadOnlyNoSize(A),
861+
ocl::KernelArg::ReadOnlyNoSize(B, cn, kercn),
862+
ocl::KernelArg::ReadWrite(D, cn, kercn),
863+
sizeA.width, alpha, beta);
864+
else
865+
k.args(ocl::KernelArg::ReadOnlyNoSize(A),
866+
ocl::KernelArg::ReadOnlyNoSize(B, cn, kercn),
867+
ocl::KernelArg::ReadWrite(D, cn, kercn),
868+
sizeA.width, (float)alpha, (float)beta);
869+
870+
size_t globalsize[2] = { (size_t)sizeD.width * cn / kercn, (size_t)sizeD.height};
871+
size_t localsize[2] = { (size_t)block_size, (size_t)block_size};
850872

851-
if (depth == CV_64F)
852-
k.args(ocl::KernelArg::ReadOnlyNoSize(A),
853-
ocl::KernelArg::ReadOnlyNoSize(B, cn, kercn),
854-
ocl::KernelArg::ReadWrite(D, cn, kercn),
855-
sizeA.width, alpha, beta);
873+
return k.run(2, globalsize, block_size!=1 ? localsize : NULL, false);
874+
}
856875
else
857-
k.args(ocl::KernelArg::ReadOnlyNoSize(A),
858-
ocl::KernelArg::ReadOnlyNoSize(B, cn, kercn),
859-
ocl::KernelArg::ReadWrite(D, cn, kercn),
860-
sizeA.width, (float)alpha, (float)beta);
861-
862-
size_t globalsize[2] = { (size_t)sizeD.width * cn / kercn, (size_t)sizeD.height};
863-
size_t localsize[2] = { (size_t)block_size, (size_t)block_size};
864-
return k.run(2, globalsize, block_size!=1 ? localsize : NULL, false);
876+
{
877+
if (haveC && beta != 0.0)
878+
{
879+
ctrans ? transpose(matC, D) : matC.copyTo(D);
880+
}
881+
else
882+
{
883+
beta = 0.0;
884+
}
885+
886+
return intel_gpu_gemm(A, sizeA,
887+
B, sizeB,
888+
D, sizeD,
889+
alpha,
890+
beta,
891+
atrans, btrans);
892+
}
865893
}
866894
#endif
867895

modules/core/src/ocl.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,6 +1812,8 @@ struct Device::Impl
18121812
String deviceVersion_ = getStrProp(CL_DEVICE_VERSION);
18131813
parseDeviceVersion(deviceVersion_, deviceVersionMajor_, deviceVersionMinor_);
18141814

1815+
intelSubgroupsSupport_ = isExtensionSupported("cl_intel_subgroups");
1816+
18151817
vendorName_ = getStrProp(CL_DEVICE_VENDOR);
18161818
if (vendorName_ == "Advanced Micro Devices, Inc." ||
18171819
vendorName_ == "AMD")
@@ -1851,6 +1853,18 @@ struct Device::Impl
18511853
sz < sizeof(buf) ? String(buf) : String();
18521854
}
18531855

1856+
bool isExtensionSupported(const String& extensionName) const
1857+
{
1858+
bool ret = false;
1859+
size_t pos = getStrProp(CL_DEVICE_EXTENSIONS).find(extensionName);
1860+
if (pos != String::npos)
1861+
{
1862+
ret = true;
1863+
}
1864+
return ret;
1865+
}
1866+
1867+
18541868
IMPLEMENT_REFCOUNTABLE();
18551869
cl_device_id handle;
18561870

@@ -1866,6 +1880,7 @@ struct Device::Impl
18661880
String driverVersion_;
18671881
String vendorName_;
18681882
int vendorID_;
1883+
bool intelSubgroupsSupport_;
18691884
};
18701885

18711886

@@ -2072,6 +2087,9 @@ size_t Device::imageMaxArraySize() const
20722087
{ CV_REQUIRE_OPENCL_1_2_ERROR; }
20732088
#endif
20742089

2090+
bool Device::intelSubgroupsSupport() const
2091+
{ return p ? p->intelSubgroupsSupport_ : false; }
2092+
20752093
int Device::maxClockFrequency() const
20762094
{ return p ? p->getProp<cl_uint, int>(CL_DEVICE_MAX_CLOCK_FREQUENCY) : 0; }
20772095

0 commit comments

Comments
 (0)