Comments 27
Тестировать я буду так: с помощью compute для всех устройств вызову функцию compute::sort() для того чтоб отсортировать массив из 100 млн. значений типа float.


537 мс ушло? И быстрее всего в 10 раз по сути, чем CPU? Что-то слабо как-то. А если в чистой CUDA написать сортировку?
Скорее всего будет быстрее, а может это только с сортировкой так. Я на CUDA ничего ещё не писал, думаю пришло время что то попробовать. Наверное стоит добавить тесты разных функций.
Попробуйте. Там нет ничего сложного. Надо лишь поставить компилятор от nVidia с их сайта. А вот оптимальное разделение на блоки (там матрица блоков 65536x65536), потоки (моя GTX 1060 может создать 1024 штуки для блока) и варпы (32 потока) — вот тут придётся подумать об оптимальном распределении — это может сильно повлиять на быстродействие.
Вообще, неплохая книжка есть. «Технология CUDA в примерах» Сандерс и Кэндрот.
Главная проблема этого бенчмарка в том, что вы не учитываете время компляции кернела.
Под капотом compute::sort компилирует кернел(для первого запуска), а для последущих запусков ищет скомпилированный кернел в кэше, что также очень медленно.
Не беспокойтесь, я тестировал код со второго раза. Спасибо, что подметили.
Так в этом как раз и главная проблема использования алгоритмов boost::compute, функций и лямбд. Поиск кернела в кеше действительно тяжелая операция. Описание проблемы и бенчмарки здесь: github.com/boostorg/compute/issues/768 За два с половиной года ничего не изменилось.

Если действительно нужна производительность, то нужно использовать чистый opencl/ дефолтный opencl wrapper или только ограниченое подмножество boost::compute, но и тут есть проблемы.

Возьмем пример из начала статьи с асинхнронными операциями. К сожалению полной асинхронности тут нет, так как сначала выполнняется операция заполнения вектора, и только после завершения первой задачи выполняется копирование второго вектора. Эти две задачи не связаны между собой и могут быть выполнены параллельно. Для этого в opencl реализованы «out of order execution» очереди. Но в релизе boost compute эта функциональность нормально не поддерживается(хотя и исправлено в develop бранче на гитхабе пару лет назад).

Для прода boost compute бесполезен, так как медленный и не развивается.

P.S. Еще одна возможная причина такой маленькой разницы времени выполнения бенчмарка между cpu и gpu может быть в размере векторов. Из описания не ясно были ли вектора кратны 64(32/16 в зависимости от видяхи, можно проверить через clinfo). Это действительно значительно ускоряет время выполнения алгоритма, потому что кэш-фрэндли.
Добавил больше тестов, возможно дело в том что ЦПУ не такой уж и слабый.
Вряд ли. У меня с CUDA свёртки ускоряются в несколько сотен раз.
Достаточно мощный CPU и очень слабый GPU. И то разница есть в пользу GPU.
Но объёмы вычислений всё-равно микроскопические в тестах, ещё и последовательно вычисления идут (идеально в плане кэширования). Хотелось бы что-нибудь типа умножения матриц размером 10К*10К. Там уже «в лоб» будут проблемы с кэшированием и процессор гарантированно начнёт страдать, а вот поведение GPU было бы интересно. Ну и с double проблемы возникнут уже на GPU (от NVidia), а особенно интересно именно вычисление даблами…
Мне интересно. Но я пока только с силами собираюсь, чтобы за изучение OpenCL взяться.
В любом случае, спасибо за статью. Не хватает исходников.
Ну и с double проблемы возникнут уже на GPU (от NVidia), а особенно интересно именно вычисление даблами…


