在体验了OpenCV-OpenCL的使用之后,发现CPU和GPU之间的数据传输相当的耗时,既然我这个小菜鸟都发现了这个瓶颈,大佬们肯定也早就发现也有一些解决方案吧!在网上看到一篇与这个有关的文章《OpenCL2.0特性之SVM》,可以先了解一下,看完之后有所收获,但也有许多疑问如下:

        1、SVM支持CPU和GPU之间不需要数据拷贝就可以数据共享,那这个SVM是软件实现的还是硬件实现的?如果是软件实现的,那么是不是只要GPU的驱动版本为opencl2.0之后的就可以支持SVM?

        2、SVM是不是需要硬件(CPU和GPU可以统一寻址的内存,就是数据都同时在同一块物理内存上操作的硬件?)配合才能实现?如果单纯的由软件实现,CPU数据在CPU内存,GPU数据在显存,他们之间要实现数据共享,因为两者的物理内存不统一,只能通过COPY或者说MAP操作不是吗?那这样不还是要在两者之间进行数据拷贝?那么这种SVM还有什么意义呢?

        于是,我开始重新编译opencv的opencl支持SVM特性,在cmake的时候勾选如下即可(我发现opencv2.x是没有SVM特性可以选择的,由此说明opencv2.x使用的opencl版本是1.2,这边使用的是opencv3.x的源码):

torch使用gpu共享内存 gpu共享内存可用吗_共享虚拟内存

编译之后的opencv-opencl就支持SVM了。

那么如何看现在的版本支持的是opencl的那个版本以及是否支持SVM呢?通过以下代码就可以知道:

std::vector<cv::ocl::PlatformInfo> plats;
cv::ocl::getPlatfomsInfo(plats);
const cv::ocl::PlatformInfo *platform = &plats[0];
std::cout << "Platform name: " << platform->name().c_str() << std::endl;
std::cout << "OpenCL CL_PLATFORM_VERSION: " << platform->version().c_str() << std::endl;
std::cout << "OpenCL CL_PLATFORM_VENDOR: " << platform->vendor().c_str() << std::endl;
std::cout << "OpenCL deviceNumber: " << platform->deviceNumber() << std::endl;
cv::ocl::Device current_device;
platform->getDevice(current_device, 0);
std::cout << "Device name: " << current_device.name().c_str() << std::endl;
current_device.set(0);

bool is_have_opencl		= cv::ocl::haveOpenCL();
bool is_have_svm		= cv::ocl::haveSVM();
bool is_use_opencl		= cv::ocl::useOpenCL();
bool is_have_amd_blas	= cv::ocl::haveAmdBlas();
bool is_have_amd_fft	= cv::ocl::haveAmdFft();
std::cout << "is_have_opencl:"		<< is_have_opencl	<< std::endl;
std::cout << "is_have_amd_blas:"	<< is_have_amd_blas << std::endl;
std::cout << "is_have_amd_fft:"		<< is_have_amd_fft	<< std::endl;
std::cout << "is_have_svm:"			<< is_have_svm		<< std::endl;
std::cout << "is_use_opencl:"		<< is_use_opencl	<< std::endl;

cv::ocl::setUseOpenCL(true);


在没有勾选SVM特性的CV库跑起来的结果如下:

torch使用gpu共享内存 gpu共享内存可用吗_共享虚拟内存_02


可以看到当前的CV-OCL版本是2.0,但是没有使用SVM。

在勾选了SVM特性的CV库跑起来的结果如下:

torch使用gpu共享内存 gpu共享内存可用吗_GPU_03

可以看到当前的CV-OCL版本是2.0,并且有SVM特性。

所以,还是测试之前的灰度模板匹配算法是否有了更快的速度,代码如下:

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/core/core.hpp> 
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/flann/flann.hpp>
#include <opencv2/core/ocl.hpp> 
#include <iostream>
#include <string>

#define SRC_IMG "1.bmp"
#define TMP_IMG "tmp.png"

