【问题标题】:Is the C++ AMP library useful from F#?C++ AMP 库对 F# 有用吗?
【发布时间】:2012-12-24 07:04:38
【问题描述】:

我正在尝试使用 F# 中的 C++ AMP 库作为使用 GPU 进行并行工作的一种方式。但是,我得到的结果似乎并不直观。

在 C++ 中,我用一个函数创建了一个库,该函数使用 AMP 对数组中的所有数字求平方:

extern "C" __declspec ( dllexport ) void _stdcall square_array(double* arr, int n)
{
// Create a view over the data on the CPU
    array_view<double,1> dataView(n, &arr[0]);

// Run code on the GPU
    parallel_for_each(dataView.extent, [=] (index<1> idx) restrict(amp)
    {
        dataView[idx] = dataView[idx] * dataView[idx];
    });

// Copy data from GPU to CPU
    dataView.synchronize();
}

(代码改编自 Igor Ostrovsky 在 MSDN 上的 blog。)

然后我编写了以下 F# 来将任务并行库 (TPL) 与 AMP 进行比较:

// Print the time needed to run the given function
let time f =
    let s = new Stopwatch()
    s.Start()
    f ()
    s.Stop()
    printfn "elapsed: %d" s.ElapsedTicks

module CInterop =
    [<DllImport("CPlus", CallingConvention = CallingConvention.StdCall)>]
    extern void square_array(float[] array, int length)

let options = new ParallelOptions()
let size = 1000.0
let arr = [|1.0 .. size|]
// Square the number at the given index of the array
let sq i =
    do arr.[i] <- arr.[i] * arr.[i]
    ()
// Square every number in the array using TPL
time (fun() -> Parallel.For(0, arr.Length - 1, options, new Action<int>(sq)) |> ignore)

let arr2 = [|1.0 .. size|]
// Square every number in the array using AMP
time (fun() -> CInterop.square_array(arr2, arr2.Length))

如果我将数组大小设置为 10 这样的微不足道的数字,则完成 TPL ~22K 滴答声和 AMP ~10K 滴答声。这就是我所期望的。据我了解,GPU(因此是 AMP)应该比 TPL 更适合这种情况,在这种情况下,工作被分解成非常小的部分。

但是,如果我将数组大小增加到 1000,TPL 现在需要约 30K 滴答,而 AMP 需要约 70K 滴答。从那里开始变得更糟。对于 100 万大小的数组,AMP 所需的时间几乎是 TPL 的 1000 倍。

由于我希望 GPU(即 AMP)能够更好地完成此类任务,我想知道我在这里缺少什么。

我的显卡是 1GB 的 GeForce 550 Ti,据我所知,还算不错。我知道使用 PInvoke 调用 AMP 代码会产生开销,但我希望这是一个固定成本,可以在更大的数组大小上摊销。我相信该数组是通过引用传递的(尽管我可能是错的),所以我不认为复制它会产生任何成本。

感谢大家的建议。

【问题讨论】:

标签: f# task-parallel-library gpgpu c++-amp


【解决方案1】:

在 GPU 和 CPU 之间来回传输数据需要时间。您很可能在这里测量您的 PCI Express 总线带宽。对 1M 的浮点数求平方对于 GPU 来说是小菜一碟。

使用 Stopwach 类来衡量 AMP 的性能也不是一个好主意,因为 GPU 调用可能异步发生。在您的情况下没关系,但如果您只测量计算部分(parallel_for_each),这将不起作用。我认为您可以为此使用 D3D11 性能计数器。

【讨论】:

  • 谢谢斯金格。我将在 C++ 代码中添加更多计时器,以查看确切花费的时间(感谢有关性能计数器的提示)。所有人的问题:如果是带宽问题,是否有某种方法可以优化与 GPU 之间的数据传输?也就是说,如果游戏真的因此而受到性能影响,他们就不会费心使用我的 GPU,我假设他们正在做一些我没有做的事情。
  • @FSharpN00b 这是一个相当大的问题。一种方法是逐步将数据移动到 gpu,以便 gpu 在等待其余数据的同时做一些工作
  • @mydogisbox 是的,这是通过与计算重叠来隐藏数据传输的有效策略。
  • 谢谢 mydogisbox 和 Ade。听起来好像我应该坚持 TPL,只担心在我用尽 CPU(这不太可能)之后将数据异步移动到 GPU,而不是先尝试去 GPU。感谢大家的回复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-05
  • 2011-11-03
  • 2020-07-18
  • 2012-05-05
相关资源
最近更新 更多