Является ли библиотека C++ AMP полезной из F#?

Я экспериментирую с библиотекой C++ AMP в F# как способ использования графического процессора для параллельной работы. Однако результаты, которые я получаю, не кажутся интуитивными.

В 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();
}

(Код адаптирован из блога Игоря Островского на MSDN.)

Затем я написал следующий 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. Это то, что я ожидаю. Насколько я понимаю, графический процессор (следовательно, AMP) должен лучше подходить для этой ситуации, когда работа разбита на очень маленькие кусочки, чем TPL.

Однако, если я увеличу размер массива до 1000, TPL теперь берет ~30K тиков, а AMP - ~70K. И это только ухудшается оттуда. Для массива размером 1 миллион, AMP занимает почти 1000x столько же, сколько TPL.

Поскольку я ожидаю, что GPU (то есть AMP) будет лучше справляться с такой задачей, мне интересно, чего мне здесь не хватает.

Моя видеокарта - GeForce 550 Ti с 1 ГБ, насколько я знаю, не дурак. Я знаю, что использование PInvoke для вызова кода AMP накладно, но я ожидаю, что это будет фиксированная стоимость, которая амортизируется при больших размерах массива. Я считаю, что массив передается по ссылке (хотя я могу ошибаться), поэтому я не ожидаю каких-либо затрат, связанных с его копированием.

Спасибо всем за ваш совет.

1 ответ

Решение

Передача данных между GPU и CPU занимает много времени. Скорее всего, вы здесь измеряете пропускную способность шины PCI Express. Квадрат 1M поплавков это кусок пирога для GPU.

Также не очень хорошая идея использовать Stopwach класс для измерения производительности для AMP, потому что вызовы графического процессора могут происходить асинхронно. В вашем случае это нормально, но если вы измеряете только вычислительную часть (parallel_for_each) это не сработает. Я думаю, что вы можете использовать счетчики производительности D3D11 для этого.

Другие вопросы по тегам