▶ 向量前缀和

● 最简单的版本,代码

 #include <stdio.h>
#include <stdlib.h>
#include <cl.h> const char *programSource = " \
__kernel void prefixSum(__global int *input, int n) \
{ \
int offset, level, idx, lp, rp, temp; \
for (offset = , level = n / ; level > ; offset *= , level /= ) \
{ \
barrier(CLK_LOCAL_MEM_FENCE); \
for (idx = get_local_id(); idx < level; idx += get_local_size()) \
{ \
lp = offset * ( * idx + ) - ; \
rp = offset * ( * idx + ) - ; \
input[rp] = input[lp] + input[rp]; \
} \
} \
barrier(CLK_LOCAL_MEM_FENCE); \
if (get_local_id() == ) \
input[n - ] = ; \
\
for (level = ; level < n; level *= ) \
{ \
offset /= ; \
barrier(CLK_LOCAL_MEM_FENCE); \
for (idx = get_local_id(); idx < level; idx += get_local_size()) \
{ \
lp = offset * ( * idx + ) - ; \
rp = offset * ( * idx + ) - ; \
temp = input[lp]; \
input[lp] = input[rp]; \
input[rp] += temp; \
} \
} \
} \
"; int main()
{
const size_t nElement = , nWorkItem = , datasize = sizeof(int) * nElement;
const int n = nElement;
int i, *A, *C;
cl_int status; A = (int*)malloc(datasize);
C = (int*)malloc(datasize);
for (i = ; i < nElement; A[i] = , i++);
for (C[] = , i = ; i < nElement; C[i] = C[i - ] + A[i - ], i++); // 开前缀和
//for (C[0] = A[0], i = 1; i < nElement; C[i] = C[i - 1] + A[i], i++); // 闭前缀和 cl_uint nPlatform;
clGetPlatformIDs(, NULL, &nPlatform);
cl_platform_id *listPlatform = (cl_platform_id*)malloc(nPlatform * sizeof(cl_platform_id));
clGetPlatformIDs(nPlatform, listPlatform, NULL);
cl_uint nDevice = ;
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, , NULL, &nDevice);
cl_device_id *listDevice = (cl_device_id*)malloc(nDevice * sizeof(cl_device_id));
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, nDevice, listDevice, NULL);
cl_context context = clCreateContext(NULL, nDevice, listDevice, NULL, NULL, &status);
cl_command_queue queue = clCreateCommandQueue(context, listDevice[], , &status); cl_mem bufferA = clCreateBuffer(context, CL_MEM_READ_WRITE, datasize, NULL, &status);
clEnqueueWriteBuffer(queue, bufferA, CL_TRUE, , datasize, A, , NULL, NULL); cl_program program = clCreateProgramWithSource(context, , (const char**)&programSource, NULL, &status);
clBuildProgram(program, nDevice, listDevice, NULL, NULL, NULL);
cl_kernel kernel = clCreateKernel(program, "prefixSum", &status); clSetKernelArg(kernel, , sizeof(cl_mem), &bufferA);
clSetKernelArg(kernel, , sizeof(cl_int), &n);
status = clEnqueueNDRangeKernel(queue, kernel, , NULL, &nElement, &nWorkItem, , NULL, NULL);
clFinish(queue); clEnqueueReadBuffer(queue, bufferA, CL_TRUE, , datasize, A, , NULL, NULL);
for (i = ; i < nElement; i++)
{
if (A[i] != C[i])
break;
}
printf("Output is %s, %d.\n", (i == nElement) ? "correct" : "incorrect", i); free(A);
free(C);
free(listPlatform);
free(listDevice);
clReleaseContext(context);
clReleaseCommandQueue(queue);
clReleaseMemObject(bufferA);
clReleaseProgram(program);
clReleaseKernel(kernel);
getchar();
return ;
}

● 输出结果

Output is correct, .

● 核函数说明

 // 代码说明
void prefixSum(int *input, int n)
{
int offset, level, idx, lp, rp, temp;
for (offset = , level = n / ; level > ; offset *= , level /= )// level 为当前循环写入元素个数,每次循环处理所有跨度为 offset 的元素
{
barrier(CLK_LOCAL_MEM_FENCE);
for (idx = get_local_id(); idx < level; idx += get_local_size())// 每次循环处理 get_local_size(0) 对元素,靠前元素加到靠后元素上
{
lp = offset * ( * idx + ) - ;
rp = offset * ( * idx + ) - ;
input[rp] += input[lp];
}
}
barrier(CLK_LOCAL_MEM_FENCE);
if (get_local_id() == )// 主线程将最后一个元素置零,之后要向前传,注意到这里时 offset == n
input[n - ] = ;
for (level = ; level < n; level *= )// level 为当前循环写入元素对数,每次循环处理所有跨度为 offset 的元素
{
offset /= ;
barrier(CLK_LOCAL_MEM_FENCE);
for (idx = get_local_id(); idx < level; idx += get_local_size())// 每次循环处理 get_local_size(0) 对元素,靠前元素赋为靠后元素,靠后元素等于两者原值之和
{
lp = offset * ( * idx + ) - ;
rp = offset * ( * idx + ) - ;
temp = input[lp];
input[lp] = input[rp];
input[rp] += temp;
}
}
}