А какие там с double проблемы? Оно давно ведь поддерживается.
NVidia хочет деняк, поэтому на игровых GPU порезали скорость вычислений double. Хочешь считать double — покупай Quadro или как там у них про-карточки называются и внешние девайсы специальные.
В интернете… Я пока изучаю вопрос, личного опыта нет. Пишут, что float до 32 раз быстрее считает, в зависимости от размеров данных. Причём на TESLA (вспомнил название) разница в скорости не столь большая.
Так то очевидно, что double вдвое больше нагружает шину и, если объёмы огромные, то будет заметная разница в скорсоти из-за бОльших издержек. Потому я и пишу, что интересно сравнение именно на больших объёмах данных, а не на игрушечных массивах в 100 тыссяч элементов.
Я пробовал, что будет с double, во всех тестах как CPU так и GPU в параллеле считают его в два раза дольше чем float
Мне вот тоже такое помнится в CUDA. Откуда взялось в 32 раза замедление не знаю.
Может, кто-то неправильно потоки по блокам раскидал и получил тормоза?
Платформы
— и как это использовать этот список платформ?
Экспериментальным путём я выяснил что собранные на моём ноутбуке программы будут запускаться лишь на устройствах таких же производителей.
— тоже странно, на то эту либу и делали чтобы один раз написать а потом запускать на девайсах которые под руку попадутся.
Сортировка — не самый показательный алгоритм для сравнения CPU vs GPU.
А что на счёт других алгоритмов? Один из из них работает с такой же разницей в скорости как и сортировка.
Наверное мне следовало высказаться более развёрнуто: коль скоро GPU ускоряет даже сортировку — многошаговый алгоритм с зависимостью по данным, то это уже обо многом говорит в плане пользы вычислений на GPU.
Что-то я ничего не понял, а остальные поняли.
Я сейчас пишу некий простенький алгоритм, двойной цикл по массиву, внутри какие-то вычисления. Написали на CUDA, сейчас хочу написать просто для процессора, есть девайс с кучей ядер (2xXeon 12 cores). Ну там _beginthread и вперед. Чем мне может помочь эта библиотека?
Спасибо, пишу и сюда и в ЛС.
Сейчас так.
for (i=0;i<N;i++)
{
summa=0;
for (j=0;j<N;j++)
{
if(i==j)continue;
summa+=A[j]*B[i]...//+еще какие-то вычисления
}
C[i]=summa;
}
Текст программы слегка условен — в реальности там другие переменные, другой способ доступа к массиву, но так нагляднее.
Это алгоритм с N^2 операций, N~10^6, каждая операция по сути независима от других — для каждой ячейки массива происходит вычисление для которого надо пробежаться по всему массиву (исключив эту ячейку), это можно делать параллельно, что и делается. На Куде получилось ускорение в 140 раз по сравнению с однопоточным режимом на CPU. Сейчас хочу сделать версию для CPU.
Что то такое у меня получилось, цикл всё равно приходится использовать, но в теории должно работать быстрее.
auto device = compute::system::default_device(); //тут поменяешь на тот девайс 
//который у тебя процессор
auto context = compute::context(device);
auto queue = compute::command_queue(context, device);
	
vector<float> host_vec = { 1, 2, 3, 4, 5};

compute::vector<float> A(host_vec.begin(), host_vec.end(), queue);
compute::vector<float> B(host_vec.begin(), host_vec.end(), queue);

compute::vector<float> C(host_vec.size(), context);
float temp_res = 0;
float res = 0;

auto B_iter = B.begin();
for (size_t i = 0; i < host_vec.size(); i++)
{
	compute::transform(A.begin(), A.end(), C.begin(), 
        compute::_1 * B_iter.read(queue) /*+ещё какие-то вычисления*/, queue);
	compute::reduce(C.begin(), C.end(), &temp_res, queue);
	res += temp_res;
	B_iter++;
}
cout << res << endl;


UPD: Для большей эффективности можешь вызвать все что в цикле асинхронно.
Если надо распараллелить цикл, то есть pragma omp, чтобы вручную цикл не делить по потокам.
Если кому то что то не понятно, не стесняйтесь писать в лс или в комментарии.
Only those users with full accounts are able to leave comments. Log in, please.