首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在CPU上使用OpenCL将一个数组复制到另一个数组比C++代码慢得多。

在CPU上使用OpenCL将一个数组复制到另一个数组比C++代码慢得多。
EN

Stack Overflow用户
提问于 2013-11-20 07:33:52
回答 2查看 4.4K关注 0票数 2

我比较了运行在CPU上的OpenCL代码的性能,它简单地将数据从一个2D数组复制到另一个2D数组中,而纯C++代码则做了同样的事情。我在OpenCL代码中使用了一个工作组来进行公平的比较。我使用了英特尔的OpenCL驱动程序和英特尔编译器。OpenCL代码比CPU代码慢5倍。编译器为copy循环提供以下消息:

代码语言:javascript
复制
loop was transformed to memset or memcpy.

对于如何使OpenCL代码与C++代码同步,有什么建议吗?

谢谢

OpenCL主机代码:

代码语言:javascript
复制
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cmath>
#include <ctime>
#include <CL/cl.hpp>

int main(int argc, char **argv)
{
    // Create the two input vectors
    const int N = 8192;
    double *in = new double[N*N]; 
    double *out = new double[N*N];

    for(int i = 0; i < N; i++)
        for (int j=0; j < N; j++) {
            in[i*N + j] = i + j;
            out[i*N + j] = 0.;
    }


    double time;
    std::clock_t start;
    int niter = 100;

    cl_int cl_err;

    std::vector<cl::Platform> platforms;
    cl_err = cl::Platform::get(&platforms);

    std::vector<cl::Device> devices;
    cl_err = platforms.at(1).getDevices(CL_DEVICE_TYPE_CPU,
                                        &devices);

    cl_context_properties context_properties[3] = {CL_CONTEXT_PLATFORM,
                                    (cl_context_properties)(platforms.at(1)()),
                                                   0};
    cl::Context context = cl::Context(devices, 
                                      context_properties, 
                                      NULL, NULL, &cl_err);

    cl::Buffer buffer_in = cl::Buffer(context, 
                                      CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY,
                                      N*N*sizeof(double), 
                                      in, &cl_err);

    cl::Buffer buffer_out = cl::Buffer(context, 
                                       CL_MEM_USE_HOST_PTR | CL_MEM_WRITE_ONLY, 
                                       N*N*sizeof(double),
                                       out, &cl_err);

    cl::CommandQueue queue = cl::CommandQueue(context, devices.at(0), 0, &cl_err);

    std::ifstream sourceFile("vector_copy.cl");
    std::string sourceCode((std::istreambuf_iterator<char>(sourceFile)),
                            std::istreambuf_iterator<char>());
    cl::Program::Sources source(1, std::make_pair(sourceCode.c_str(),
                                sourceCode.length()+1));

    cl::Program program(context, source, &cl_err);

    cl_err = program.build(devices, NULL, NULL, NULL);

    cl::Kernel kernel(program, "vector_copy", &cl_err);

    cl_err = kernel.setArg(0, buffer_in); 
    cl_err = kernel.setArg(1, buffer_out);
    cl_err = kernel.setArg(2, N);

    cl::NDRange global(N);
    cl::NDRange local(N);

    start = std::clock();
    for (int n=0; n < niter; n++) {
        cl_err = queue.enqueueNDRangeKernel(kernel,
                                            cl::NullRange,
                                            global,
                                            local,
                                            NULL, NULL);

        cl_err = queue.finish();
    }

    time =  (std::clock() - start)/(double)CLOCKS_PER_SEC;
    std::cout << "Time/iteration OpenCL (s) = " << time/(double)niter << std::endl;

    return(0);
}

OpenCL内核代码:

代码语言:javascript
复制
__kernel void vector_copy(__global const double* restrict in, 
                          __global double* restrict out,
                         const int N) 
{

    int i = get_global_id(0);
    int j;

    for (j=0; j<N; j++) {
        out[j + N*i] = in[j + N*i];
    }

}

C++代码:

代码语言:javascript
复制
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cmath>
#include <ctime>

const int N = 8192;

int main(int argc, char **argv)
{
    double *in = new double[N*N]; 
    double *out = new double[N*N];
    // Create the two input vectors
    for(int i = 0; i < N; i++)
        for (int j=0; j < N; j++) {
            in[j + N*i] = i + j;
            out[j + N*i] = 0.;
    }


    std::clock_t start;
    int niter = 100;

    start = std::clock();
    for (int n=0; n < niter; n++) {
        for (int i=0; i<N; i++)
            for (int j=0; j<N; j++) {
                out[j + N*i] = in[j + N*i];
            }

    }

    double time =  (std::clock() - start)/(double)CLOCKS_PER_SEC;
    std::cout << "Time/iteration C = " << time/(double)niter << std::endl;

    return(0);
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-11-20 13:42:11

Intel OpenCL编译器能够跨工作组进行矢量化。基本上,一个函数在不同的SSE寄存器中同时运行8个线程。

您的特定内核不会这样做。但这并不重要。我使用Visual 2010和最新的Intel OpenCL测试了您的应用程序。我被迫将N从8192减少到4096,因为集成的GPU将最大OpenCL缓冲区大小减少到128 is,即使只使用CPU。

我的结果是:您的OpenCL内核给了我6956 My /s的带宽。一个变化很小的内核(调用它时使用N*N作为全局大小,NULL作为本地大小,因为如果我们根本不关心本地内存,那么对于CPU来说,我们应该不定义它)。

代码语言:javascript
复制
__kernel void vector_copy2(__global const double* restrict in, 
                      __global double* restrict out) 
{
  int i = get_global_id(0);
  out[i] = in[i];
}

给出了相同的结果(7006MB/s)。这个内核实际上是跨线程向量化的,可以使用Intel OpenCL内核编译器进行验证。它为多个线程(如4)生成一个内核,为单个线程生成一个内核。然后,它只运行向量化的内核,直到必须运行最后几个工作项的单线程内核。

C++代码提供了6494 in /s,因此它非常符合要求。我认为国际刑事法院甚至不可能将其速度提高5倍。

我注意到在您的代码中有platforms.at(1),您的计算机中的0平台是什么?

请记住,如果您根本不关心本地内存(内核中没有调用get_local_id ),那么应该将enqueueNDRange的本地大小作为一个简单的神奇参数来处理。要么将其保留为NULL,要么尝试找到一个生成最快结果的值。

票数 5
EN

Stack Overflow用户

发布于 2013-11-20 10:14:05

即使优化了OpenCL代码,它仍将执行1x1(工作项按工作项)的复制。因为只允许OpenCL编译器在每个工作项的基础上进行优化。虽然编译器可能会将C++情况优化为一个memcpy()调用(正如编译器告诉您的那样)。

如果禁用编译器优化,它将在GPU中执行得更快。

那么这有什么原因吗?为此,您有memcpy() in C++和OpenCL中的OpenCL。我认为后一种是你应该使用的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20089963

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档