Поэлементные операции в OpenCL (Cuda)

#cuda #opencl #gpu

#cuda #opencl #графический процессор

Вопрос:

Я создаю ядро для поэлементного умножения двух матриц, но, по крайней мере, с моими конфигурациями, мое ядро OpenCL работает быстрее, только когда размер каждой матрицы превышает 2 ГБ. Итак, мне было интересно, связано ли это с моим наивным ядром (см. Ниже) или из-за природы поэлементных операций, означающих, что поэлементные операции не выигрывают от использования графических процессоров.

Спасибо за ваш вклад!

ядро:

 KERNEL_CODE = """
// elementwise multiplication: C = A .* B.
__kernel void matrixMul(
        __global float* C,
        __global float* A,
        __global float* B,
        int width, int height)
{
    // ID
    int x = get_global_id(0);
    int y = get_global_id(1);

    // Multiplying
    C[y * height   x ] = A[y * height   x] * B[y * height   x];
}
"""
  

p.s. Я читал, что некоторые эксперты считают, что CUDA слишком отличается от OpenCL, чтобы отвечать на оба вопроса в одном вопросе, поэтому я мог удалить его из заголовка и тегов.

Комментарии:

1. Проверьте, сколько из этого времени теряется при простом запуске ядра. И с чем вы это сравниваете? Та же операция на процессоре?

Ответ №1:

Такого рода операции имеют N провалов, но 3N транзакций с памятью, поэтому они будут полностью ограничены пропускной способностью памяти. Нет возможности для повторного использования данных, поэтому верхняя граница ускорения по сравнению с эталонной версией CPU — это отношение пропускной способности GPU к CPU. Это число редко превышает 10 раз и может довольно быстро уменьшиться из-за затрат на перемещение данных в память графического процессора и из нее. Вообще говоря, такого рода операции лучше всего «объединять» с другими O (N) операциями для повышения производительности. Обычно вы бы никогда не стали просто вычислять произведение Адамара в одном ядре, вы бы сделали это как часть серии из O (N) операций в одном ядре. Итак, нет, это не лучший кандидат для ускорения, даже если бы ядро было оптимальным.

И ваше ядро определенно таковым не является. Вы выполняете 3 операции ввода-вывода за каждый ФЛОП, что является огромным штрафом. Вы определенно могли бы что-то сделать, чтобы улучшить это, но что именно, будет полностью зависеть от того, на каком оборудовании это будет выполняться.

Ответ №2:

Говоря о поэлементных операциях: это зависит от устройства. Например, графические процессоры NVidia используют скалярные процессоры (со скалярными инструкциями), векторизация не требуется. Напротив, ATI использует 5d (или 4d) VLIW-процессоры, и для них решающее значение имеет векторизация. Однако иногда это может выполняться компилятором вместо использования векторных типов данных непосредственно в коде, но это первое, что нужно сделать при оптимизации для графических процессоров ATI.

Тем не менее, как указал talonmies, приведенный выше алгоритм вряд ли ограничен пропускной способностью памяти, и вы не можете ожидать значительного ускорения, используя исключительно графический процессор для этого.

Ответ №3:

Опубликованное вами ядро должно быть по крайней мере таким же быстрым, как ядро процессора. Но вы вообще не используете объединенные обращения к памяти!

Это снижает вашу производительность.

Однако, как заявил @talonmies. Это не лучший вариант для графического процессора. Вы теряете все свое время на копирование в память.