支持并行异构计算的有CUDA和OpenCL。NVIDIA的CUDA易用,开发简单,概念清晰,但仅适用NVIDIA自己的显卡,不能通用,为此NVIDIA正在积极通用化。OpenCL通用,由于照顾不同平台,制定比较早,概念有些费解,开发略复杂、繁琐,通用是最大亮点。
1. 安装开发工具:
1.1 安装 Microsoft Visual C++ 2017,默认安装目录,我安装的是Community不要钱。
1.2 安装 intel_sdk_for_opencl_2017,我的显卡是Intel集成主板自带的,默认安装目录。NVIDIA和AMD有自己的SDK。
2. 创建开发环境:
2.1 Visual C++ 2017 创建控制台程序。
2.2 包含编译文件
2.2.1 包含头文件:项目->属性->VC++目录->包含目录:C:\Intel\OpenCL\sdk\include
2.1.2 包含库文件:项目->属性->VC++目录->库目录:C:\Intel\OpenCL\sdk\lib\x86
2.1.3 包含链接库文件:项目->属性->链接器->输入->附加依赖项:OpenCL.lib
3.1 输入代码:
扫描二维码关注公众号,回复:
4289061 查看本文章
#include <cstdlib> #include <iostream> #include <iomanip> #include <cstring> #include <cassert> #include <windows.h> #define CL_USE_DEPRECATED_OPENCL_1_2_APIS // 定义使用OpenCL 1.2 #include <CL/cl.h> using namespace std; // 全局变量 _LARGE_INTEGER g_iSysFrequency,// 系统频率 iStartTestTime; // 开始测试时间 cl_int Err; cl_platform_id Selected_Platform_ID; // 已选择平台的ID cl_device_id DevicesID; // GPU设备 cl_context Context; // 设备管理 cl_command_queue CommandQueue; // 命令队列 cl_program program; // 程序对象 cl_mem memInutBuffer; // 输入内存对象 cl_mem memOutputBuffer; // 输出内存对象 cl_kernel Kernel; // 内核对象 // 错误检查宏 #define CheckErr(Err, PrintStr) \ if(Err != CL_SUCCESS) \ { \ printf("\n\n"); \ printf(" ");\ printf(PrintStr); \ printf("\n\n"); \ system("pause"); \ exit(1); \ } //--------------------------------------------------------------------------- void RunAsCpu(const float *nums1, const float *nums2, float* sum, const int num) { for (int i = 0; i < num; i++) { sum[i] = nums1[i] + nums2[i]; } } void StartTestTime(void)// 开始测量耗时 { QueryPerformanceCounter(&iStartTestTime);//开始计时 } double StopTestTime(int iTimeUnit)// 测量耗时 { _LARGE_INTEGER iStopTime; double fRetTime; QueryPerformanceCounter(&iStopTime);// 读停止时间 switch (iTimeUnit) { case 0: fRetTime = (double)(iStopTime.QuadPart - iStartTestTime.QuadPart); // ns break; case 1: fRetTime = (double)((iStopTime.QuadPart - iStartTestTime.QuadPart) / (g_iSysFrequency.QuadPart / 1000000)); // us break; case 2: fRetTime = (double)((iStopTime.QuadPart - iStartTestTime.QuadPart) / (g_iSysFrequency.QuadPart / 1000)); // ms break; case 3: fRetTime = (double)((iStopTime.QuadPart - iStartTestTime.QuadPart) / g_iSysFrequency.QuadPart); // S break; } return fRetTime; } void SelectedPlatform(void)//选择平台 { cl_uint PlatformCount; //平台数 cl_platform_id *pTotalPlatformtID;//所有平台数ID // 获取平台数目 Err = clGetPlatformIDs(0, 0, &PlatformCount);// 获取平台数 CheckErr(Err, "错误: OpenCL获取平台数错误!"); if (PlatformCount > 0) { cout << "可用平台数量: " << PlatformCount << endl; } else { printf("\n\n 错误: 没有可用OpenCL平台!\n\n"); system("pause"); exit(0); } // 获取所有平台ID pTotalPlatformtID = new cl_platform_id[PlatformCount];//动态创建所有平台ID数组 Err = clGetPlatformIDs(PlatformCount, pTotalPlatformtID, NULL);//获取所有平台ID(列表) CheckErr(Err, "错误: OpenCL获取所有平台ID错误。\n"); // 列出所有平台名称 printf("\n"); cout << "所有平台的名称: \n\n"; for (cl_uint i = 0; i < PlatformCount; ++i) { // 获取平台名称的长度 size_t Platform_Name_Length; Err = clGetPlatformInfo(pTotalPlatformtID[i], CL_PLATFORM_NAME, 0, 0, &Platform_Name_Length);// 获取平台名称字符长度 CheckErr(Err, "获取OpenCL平台名称长度错误。\n"); // 获取平台名称 char *Platform_Name = new char[Platform_Name_Length];//动态创建各平台名称字符串数组的长度 Err = clGetPlatformInfo(pTotalPlatformtID[i], CL_PLATFORM_NAME, Platform_Name_Length, Platform_Name, 0);// 获取平台名称 CheckErr(Err, "错误: 获取平台名称失败。\n"); cout << " [" << i << "] " << Platform_Name << "\n";// 输出平台名称 Selected_Platform_ID = pTotalPlatformtID[0];//总是选第一个 /*if (strcmp(Platform_Name, "Intel(R) OpenCL") == 0)// 选择平台ID及编号 { Selected_Platform_Num = i; Selected_Platform_ID = pTotalPlatformtID[i]; }*/ delete[] Platform_Name; } delete[] pTotalPlatformtID; } void CreateDevice(void)// 创建GPU设备 { Err = clGetDeviceIDs(Selected_Platform_ID, CL_DEVICE_TYPE_GPU, 1, &DevicesID, NULL);//获得GPU设备数量 CheckErr(Err, "错误: OpenCL创建GPU设备失败!"); } void CreateContext(void)// 创建设备管理 { Context = clCreateContext(0, 1, &DevicesID, NULL, NULL, &Err); CheckErr(Err, "错误: OpenCL创建设备环境失败!"); } void CreateCommandQueue(void)// 创建命令队列 { CommandQueue = clCreateCommandQueue(Context, DevicesID, 0, &Err); CheckErr(Err, "错误: OpenCL创建命令队列失败!"); } void GetProgramBuildInfo(void)// 获取异构(设备)编译程序信息 { char* build_log; size_t log_size; clGetProgramBuildInfo(program, DevicesID, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);// 获取编译信息长度 build_log = new char[log_size + 1]; clGetProgramBuildInfo(program, DevicesID, CL_PROGRAM_BUILD_LOG, log_size, build_log, NULL);// 查询编译信息 build_log[log_size] = '\0'; printf("\n异构(设备)编译信息:\n\n"); cout << build_log << endl; delete[] build_log; } //--------------------------------------------------------------------------- //异构代码,核函数 #define GPU_KERNEL_ARGS(...)#__VA_ARGS__ const char *KernelSourceCode = GPU_KERNEL_ARGS( __kernel void RunAsGpu(__global const float *nums1, __global const float *nums2, __global float* sum) { int id = get_global_id(0); sum[id] = nums1[id] + nums2[id]; } ); //--------------------------------------------------------------------------- int main() { QueryPerformanceFrequency(&g_iSysFrequency);//读系统频率 SelectedPlatform();// 选择平台 CreateDevice();// 创建GPU设备 CreateContext();// 创建设备环境 CreateCommandQueue();// 创建命令队列 //初始化测试数据 const int size = 38888888;//大小和内存有关,仅作示例 size_t global_work_size = size;//需要工作项(线程) float* nums1_h = new float[size];//动态创建 nums1_h 数组 float* nums2_h = new float[size];//动态创建 nums2_h 数组 float* sum_h = new float[size]; //动态创建 sum_h 数组 for (int i = 0; i < size; i++)//初始数据 { nums1_h[i] = nums2_h[i] = (float)i; } const int mem_size = sizeof(float) * size;//计算所需存储器 //创建设备缓冲区 cl_mem nums1_d = clCreateBuffer(Context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, mem_size, nums1_h, &Err);//nums1_d设备输入 cl_mem nums2_d = clCreateBuffer(Context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, mem_size, nums2_h, &Err);//nums2_d设备输入 cl_mem sum_d = clCreateBuffer(Context, CL_MEM_WRITE_ONLY, mem_size, NULL, &Err);//sum_d设备输出 if (nums1_d == 0 || nums2_d == 0 || sum_d == 0) { delete[] nums1_h; delete[] nums2_h; delete[] sum_h; clReleaseMemObject(nums1_d); clReleaseMemObject(nums2_d); clReleaseMemObject(sum_d); clReleaseCommandQueue(CommandQueue); clReleaseContext(Context); printf("\n错误:OpenCL创建设备缓冲区失败!\n"); system("pause"); exit(1);//退出 } // 创建程序对象(输入异构源代码) size_t Src_size[] = { strlen(KernelSourceCode) };//读入源代码数组 program = clCreateProgramWithSource(Context, 1, &KernelSourceCode, Src_size, &Err);// 创建程序对象(输入异构源代码) CheckErr(Err, "错误: OpenCL创建程序对象(输入异构源代码)失败!"); // 编译程序对象(编译异构源代码) Err = clBuildProgram(program, 1, &DevicesID, NULL, NULL, NULL); CheckErr(Err, "错误: OpenCL编译程序对象(编译异构源代码)失败!"); // 获取异构(设备)编译程序信息 GetProgramBuildInfo(); // 创建设备(核)程序函数 RunAsGpu cl_kernel RunAsGpu = clCreateKernel(program, "RunAsGpu", &Err);// 创建 RunAsGpu 设备核函数 if (Err != CL_SUCCESS) { delete[] nums1_h; delete[] nums2_h; delete[] sum_h; clReleaseMemObject(nums1_d); clReleaseMemObject(nums2_d); clReleaseMemObject(sum_d); clReleaseCommandQueue(CommandQueue); clReleaseContext(Context); clReleaseKernel(RunAsGpu); printf("\n错误:OpenCL创建设备(核)程序函数 RunAsGpu失败!\n"); system("pause"); exit(1);//退出 } // 输入设备(核)程序函数 RunAsGpu 形参 Err = clSetKernelArg(RunAsGpu, 0, sizeof(cl_mem), &nums1_d); CheckErr(Err, "错误: OpenCL输入设备(核)程序函数 RunAsGpu 0 形参失败!"); Err = clSetKernelArg(RunAsGpu, 1, sizeof(cl_mem), &nums2_d); CheckErr(Err, "错误: OpenCL输入设备(核)程序函数 RunAsGpu 1 形参失败!"); Err = clSetKernelArg(RunAsGpu, 2, sizeof(cl_mem), &sum_d); CheckErr(Err, "错误: OpenCL输入设备(核)程序函数 RunAsGpu 2 形参失败!"); // 运行设备(核)函数 StartTestTime(); Err = clEnqueueNDRangeKernel(CommandQueue, RunAsGpu, 1, NULL, &global_work_size, NULL, 0, NULL, NULL); CheckErr(Err, "错误: OpenCL运行设备(核)函数 RunAsGpu 失败!"); cout << "GPU 耗时: " << StopTestTime(0) << " ns" << endl; // 读设备返回值 float* gpu_sum = new float[size]; clEnqueueReadBuffer(CommandQueue, sum_d, CL_TRUE, 0, mem_size, gpu_sum, 0, NULL, NULL);//读设备缓冲区 // 运行CPU函数 StartTestTime(); RunAsCpu(nums1_h, nums2_h, sum_h, size); cout << "CPU 耗时: " << StopTestTime(2) << " ms" << endl; // 比较结果 if (memcmp(sum_h, gpu_sum, size * sizeof(float)) == 0)//数值比较 { printf("\n比较GPU和CPU计算数值正确。\n"); } else { printf("\n比较GPU和CPU计算数值错误!\n"); system("pause"); exit(1);//退出 } // 释放资源 delete[] gpu_sum; delete[] nums1_h; delete[] nums2_h; delete[] sum_h; clReleaseMemObject(nums1_d); clReleaseMemObject(nums2_d); clReleaseMemObject(sum_d); clReleaseCommandQueue(CommandQueue); clReleaseContext(Context); clReleaseKernel(RunAsGpu); // 殿后处理 printf("\n"); printf("运行成功!\n"); printf("\n"); system("pause"); }
4. 运行结果: