openCL緩存對象的傳輸與映射

用GPU進行加速運行運算時,通常首先將數據copy(clEnqueueWriteBuffer)到GPU緩存對象,運算結束後,再將數據copy(clEnqueueReadBuffer)到內存;OpenCL提供了內存映射機制,無需讀寫操作,將設備上的內存映射到主機上,便可以通過指針方式直接修改主機上的內存對象,內存映射的運行性能遠遠高於普通的讀寫函數。

用OpenCL來操作映射內存中的數據通常分爲三步:
Step1:調用函數clEnqueueMapBuffer或函數clEnqueueMapImage,將內存映射命令人列;
Step2:使用諸如memcpy之類的函數,對內存中的數據進行傳輸操作。
Step3:調用clEnqueueUnmapMemObject函數解映射。

5.clCreateBuffer(cl_context,   //上下文
              cl_mem_flags,    //內存對象的性質,見下表
              size_t,          //內存對象數據塊大小
              void *,          //host_ptr主機數據內存地址(可以爲空)
              cl_int *)        // err錯誤碼

cl_mem_flags:

 

CL_MEM_READ_WRITE(默認)

這三個是很好理解的,指明的是在kernel中對該緩衝區的訪問權限:讀寫,只寫,只讀。限制設備的讀寫權限的,而不是主機端讀寫權限。

CL_MEM_WRITE_ONLY

CL_MEM_READ_ONLY

CL_MEM_COPY_HOST_PTR

  1. host_ptr指針不能爲空;
  2. 由於是直接拷貝,Gpu操作buffer時不會影響host_ptr
  3. CL_MEM_USE_HOST_PTR與CL_MEM_COPY_HOST_PTR不能夠同時使用

CL_MEM_USE_HOST_PTR

  1. host_ptr指針不能爲空;
  2. 在device中開闢空間用host指針內存區域內的數據來初始化,device執行的時候直接使用device上的內存。
  3. 在Mali GPU上,將數據拷貝到另一塊區域,map的時候再拷貝回來,浪費時間,不建議使用該選項分配內存對象。
  4. 但是對於有單獨顯存的設備,此種分配內存對象的方式還有意義的。
  5. Gpu操作buffer時會影響host_ptr
  6. CL_MEM_USE_HOST_PTR與CL_MEM_ALLOC_HOST_PTR不能夠同時使用
  7. CL_MEM_USE_HOST_PTR與CL_MEM_COPY_HOST_PTR不能夠同時使用

CL_MEM_ALLOC_HOST_PTR

  1. 零拷貝內存
  2. 從host指針可以訪問的內存區域申請內存
  3. 在Mali系列的GPU上,這將有利於系統性能提升;不適合單獨顯存的設備。
  4. 獨顯存的設備CL_MEM_ALLOC_HOST_PTR和CL_MEM_COPY_HOST_PTR一起使用,比使用CL_MEM_USE_HOST_PTR要速度快。

後三個參數中,如果主機端的數據直接傳輸後不需要再讀取結果,就使用CL_MEM_COPY_HOST_PTR,

如果數據需要再讀取回來,則可以使用CL_MEM_ALLOC_HOST_PTR和CL_MEM_COPY_HOST_PTR一起或者CL_MEM_USE_HOST_PTR

demo: 

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <malloc.h>

#ifdef MAC
#include <OpenCL/cl.h>
#else
#include <CL/cl.h>
#endif

#ifdef WIN32
#include <windows.h>
#include <time.h>
#include <direct.h>
#include <io.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif

#define MAX_SOURCE_SIZE (0x100000)
#define LENGTH       10
#define KERNEL_FUNC  "addVec"

//#define MEM_MAP    //映射內存對象

