【问题标题】:matlab if statements with CUDAmatlab if 语句与 CUDA
【发布时间】:2012-02-21 17:17:28
【问题描述】:

我有以下matlab代码:

randarray = gpuArray(rand(N,1));
N = 1000;

tic
g=0;
for i=1:N

    if randarray(i)>10
        g=g+1;
    end

end
toc

secondrandarray = rand(N,1);
 g=0;

 tic 
for i=1:N

    if secondrandarray(i)>10
        g=g+1;
    end

end
toc



Elapsed time is 0.221710 seconds.
Elapsed time is 0.000012 seconds.

1) 为什么 GPU 上的 if 子句这么慢?它正在减慢我所有的优化尝试

2) 我可以做些什么来绕过这个限制?

谢谢

【问题讨论】:

    标签: matlab cuda


    【解决方案1】:

    无论您是在 cpu 还是 gpu 上执行此操作,这通常都是一件坏事。

    以下将是执行您正在查看的操作的好方法。

    N = 1000;
    randarray = gpuArray(100 * rand(N,1));
    tic
    g = nnz(randarray > 10);
    toc
    

    我没有 PCT,我无法验证这是否真的有效(GPU 支持的功能数量相当有限)。

    但是,如果您有Jacket,您肯定可以执行以下操作。

    N = 1000;
    randarray = gdouble(100 * rand(N, 1));
    tic
    g = nnz(randarray > 10);
    toc
    

    完全披露:我是开发夹克的工程师之一。

    【讨论】:

    • 所以在使用 CUDA 时我应该避免 if/then 像魔鬼一样?
    • RRs_Ghost,这是个好主意。当您使用 if / while 和其他命令时,它会将 mem 复制回主机。在 for 循环中使用一堆小的(1 个元素)mem 副本是个坏主意。
    • @Pavan:我不相信PCT中当前的gpuArray实现支持nnz。
    • @talonmies 正如我所说,我无法验证它,但夹克可以:)。如果您可以访问 PCT,您能否检查它们是否在同一段代码中支持 sum 而不是 nnz。它们应该是等价的。
    • @RRs_Ghost:特别是避免使用 CUDA 的 if/then,原因有很多,但一般来说,避免循环中的 if 语句,而在 Matlab 中,避免循环。
    【解决方案2】:

    没有 Matlab gpuArray 实现方面的专家,但我怀疑第一个循环中的每个 randarray(i) 访问都会触发 PCI-e 事务以从 GPU 内存中检索一个值,这将导致非常大的延迟损失。调用gather 以在单个事务中传输整个数组,然后循环访问主机内存中的本地副本可能会更好。

    【讨论】:

      【解决方案3】:

      在现在相当旧的 GPU (Tesla C1060) 上使用 MATLAB R2011b 和 Parallel Computing Toolbox,我看到的是:

      >> g = 100*parallel.gpu.GPUArray.rand(1, 1000);
      >> tic, sum(g>10); toc
      Elapsed time is 0.000474 seconds.
      

      一次对gpuArray 的标量元素进行操作总是很慢,因此使用sum 方法要快得多。

      【讨论】:

        【解决方案4】:

        我无法评论先前的解决方案,因为我太新了,但我对 Pavan 的解决方案进行了扩展。 nnz 函数(尚未)为 gpuArrays 实现,至少在我使用的 Matlab 版本(R2012a)上。

        一般来说,向量化 Matlab 代码要好得多。但是,在某些情况下,由于 JIT 编译,循环代码可以在 Matlab 中快速运行。

        检查结果

        N = 1000;
        randarray_cpu = rand(N,1);
        randarray_gpu = gpuArray(randarray_cpu);
        threshold     = 0.5;
        
        % CPU: looped
        g=0;
        tic
        for i=1:N
            if randarray_cpu(i)>threshold
                g=g+1;
            end
        end
        toc
        
        % CPU: vectorized
        tic
        g = nnz(randarray_cpu>threshold);
        toc
        
        % GPU: looped
        tic
        g=0;
        for i=1:N
            if randarray_gpu(i)>threshold
                g=g+1;
            end
        end
        toc
        
        % GPU: vectorized
        tic
        g_d = sum(randarray_gpu > threshold);
        g = gather(g_d); % I'm assuming that you want this in the CPU at some point
        toc
        

        这是(在我的核心 i7+ GeForce 560Ti 上):

        Elapsed time is 0.000014 seconds.
        Elapsed time is 0.000580 seconds.
        Elapsed time is 0.310218 seconds.
        Elapsed time is 0.000558 seconds.
        

        所以我们从这个案例中看到的是:

        Matlab 中的循环不被认为是好的实践,但在您的特定情况下,它运行得很快,因为 Matlab 以某种方式在内部“预编译”它。我将您的阈值从 10 更改为 0.5,因为 rand 永远不会给您高于 1 的值。

        循环 GPU 版本的性能非常糟糕,因为在每次循环迭代时,都会启动一个内核(或从 GPU 读取数据,但是 TMW 实现了...),这很慢。在 GPU 上做的最糟糕的事情是大量的小内存传输而基本上什么都不计算。

        从最后一个(最好的)GPU 结果来看,答案是:除非数据已经在 GPU 上,否则在 GPU 上计算它是没有意义的。由于您的操作的算术复杂性基本上不存在,因此内存传输开销不会以任何方式得到回报。如果这是更大的 GPU 计算的一部分,那没关系。如果不是...最好坚持 CPU ;)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-05-24
          • 1970-01-01
          • 1970-01-01
          • 2017-08-29
          • 1970-01-01
          • 2014-07-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多