using namespace cv;
using namespace cv::ocl;
using namespace std;

void initOpenCL();
void runMatchGrayUseCpu(int method);
void runMatchGrayUseGpu(int method);

int main(int argc, char **argv){

	initOpenCL();
	int method = CV_TM_SQDIFF;
	runMatchGrayUseCpu(method);
	runMatchGrayUseGpu(method);
	return 0;
}

void initOpenCL(){

	// launch OpenCL environment...  
	std::vector<cv::ocl::PlatformInfo> plats;
	cv::ocl::getPlatfomsInfo(plats);
	const cv::ocl::PlatformInfo *platform = &plats[0];
	std::cout << "Platform name: " << platform->name().c_str() << std::endl;
	std::cout << "OpenCL CL_PLATFORM_VERSION: " << platform->version().c_str() << std::endl;
	std::cout << "OpenCL CL_PLATFORM_VENDOR: " << platform->vendor().c_str() << std::endl;
	std::cout << "OpenCL deviceNumber: " << platform->deviceNumber() << std::endl;
	cv::ocl::Device current_device;
	platform->getDevice(current_device, 0);
	std::cout << "Device name: " << current_device.name().c_str() << std::endl;
	current_device.set(0);
	
	bool is_have_opencl		= cv::ocl::haveOpenCL();
	bool is_have_svm		= cv::ocl::haveSVM();
	bool is_use_opencl		= cv::ocl::useOpenCL();
	bool is_have_amd_blas	= cv::ocl::haveAmdBlas();
	bool is_have_amd_fft	= cv::ocl::haveAmdFft();
	std::cout << "is_have_opencl:"		<< is_have_opencl	<< std::endl;
	std::cout << "is_have_amd_blas:"	<< is_have_amd_blas << std::endl;
	std::cout << "is_have_amd_fft:"		<< is_have_amd_fft	<< std::endl;
	std::cout << "is_have_svm:"			<< is_have_svm		<< std::endl;
	std::cout << "is_use_opencl:"		<< is_use_opencl	<< std::endl;

	cv::ocl::setUseOpenCL(true);
}

void runMatchGrayUseCpu(int method){

	std::cout << "===Test Match Template Use CPU===" << std::endl;

	double t = 0.0;

	cv::Mat src = cv::imread(SRC_IMG, 1);
	cv::Mat tmp = cv::imread(TMP_IMG, 1);

	cv::Mat gray_src, gray_tmp;
	if (src.channels() == 1) gray_src = src;
	else cv::cvtColor(src, gray_src, CV_RGB2GRAY);
	if (tmp.channels() == 1) gray_tmp = tmp;
	else cv::cvtColor(tmp, gray_tmp, CV_RGB2GRAY);

	int result_cols = gray_src.cols - gray_tmp.cols + 1;
	int result_rows = gray_src.rows - gray_tmp.rows + 1;
	cv::Mat result = cv::Mat(result_cols, result_rows, CV_32FC1);

	t = (double)cv::getTickCount();
	cv::matchTemplate(gray_src, gray_tmp, result, method);
	t = ((double)cv::getTickCount() - t) / cv::getTickFrequency();

	cv::Point point;
	double minVal, maxVal;
	cv::Point minLoc, maxLoc;
	cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
	switch (method){

	case CV_TM_SQDIFF:
		point = minLoc;
		break;
	case CV_TM_SQDIFF_NORMED:
		point = minLoc;
		break;
	case CV_TM_CCORR:
	case CV_TM_CCOEFF:
		point = maxLoc;
		break;
	case CV_TM_CCORR_NORMED:
	case CV_TM_CCOEFF_NORMED:
	default:
		point = maxLoc;
		break;
	}
	

	std::cout << "CPU time :" << t << " second" << std::endl;
	std::cout << "obj.x :" << point.x << " obj.y :" << point.y << std::endl;
	std::cout << " " << std::endl;
}