● 使用局部内存优化吗,C++ 版本

 // kernel.cl
__kernel void reduce(__global uint4* input, __global uint4* output, int NUM)
{
__local uint4 resArray[];
unsigned int lid = get_local_id(), globalSize = get_global_size();
int i;
uint4 res = (uint4) { , , , }; for (i = get_global_id(); i < NUM / ; res += input[i], i += globalSize);// 在局部内存中做第一次规约
resArray[lid] = res;
barrier(CLK_LOCAL_MEM_FENCE); for (i = get_local_size() >> ; i > ; i >>= )// 分发
{
if (lid < i)
resArray[lid] += resArray[lid + i];
barrier(CLK_LOCAL_MEM_FENCE);
} if (lid == )
output[get_group_id()] = resArray[];
}
 // main.cpp
#include <cl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <fstream> const char *filename = "kernel.cl"; using namespace std; int isVerify(int NUM, int groupNUM, int *res)// 校验结果
{
int i, sum;
for (i = sum = ; i < groupNUM * ; sum += res[i++]);
return sum == (NUM + ) * NUM / ;
} char* readSource(const char *filename)// 读取代码文件,C++ 版本
{
std::fstream f(filename, (std::fstream::in | std::fstream::binary));
if (!f.is_open())
{
cout << "<readSource> Error open file " << filename << "\n" << endl;
return nullptr;
}
f.seekg(, std::fstream::end);
size_t size = (size_t)f.tellg();
f.seekg(, std::fstream::beg);
char *str = new char[size + ];
if (str == nullptr)
{
cout << "<readSource> Error malloc memory\n" << endl;
f.close();
return nullptr;
}
f.read(str, size);
f.close();
str[size] = '\0';
return str;
} int main(int argc, char* argv[])
{
cl_int status;
cl_uint nPlatform;
clGetPlatformIDs(, NULL, &nPlatform);
cl_platform_id *listPlatform = (cl_platform_id*)malloc(nPlatform * sizeof(cl_platform_id));
clGetPlatformIDs(nPlatform, listPlatform, NULL);
cl_uint nDevice = ;
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, , NULL, &nDevice);
cl_device_id *listDevice = (cl_device_id*)malloc(nDevice * sizeof(cl_device_id));
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, nDevice, listDevice, NULL);
cl_context context = clCreateContext(NULL, nDevice, listDevice, NULL, NULL, &status);
cl_command_queue queue = clCreateCommandQueue(context, listDevice[], , &status); const char *source = readSource(filename);
const size_t sourceSize = strlen(source);
cl_program program = clCreateProgramWithSource(context, , &source, &sourceSize, NULL);
clBuildProgram(program, , listDevice, NULL, NULL, NULL);
cl_kernel kernel = clCreateKernel(program, "reduce", NULL); const int NUM = * ;
size_t global_work_size[] = { };
size_t local_work_size[] = { };
size_t groupNUM = global_work_size[] / local_work_size[];
int* input = new int[NUM];
for (int i = ; i < NUM; input[i] = i + , i++);
int* output = new int[(global_work_size[] / local_work_size[]) * ];
cl_mem inputBuffer = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, (NUM) * sizeof(int), (void *)input, NULL);
cl_mem outputBuffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY, groupNUM * * sizeof(int), NULL, NULL); clSetKernelArg(kernel, , sizeof(cl_mem), (void *)&inputBuffer);
clSetKernelArg(kernel, , sizeof(cl_mem), (void *)&outputBuffer);
clSetKernelArg(kernel, , sizeof(int), &NUM);
cl_event enentPoint;
clEnqueueNDRangeKernel(queue, kernel, , NULL, global_work_size, local_work_size, , NULL, &enentPoint);
clWaitForEvents(, &enentPoint);
clReleaseEvent(enentPoint); clEnqueueReadBuffer(queue, outputBuffer, CL_TRUE, , groupNUM * * sizeof(int), output, , NULL, NULL);
cout << "The result is " << (isVerify(NUM, groupNUM, output) ? "Correct" : "Error") << ".\n" << endl; free(input);
free(output);
free(listDevice);
clReleaseContext(context);
clReleaseCommandQueue(queue);
clReleaseProgram(program);
clReleaseKernel(kernel);
clReleaseMemObject(inputBuffer);
clReleaseMemObject(outputBuffer);
getchar();
return ;
}

● 输出结果

The result is correct.

