C++ 计算速度比普通CPU慢?

C++ 计算速度比普通CPU慢?,c++,boost,opencl,boost-compute,C++,Boost,Opencl,Boost Compute,我刚开始玩Boost.Compute,为了看看它能给我们带来多大的速度,我写了一个简单的程序: #include <iostream> #include <vector> #include <algorithm> #include <boost/foreach.hpp> #include <boost/compute/core.hpp> #include <boost/compute/platform.hpp> #inclu

我刚开始玩Boost.Compute,为了看看它能给我们带来多大的速度,我写了一个简单的程序:

#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/foreach.hpp>
#include <boost/compute/core.hpp>
#include <boost/compute/platform.hpp>
#include <boost/compute/algorithm.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/functional/math.hpp>
#include <boost/compute/types/builtin.hpp>
#include <boost/compute/function.hpp>
#include <boost/chrono/include.hpp>

namespace compute = boost::compute;

int main()
{
    // generate random data on the host
    std::vector<float> host_vector(16000);
    std::generate(host_vector.begin(), host_vector.end(), rand);

    BOOST_FOREACH (auto const& platform, compute::system::platforms())
    {
        std::cout << "====================" << platform.name() << "====================\n";
        BOOST_FOREACH (auto const& device, platform.devices())
        {
            std::cout << "device: " << device.name() << std::endl;
            compute::context context(device);
            compute::command_queue queue(context, device);
            compute::vector<float> device_vector(host_vector.size(), context);

            // copy data from the host to the device
            compute::copy(
                host_vector.begin(), host_vector.end(), device_vector.begin(), queue
            );

            auto start = boost::chrono::high_resolution_clock::now();
            compute::transform(device_vector.begin(),
                       device_vector.end(),
                       device_vector.begin(),
                       compute::sqrt<float>(), queue);

            auto ans = compute::accumulate(device_vector.begin(), device_vector.end(), 0, queue);
            auto duration = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - start);
            std::cout << "ans: " << ans << std::endl;
            std::cout << "time: " << duration.count() << " ms" << std::endl;
            std::cout << "-------------------\n";
        }
    }
    std::cout << "====================plain====================\n";
    auto start = boost::chrono::high_resolution_clock::now();
    std::transform(host_vector.begin(),
                host_vector.end(),
                host_vector.begin(),
                [](float v){ return std::sqrt(v); });

    auto ans = std::accumulate(host_vector.begin(), host_vector.end(), 0);
    auto duration = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - start);
    std::cout << "ans: " << ans << std::endl;
    std::cout << "time: " << duration.count() << " ms" << std::endl;

    return 0;
}

我的问题是:为什么普通(非opencl)版本速度更快?

我可以看出造成巨大差异的一个可能原因。比较CPU和GPU数据流:-

CPU              GPU

                 copy data to GPU

                 set up compute code

calculate sqrt   calculate sqrt

sum              sum

                 copy data from GPU
[P>这样,英特尔芯片在通用计算中只是一个垃圾,英伟达可能会遭受额外数据的复制和GPU的计算。p> 您应该尝试相同的程序,但使用更复杂的操作-sqrt和sum太简单,无法克服使用GPU的额外开销。例如,您可以尝试计算Mandlebrot点


在您的示例中,将lambda移动到累加中会更快(一次通过内存而不是两次通过内存)

您得到的结果不好,因为您测量的时间不正确

OpenCL设备有自己的时间计数器,与主机计数器无关。每个OpenCL任务都有4个状态,可以查询其计时器:(从

  • CL\u PROFILING\u COMMAND\u QUEUED
    ,当主机将事件标识的命令排入命令队列时
  • CL\u PROFILING\u COMMAND\u SUBMIT
    ,当主机将已排队的事件标识的命令提交到与命令队列关联的设备时
  • CL\u PROFILING\u COMMAND\u START
    ,当事件标识的命令开始在设备上执行时
  • CL\u PROFILING\u COMMAND\u END
    ,当事件标识的命令在设备上完成执行时
  • 考虑到计时器是设备端的。因此,要测量内核和命令队列性能,您可以查询这些计时器。在您的情况下,需要两个最后的计时器

    在示例代码中,您测量的是主机时间,包括数据传输时间(正如Skizz所说的)加上命令队列维护所浪费的所有时间


    所以,要了解实际的内核性能,您需要将cl_事件传递给内核(不知道如何在boost::compute中实现)查询该事件以获取性能计数器或使内核变得非常庞大和复杂,以隐藏所有开销。

    正如其他人所说,内核中很可能没有足够的计算量,不值得在GPU上运行一组数据(您受到内核编译时间和向GPU传输时间的限制)

    为了获得更好的性能数据,您应该多次运行该算法(并且很可能会丢弃第一个算法,因为这会更大,因为它包括编译和存储内核的时间)

    此外,与单独运行和操作不同,您应该使用融合算法,该算法使用单个内核执行转换和还原。代码如下所示:

    float ans = 0;
    compute::transform_reduce(
        device_vector.begin(),
        device_vector.end(),
        &ans,
        compute::sqrt<float>(),
        compute::plus<float>(),
        queue
    );
    std::cout << "ans: " << ans << std::endl;
    
    float ans=0;
    compute::transform\u reduce(
    设备_vector.begin(),
    设备_vector.end(),
    &嗯,,
    compute::sqrt(),
    compute::plus(),
    队列
    );
    
    std::你可以不看代码就看一看,你的示例太小,无法进行性能比较…@user1535111,是的,我在这篇文章之前读过。@Jamboree那么你不认为差距来自内核的编译吗?@Jamboree看起来boost::compute会缓存编译后的内核,所以你可以使用boost::transform和boost::第一次计时前累积。
    transform\u reduce
    在这种情况下执行得更好,我还尝试了与自定义函数等效的
    acculate
    ,但它不如
    transform\u reduce
    ,结果有些不同。这是预期的。对于浮点加法(与整数加法不同,
    accumulate()
    将使用较慢的非并行代码路径。我的意思是测量主机时间,因为我想知道与正常解决方案相比OpenCL的性能如何。我认为设备端性能计数器更适合于比较用OpenCL编写的不同算法。
    float ans = 0;
    compute::transform_reduce(
        device_vector.begin(),
        device_vector.end(),
        &ans,
        compute::sqrt<float>(),
        compute::plus<float>(),
        queue
    );
    std::cout << "ans: " << ans << std::endl;