void runMatchGrayUseGpu(int method){

	std::cout << "===Test Match Template Use GPU===" << std::endl;

	double t = 0.0;

	cv::UMat src = cv::imread(SRC_IMG, 1).getUMat(cv::ACCESS_RW);
	cv::UMat tmp = cv::imread(TMP_IMG, 1).getUMat(cv::ACCESS_RW);
	cv::UMat gray_src, gray_tmp;
	
	
	if (src.channels() == 1) gray_src = src;
	else cv::cvtColor(src, gray_src, CV_RGB2GRAY);
	if (tmp.channels() == 1) gray_tmp = tmp;
	else cv::cvtColor(tmp, gray_tmp, CV_RGB2GRAY);

	int result_cols = gray_src.cols - gray_tmp.cols + 1;
	int result_rows = gray_src.rows - gray_tmp.rows + 1;
	cv::UMat result = cv::UMat(result_cols, result_rows, CV_32FC1);


	t = (double)cv::getTickCount();
	cv::matchTemplate(gray_src, gray_tmp, result, method);
	t = ((double)cv::getTickCount() - t) / cv::getTickFrequency();

	cv::Point point;
	double minVal, maxVal;
	cv::Point minLoc, maxLoc;
	cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::UMat());
	
	switch (method){

	case CV_TM_SQDIFF:
		point = minLoc;
		break;
	case CV_TM_SQDIFF_NORMED:
		point = minLoc;
		break;
	case CV_TM_CCORR:
	case CV_TM_CCOEFF:
		point = maxLoc;
		break;
	case CV_TM_CCORR_NORMED:
	case CV_TM_CCOEFF_NORMED:
	default:
		point = maxLoc;
		break;
	}
	
	std::cout << "GPU time :" << t << " second" << std::endl;
	std::cout << "obj.x :" << point.x << " obj.y :" << point.y << std::endl;
	std::cout << " " << std::endl;
}

运行结果:

torch使用gpu共享内存 gpu共享内存可用吗_GPU_04


提示SVM的Capabilities为0。并且运行速度还是辣么的慢!那么这个SVMCapabilities是个什么东东呢?这个其实就是上面那篇文章所说的粗粒度缓冲SVM细粒度缓冲SVM细粒度系统SVM这三种SVM类型,GPU可能支持其中的一种、两种或者三种,或者0。

如下对于SVMCapabilities的说明引用来自《OpenCL2.0特性之SVM

对于SVM的创建,OpenCL2.0中有两种方式,一种缓冲分配(buffer allocation),另一种是系统分配(System allocation):
1、所谓缓冲分配,就是我们使用OpenCL API函数clSVMAlloc来分配,然后使用clSetKernelArgSVMPointer把分配的SVM作为内核参数传入
2、所谓系统分配,就是在主机端,我们可以使用malloc,new之类的系统分配内存函数来分配空间,然后使用clSetKernelArgSVMPointer把分配的SVM作为内核参数传入。
对于SVM的类型,OpenCL2.0也是有两种类型:一种是粗粒度;另一种是细粒度:
1、粗粒度SVM:共享发生在OpenCL缓冲内存对象区域粒度。在同步点强制内存一致性,使用map/unmap命令来更新主机与设备间的数据。粗粒度的SVM与OpenCL1.2中使用缓冲对象类似,不过唯一不同的是:我们不需要来回拷贝数据,设备与主机可以直接访问对方的数据,这才是重点!
2、所谓细粒度SVM:共享发生在OpenCL缓冲对象单个的加载/存储粒度。内存一致性在同步点得到保证。
好,结合SVM分配方式和SVM类型,可以把OpenCL2.0中的SVM分为:粗粒度缓冲SVM,细粒度缓冲SVM,细粒度系统SVM。(木有粗粒度系统SVM)。

进ocl.cpp可以看到以下代码(列出主要的出错地方代码):