int main(void)
{
	cl_platform_id *platformALL   = NULL;
	cl_uint ret_num_platforms;

	cl_device_id device_id = NULL;
	cl_uint ret_num_devices;

	cl_context context = NULL;
	cl_command_queue command_queue = NULL;
	
	cl_program program = NULL;
	cl_kernel kernel   = NULL;

	cl_int ret, err;

	const char   *kernel_src_str = "\n" \
		"__kernel void addVec(__global int *dataSrc){"\
		"float16 aa; int idx = get_global_id(0);"\
		"if (idx<10){"\
		"	dataSrc[idx] += 10;"\
		"}}";

	//Step1:獲取平臺列表
	err = clGetPlatformIDs(0, NULL, &ret_num_platforms);
	if (ret_num_platforms<1)
	{
		printf("Error: Getting Platforms; err = %d,numPlatforms = %d !", err, ret_num_platforms);
	}
	printf("Num of Getting Platforms = %d!\n", ret_num_platforms);

	platformALL = (cl_platform_id *)alloca(sizeof(cl_platform_id) * ret_num_platforms);
	ret = clGetPlatformIDs(ret_num_platforms, platformALL, &ret_num_platforms);
	
	//Step2:獲取指定設備,platformALL[0],platformALL[1]...
	//帶有獨顯的PC,選擇intel核顯或獨顯
	ret = clGetDeviceIDs(platformALL[0], CL_DEVICE_TYPE_DEFAULT, 1, &device_id, &ret_num_devices);

	char  nameDevice[64];
	clGetDeviceInfo(device_id, CL_DEVICE_NAME, sizeof(nameDevice), &nameDevice, NULL);
	printf("Device Name: %s\n", nameDevice);

	//Step3:創建上下文
	context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &ret);
	if (ret < 0)
	{
		printf("clCreateContext Fail,ret =%d\n", ret);
		exit(1);
	}

	//Step4:創建命令隊列
	command_queue = clCreateCommandQueue(context, device_id, 0, &ret);
	if (ret < 0)
	{
		printf("clCreateCommandQueue Fail,ret =%d\n", ret);
		exit(1);
	}

	//Step5:創建程序
	program = clCreateProgramWithSource(context, 1, (const char **)&kernel_src_str, NULL, &ret);
	if (ret < 0)
	{
		perror("clCreateProgramWithSource Fail\n");
		exit(1);
	}
	//Step6:編譯程序
	ret = clBuildProgram(program, 1, &device_id, NULL, NULL, NULL);
	if (ret < 0)
	{
		perror("clBuildProgram Fail\n");
		exit(1);
	}

	//Step7:創建內核
	kernel = clCreateKernel(program, KERNEL_FUNC, &ret);
	if (ret < 0)
	{
		perror("clCreateKernel Fail\n");
		exit(1);
	}
 
   printf("GPU openCL init Finish\n");

   cl_mem clDataSrcBuf;
   int dataSrcHost[LENGTH] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
	
   //創建緩存對象
#ifdef MEM_MAP
   clDataSrcBuf = clCreateBuffer(context, CL_MEM_READ_WRITE, 4 * LENGTH, NULL, &err);
#else
   clDataSrcBuf = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, 4 * LENGTH, dataSrcHost, &err);
#endif
	if (err < 0)
	{
		printf("clCreateBuffer imgSrcBuf Fail,err=%d\n", err);
		exit(1);
	}

#ifdef MEM_MAP
	cl_int * bufferMap = (cl_int *)clEnqueueMapBuffer(command_queue, clDataSrcBuf, CL_TRUE, CL_MAP_WRITE,
		0, LENGTH * sizeof(cl_int), 0, NULL, NULL, NULL);
	memcpy(bufferMap, dataSrcHost, 10 * sizeof(int));
#endif

	//設置內核參數
	err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &clDataSrcBuf);
    if (err < 0)
    {
        perror("clSetKernelArg imgDstBuf Fail\n");
        exit(1);
    }
 
    printf("GPU openCL Create and set Buffter Finish\n");
	cl_uint work_dim = 1;
	size_t  global_item_size = LENGTH;

	err = clEnqueueNDRangeKernel(command_queue, kernel, work_dim, NULL, &global_item_size, NULL, 0, NULL, NULL);
	clFinish(command_queue);
	if (err < 0)
	{
		printf("err:%d\n", err);
		perror("clEnqueueNDRangeKernel Fail\n");

	}

#ifndef MEM_MAP
	err = clEnqueueReadBuffer(command_queue, clDataSrcBuf, CL_TRUE, 0, (4 * LENGTH), dataSrcHost, 0, NULL, NULL);
	if (err < 0)
	{
		printf("err:%d\n", err);
		perror("Read buffer command Fail\n");
	
	}
#endif

	//print result
	for (int i = 0; i < LENGTH; i++)
	{
#ifdef MEM_MAP
		printf("dataSrcHost[%d]:%d\n", i, bufferMap[i]);
#else
		printf("dataSrcHost[%d]:%d\n", i, dataSrcHost[i]);
#endif
	}
	
#ifdef MEM_MAP
	err = clEnqueueUnmapMemObject(command_queue, clDataSrcBuf, bufferMap, 0, NULL, NULL);
#endif

	/* OpenCL Object Finalization */
	ret = clReleaseKernel(kernel);
	ret = clReleaseProgram(program);
	ret = clReleaseCommandQueue(command_queue);
	ret = clReleaseContext(context);

	return 0;
}

參考:

https://www.cnblogs.com/zenny-chen/p/3640870.html
https://blog.csdn.net/wd1603926823/article/details/78144547

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章