OpenCL 前缀和的更多相关文章

  1. 基于SoCkit的opencl实验1-基础例程

    基于SoCkit的opencl实验1-基础例程 准备软硬件 Arrow SoCkit Board 4GB or larger microSD Card Quartus II v14.1 SoCEDS ...

  2. OpenCL与CUDA,CPU与GPU

    OpenCL OpenCL(全称Open Computing Language,开放运算语言)是第一个面向异构系统通用目的并行编程的开放式.免费标准,也是一个统一的编程环境,便于软件开发人员为高性能计 ...

  3. Android AARCH64 平台的 OpenCL 配置

    原文地址:Android AARCH64 平台的 OpenCL 配置 Android AARCH64 平台的 OpenCL 配置 开发环境 IDE: Android Studio 3.4.1 Andr ...

  4. HDU1671——前缀树的一点感触

    题目http://acm.hdu.edu.cn/showproblem.php?pid=1671 题目本身不难,一棵前缀树OK,但是前两次提交都没有成功. 第一次Memory Limit Exceed ...

  5. 【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

    之前以为BinaryWriter写string会严格按构造时指定的编码(不指定则是无BOM的UTF8)写入string的二进制,如下面的代码: //将字符串"a"写入流,再拿到流的 ...

  6. ASP.NET Core MVC 配置全局路由前缀

    前言 大家好,今天给大家介绍一个 ASP.NET Core MVC 的一个新特性,给全局路由添加统一前缀.严格说其实不算是新特性,不过是Core MVC特有的. 应用背景 不知道大家在做 Web Ap ...

  7. 如何处理CSS3属性前缀

    今天闲来无聊,重新来说说CSS3前缀的问题.在春节前和@一丝姐姐说起Sass中有关于gradient的mixins.姐姐说: 为什么还要用mixin呢?为什么不使用Autoprefixer?使用Aut ...

  8. context:component-scan" 的前缀 "context" 未绑定。

    SpElUtilTest.testSpELLiteralExpressiontestSpELLiteralExpression(cn.zr.spring.spel.SpElUtilTest)org.s ...

  9. 解决adobe air sdk打包 apk后自动在包名前面加上air. (有个点)前缀的问题

    早就找到了这个方法,但是一直忙没心思写博客. 默认情况下,所有 AIR Android 应用程序的包名称都带 air 前缀.若不想使用此默认行为,可将计算机环境变量 AIR_NOANDROIDFLAI ...

随机推荐

  1. 递归--练习8--noi1788Pell数列

    递归--练习8--noi1788Pell数列 一.心得 5 1. 6 //直接递归Time Limit Exceeded 7 //那就记忆化递归 8 2. 9 直接记忆化递归后还有问题 10 a[k] ...

  2. 利用JS将页面中包含“一个”字段值的元素改为红色

    document.body.innerHTML = document.body.innerHTML.replace(/一个/ig,"<span style='color: red;'& ...

  3. [linux]文件系统损坏,linux启动时 checking filesystems fail

    先敲root password进入maintenance状态,然后fsck -y /dev/mapper/vg_wwwdata-lv_root等干净了以后,再exit就行了. ------------ ...

  4. 数论练习(4)——同余方程(扩gcd)

    CODEVS 1200 同余方程 题目描述 Description 求关于 x 同余方程 ax ≡ 1 (mod b)的最小正整数解. 输入描述 Input Description 输入只有一行,包含 ...

  5. 【hive】关于用户留存率的计算

    首先用户留存率一般是面向新增用户的概念,是指某一天注册后的几天还是否活跃,是以每天为单位进行计算的.一般收到的需求都是一个时间段内的新增用户的几天留存 (1)找到这个时间段内的新增用户(也可能含有地区 ...

  6. asp.net(c#)开发中的文件上传组件uploadify的使用方法(带进度条)

    上文件传很常见,现在就文件上传利用HTML的File控件(uploadify)的,这里为大家介绍一下(uploadify)的一些使用方法.在目前Web开发中用的比较多的,可能uploadify(参考h ...

  7. mysql配置调优-开启慢查询日志-slow_query_log

    工作中,会遇到需要查看mysql的top 20 慢sql,逐个进行优化,加上必要的索引这种需求,这时就需要开启数据库的慢查询日志的功能 1.查询当前慢查询日志的状态 # 默认为关闭状态 mysql - ...

  8. 2017《Java技术》预备作业02

    1.学习使用Git和码云托管代码 参考资料:如何使用Git和码云 安装Git 在码云注册账号,新建项目,名称为Java-CS01(02)XXX, 一班为CS01,二班为CS02,后三位或两位为姓名缩写 ...

  9. 【C++基础】sort函数

    sort函数的时间复杂度为O(n*logn),排序方法类似于快排. # 头文件 #include<algorithm> using namespace std; # 参数 第一个参数:要排 ...

  10. Python中定时任务框架APScheduler

    前言 大家应该都知道在编程语言中,定时任务是常用的一种调度形式,在Python中也涌现了非常多的调度模块,本文将简要介绍APScheduler的基本使用方法. 一.APScheduler介绍 APSc ...