#ifdef HAVE_OPENCL_SVM
bool svmInitialized;
bool svmAvailable;
bool svmEnabled;
svm::SVMCapabilities svmCapabilities;
svm::SVMFunctions svmFunctions;

void svmInit()
{
	CV_Assert(handle != NULL);
	const Device& device = devices[0];
	cl_device_svm_capabilities deviceCaps = 0;
	CV_Assert(((void)0, CL_DEVICE_SVM_CAPABILITIES == CL_DEVICE_SVM_CAPABILITIES_AMD)); // Check assumption
	cl_int status = clGetDeviceInfo((cl_device_id)device.ptr(), CL_DEVICE_SVM_CAPABILITIES, sizeof(deviceCaps), &deviceCaps, NULL);
	if (status != CL_SUCCESS)
	{
		CV_OPENCL_SVM_TRACE_ERROR_P("CL_DEVICE_SVM_CAPABILITIES via clGetDeviceInfo failed: %d\n", status);
		goto noSVM;
	}
	CV_OPENCL_SVM_TRACE_P("CL_DEVICE_SVM_CAPABILITIES returned: 0x%x\n", (int)deviceCaps);
	CV_Assert(((void)0, CL_DEVICE_SVM_COARSE_GRAIN_BUFFER == CL_DEVICE_SVM_COARSE_GRAIN_BUFFER_AMD)); // Check assumption
	// CL_DEVICE_SVM_COARSE_GRAIN_BUFFER,CL_DEVICE_SVM_FINE_GRAIN_BUFFER,CL_DEVICE_SVM_FINE_GRAIN_SYSTEM
	// 对应粗粒度缓冲SVM,细粒度缓冲SVM,细粒度系统SVM
	svmCapabilities.value_ =
			((deviceCaps & CL_DEVICE_SVM_COARSE_GRAIN_BUFFER) ? svm::SVMCapabilities::SVM_COARSE_GRAIN_BUFFER : 0) |
			((deviceCaps & CL_DEVICE_SVM_FINE_GRAIN_BUFFER) ? svm::SVMCapabilities::SVM_FINE_GRAIN_BUFFER : 0) |
			((deviceCaps & CL_DEVICE_SVM_FINE_GRAIN_SYSTEM) ? svm::SVMCapabilities::SVM_FINE_GRAIN_SYSTEM : 0) |
			((deviceCaps & CL_DEVICE_SVM_ATOMICS) ? svm::SVMCapabilities::SVM_ATOMICS : 0);
	svmCapabilities.value_ &= svm::getSVMCapabilitiesMask();
	if (svmCapabilities.value_ == -1)
	{
		// 代码死在这里,说明本GPU没有支持以上任何一种的SVM(GPU为 AMD Radeon R5 M430)
		CV_OPENCL_SVM_TRACE_ERROR_P("svmCapabilities is empty\n");
		goto noSVM;
	}
	try
	{
		// Try OpenCL 2.0
		CV_OPENCL_SVM_TRACE_P("Try SVM from OpenCL 2.0 ...\n");
		// ···略
	}
	// ···略
}

所以,我就开始纳闷了,既然读取出来的opencl版本是2.0的,理论上应该支持SVM,但是为什么是这个结果?

        于是我换个显卡,在集成显卡上跑下上面的代码,只要将上面代码的plats[0]改成plats[1]即可,前提得有集显。结果还是一样的,没有支持任何一种SVM,如下图:

torch使用gpu共享内存 gpu共享内存可用吗_SVM_05


这结果不顺我意啊···还是回答不了我上面的2个疑问啊!!!

        条件有限啊,我还想再其他的显卡上做一下实验啊···,可惜本人只有一个PC啊···所以将上面的那个代码打包成可执行文件(在这儿)不好意思啊,下载分系统不让我填0分···,希望有兴趣的下一下在电脑上跑一下看看什么情况···!也希望能解答上面两个问题的大佬高台贵手为小弟解惑一下下(我已经度娘了一大圈,没能找到满意的答案)!