Boost.Compute比普通CPU慢?

Jam*_*ree 6 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>
#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;
}
Run Code Online (Sandbox Code Playgroud)

这是我的机器上的示例输出(win7 64位):

====================Intel(R) OpenCL====================
device: Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
ans: 1931421
time: 64 ms
-------------------
device: Intel(R) HD Graphics 4600
ans: 1931421
time: 64 ms
-------------------
====================NVIDIA CUDA====================
device: Quadro K600
ans: 1931421
time: 4 ms
-------------------
====================plain====================
ans: 1931421
time: 0 ms
Run Code Online (Sandbox Code Playgroud)

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

Kyl*_*utz 8

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

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

此外,您应该使用融合算法,而不是运行transform()accumulate()作为单独的操作,它使用transform_reduce()单个内核执行转换和缩减.代码如下所示:

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;
Run Code Online (Sandbox Code Playgroud)

您还可以使用Boost.Compute编译代码,-DBOOST_COMPUTE_USE_OFFLINE_CACHE这将启用脱机内核缓存(这需要链接boost_filesystem).然后,您使用的内核将存储在您的文件系统中,并且仅在您第一次运行应用程序时进行编译(默认情况下,Linux上的NVIDIA已经执行此操作).


Ski*_*izz 2

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

CPU              GPU

                 copy data to GPU

                 set up compute code

calculate sqrt   calculate sqrt

sum              sum

                 copy data from GPU
Run Code Online (Sandbox Code Playgroud)

鉴于此,Intel 芯片在一般计算方面似乎有点垃圾,NVidia 可能会受到额外数据复制和设置 GPU 进行计算的困扰。

您应该尝试相同的程序,但使用更复杂的操作 - sqrt 和 sum 太简单,无法克服使用 GPU 的额外开销。例如,您可以尝试计算 Mandlebrot 点。

在您的示例中,将 lambda 移入累加会更快(一次通过内存与两次通过)