Производительность OpenCL на CPU

Добрый день!
Господа, интересует такой вопрос - какова производительность OpenCL-приложений написанных для центрального процессора в сравнении с сишными аналогами? Есть ли цифры подобных сравнений?

Forums: 

Цифр нет. Есть ощущения: 1)

Цифр нет. Есть ощущения:

1) OpenCL-приложения работают на всех ядрах без дополнительных усилий со стороны программиста. Для C/C++ программы придется отдельно стараться (есть OpenMP, конечно)
2) OpenCL-приложения лучше векторизуются (т.к. проблема aliasing не волнует, выравнивание можно обеспечить).
Но зато clEnqueueBuffer (двойная буферизация).

Другими словами "случаи бывают всякие"

А где там C? Точнее сравнение

А где там C? Точнее сравнение с C - я подозреваю там возможно тоже самое (ну естественно без классов).

То, что сам код C-style, я не спорю, но зачем так писать? Как сказал Бьярн - пишите C-Style - получите C-Style проблемы.
Есть же итераторы, векторы, смарт-поинтеры. Которые не менее эффективны чем C-style.

C там в предыдущем сообщении

C там в предыдущем сообщении (по ссылке). У C++ проблема с алиасингом на this

А итераторы-векторы-смарт-поинтеры - отличная штука, пока мы не хотим сделать банального:
d[i] = a[i]*b[i]+c[i]

а что не так с векторами

а что не так с векторами на:
d[i] = a[i]*b[i]+c[i]
?

Во-вторых, в C++ есть механизмы абстрацкии от вот этого банального (что на самом деле наверняка не так банально как кажется, потому что вырвано из контекста) на намного более высокий уровень, с сохранением производительности, а в некоторых местах с увеличением.
Вы посмотрите http://stlab.adobe.com/gil/presentation/index.htm - там он как раз на примере вот такого банального, показывает как и куда можно абстрагироваться.
И главное - ассемблерный код отличается буквально парой инструкций от ручной, православной оптимизации.

С ними то, что на AMD оно

С ними то, что на AMD оно должно транслироваться в одну инструкцию (+цикл). На Intel AVX (трехадресные инструкции) - ну в две. И инструкция должна офигачивать 4 double за раз.
Ну, понятно, для векторов с размером не кратным 4-м должен еще как-то хвост досчитываться.

C удовольствием увидел бы такой код, произведенный C++-компилятором. Фортрановские - как я понимаю, могут.

С gil же - я быстро пролистал презентацию (слушать - никакого терпения нет) до места, где гордятся, что оно не хуже чем old plain C (по производительности). Я бы old plain C постеснялся бы гордиться.

С ними то, что на AMD оно

С ними то, что на AMD оно должно транслироваться в одну инструкцию (+цикл). На Intel AVX (трехадресные инструкции) - ну в две. И инструкция должна офигачивать 4 double за раз.

1. Я не совсем понял, в случае когда это обычные массивы/указатели оно транслируется, а в случае с std::vector нет?
2. Напишите свой контейнер/transform, который будет внутрях использовать нужные intrinsic, причём всё это дело можно ещё и в обобщённом виде записать + специализации для конкретных traits. Снаружи всё будет выглядеть очень цивильно, внутри всё будет производительно.

По поводу кода - там справа есть оглавление (outline) (таб с оглавлением по умолчанию открыт) - пролистайте до "Assembly code of the inner loop" - примерно посередине.

В Fortran оно просто будет

В Fortran оно просто будет транслироваться в векторное.
В C - если restrict написать. Без этого - компилятор будет подозревать aliasing и не векторизует.
В C++ для операторов написать restrict просто негде, насколько я понимаю (или я чего-то не понимаю).

да блин, начитался,

да блин, начитался, проникся.
везде какие-то компиляторо-зависимые опции (ну почти везде) - хорошо хоть это есть.
http://msdn.microsoft.com/en-us/library/chh3fb0k%28v=vs.71%29.aspx
и т.п.

В стандарте есть правда valarray, и компиляторы имеют право его векторизировать - но пользуются ли этой возможностью msvc, gcc, icc - нужно руками проверять.
А так как я и говорил, писать
d[i] = a[i]*b[i]+c[i]
это не C++ way, и не только из-за алиасинга.
Я за то, что векторные операции - нужно делать не по-поэлементно. тогда появляется простор для ручной оптимизации(обощённой) и т.п.
А valarray надо посмотреть, да.

Way, вероятно, не C++, а

Way, вероятно, не C++, а операция FMA настолько жизненная, что на нее даже процессорную инструкцию заводят (если могут). FMA или MAD, несущественно (существенно, но не в данном контексте).

А C++ way в этом месте получается какой-то совсем сомнительный. Ну то есть бинарные операции (вектор-вектор, вектор-скаляр) еще можно выписать руками. А тернарные, что, тоже руками выписывать? и a[i]*b[i]+c[i[ и все варианты, когда вместо a[i]/b[i]/c[i] константа (или две)? Безобразно много получается.

P.S. А опция про алиасинг была в 2003-м MSVC, а в более поздних нету (про 2005-й не в курсе, в 8-м и 10-м нету). Вместо нее есть restrict, но толку с него не очень много для C++.

и a[i]*b[i]+c[i[ и все

и a[i]*b[i]+c[i[ и все варианты, когда вместо a[i]/b[i]/c[i] константа (или две)? Безобразно много получается.

Насколько я вижу, С++ way это:
1. std::transfrom + кастомный итератор-обвёртка, который будет итерировать сразу три объекта. а дальше функтор. Это всё можно ещё подсластить шаблонной магией, и получится итератор который может итерировать произвольное колличество(compile-time) векторов одновременно. + boost::lambda (ох чувствую мне C++11 ещё не скоро светит). И я подозреваю, что такой итератор уже кем-то реализован, нужно только его имя знать.
2. a*b+c - сделать это как операцию между векторами, а не поэлементно, но действия делать не сразу, а конструировать объект специального типа, а при присвоении делать непосредственно вычисления. По-типу того, как реализованы lambda в boost.
Можно даже отложить вычисление не до присваивания, а гораздо позже - до первой необходимости знать результат.

я хоть уже давно не спал, но оба варианта мне кажутся жизнеспособными.

Вместо нее есть restrict, но толку с него не очень много для C++.
Почему?

из преимуществ описанных

из преимуществ описанных способов перед обычным a[i]*b[i]+c[i]
можно выделить свободу во внутренней реализации transform (кастомного), либо отложенных операций.
для разных i - операции не зависимы, можно делать параллельно, на sse, да где угодно. можно сделать хоть шаблонный unroll на 1024 элемента.

Ну подождите, итератор видит

Ну подождите, итератор видит только один элемент (каждого/всех) векторов за раз.

Параллелизацию, векторизацию и прочая - придется как-то снаружи навешивать.

в обычном transform

в обычном transform http://www.cplusplus.com/reference/algorithm/transform/ :
while (first1 != last1)
*result++ = op(*first1++);

http://www.cplusplus.com/reference/std/iterator/
"all categories - Can be copied and copy-constructed"
копировать можно любые итераторы.
в нашем transform мы можем делать что угодно, хоть нити запустить:

1. сделать копии итераторов, и дать им равные порции работы (для разных категорий итераторов это будет разных код(специализации по traits) - для Random Access там тривиально, для Forward надо итерировать до конца чтобы знать сколько)
2. запускаем нити - у каждого свой фронт работы.
3. join нитей.

и самое главное, если

и самое главное, если выйдет/вдруг захочется использовать какую-то новую/другую технологию распараллеливания и т.п., достаточно переписать/специализировать transform, и весь код его использующий ничего не заметит.
А в случае, когда у нас по проекту раскиданы низкоуровневые циклы по [i], это всё прийдётся править руками.

Низкоуровневые циклы с SIMD

Низкоуровневые циклы с SIMD (и параллелизацией) налепит компилятор с фортрана. Сам. Использующий код тоже ничего не заметит.

ну как я понимаю тут главный

ну как я понимаю тут главный вопрос в unroll'е?

А параллельные нити он тоже запустит?

Или может передаст на GPU при большом размере умножаемых матриц?

Ага, и на GPU передаст.Я

Ага, и на GPU передаст.

Я совершенно не шучу - PGI сделали именно это. Оно не безумно эффективно, как я понимаю, но работает.

Параллельные нити даже Интел может запустить с /Qparallel. Даже на C/C++ может, но только в очень простых случаях, реально на практике они встречаются редко

Ага, и на GPU передаст. Я

Ага, и на GPU передаст.
Я совершенно не шучу - PGI сделали именно это. Оно не безумно эффективно, как я понимаю, но работает.

это хорошо(я вообще не против Fortran'а), но больше напоминает matlab с gpu плагинами (или оно уже встроено?).

видел /Qparallel у intel'а, но пока особо не верю в него, так как не пробовал/не видел результатов.

Ну я о том и толкую -

Ну я о том и толкую - Qparallel работает только в "простых случаях" (потому что алиасинг, выравнивание и все такое).

И C++ оной простоте не способствует.

я согласен, тут это можно

я согласен, тут это можно рассмотреть как kiss, ну с натяжкой:
в C++ есть разные low-level фишки, которые можно комбинировать друг с другом.
Но, часто комбинирование простых операций, намного менее эффективно чем одна жирная.

Вот например, есть покомпонентное умножение векторов(по-моему Hadamard или как-то так) и есть Reduce одного вектора. Скомбинировав их получим dot product.
Но, например для GPU это будет не эффективно(когда это два разных ядра). Потому что после Hadamard'а, можно воспользоваться данными загруженными из глобальной памяти в блок, и сразу на месте сделать Reduce(по блоку), то есть ядро Hadamard+Reduce. Ещё пару раз запустить Reduce(чистый) прийдётся, но это будет гораздо быстрее чем первоначальный вариант.

Не буду отвечать

Не буду отвечать содержательно, пока не переделаю на этом сайте дизайн, в колонку уж совсем смешно.

Отложим на несколько недель.

И каким образом тут возникнет

И каким образом тут возникнет SIMD?

Тут же не копии итераторов нужны, а специальный итератор, итерирующий по 4.

пример с нитями это один

пример с нитями это один из.
Насколько я понимаю, SIMD может возникнуть сам собой, если сделать unroll (для векторов нектарных uroll'у можно добавить dummy элементы, ничего страшного. можно даже сделать обвёртку для итератора, которая эмулирует dummy элементы).
насколько я понимаю, ещё нужно скопировать данные в локальные переменные, для более благоприятного SIMD.
И это даже не обязательно делать руками - рекурсивный шаблон, с force_inline, сделает unroll для любого заданного compile time целого числа.

Ну вот я про то и толкую -

Ну вот я про то и толкую - чтобы просто получить банальный FMA (который фортрановский компилятор сделает сам, препятствий к этому нет) - в C++ нужно нафигачить просто гору СЛОЖНОГО кода.

А чтобы возник эффективный SIMD - должно быть еще и выравнивание. Что в случае итераторов (могущих, в теории, начать с любого места) - не гарантируется.

ну её же можно получить и

ну её же можно получить и обычным циклом по
a[i]*b[i]+c[i] ?
А сложный(общий) код затем, чтобы этот параллельный transform использовать для чего угодно, а не только для регулярных векторов и матриц.

И я подозреваю, что такой

И я подозреваю, что такой итератор уже кем-то реализован, нужно только его имя знать.

так оно и есть:
http://www.boost.org/doc/libs/1_46_0/libs/iterator/doc/zip_iterator.html
"The zip iterator provides the ability to parallel-iterate over several controlled sequences simultaneously. A zip iterator is constructed from a tuple of iterators. Moving the zip iterator moves all the iterators in parallel. Dereferencing the zip iterator returns a tuple that contains the results of dereferencing the individual iterators."

http://stackoverflow.com/questions/7286755/how-can-i-iterate-over-two-ve...

  1. BOOST_FOREACH(boost::tuple<int,int> &p, zip_range(v1, v2)) {
  2.     doSomething(p.get<0>(), p.get<1>());
  3. }

всё готово уже готово

А код то какой получается?

А код то какой получается? Вот мы хотим два vector<float> перемножить поэлементно и третий поэлементно же прибавить.
И хотим это делать SSE, ибо реально быстрее.

Пожелание писать результат в память мимо кэша (чтобы его не размывать) - ну давайте не будем выдвигать.

Я попробую, только

Я попробую, только подскажите, какой компилятор преоразует
result=a*b+c
в mad?
а то у меня и VS2010 SP1(последние апдейты), и Intel (довольно старый - где-то 2010 года) выдают mulss, addss

Ну так нету MAD-а пока на

Ну так нету MAD-а пока на процессорах (кроме оптеронов). Т.е. если кто и умеет, то Open64 (маловероятно) ну и PGI.
Поэтому mulps/addps (4 элемента за раз) меня полностью удовлетворят.
Интел так умеет, если сильно повезет (и с простыми типами).

mulps/addps я смотрю

mulps/addps
я смотрю автовекторизация в msvc появится только в 11 версии. Я в шоке, sse2 появилось в 2001 году.
Вот смотрю на код - вот прям почти подряд идут инструкции..
У меня почему-то в памяти отложилось, что MSVC векторизовывал, я наверное с одномерным sse перепутал.

но это не беда, это просто усложняет задачу - надо сделать lamda'ы которые могут быть провёрнуты как simd, которые сами будут нужные intrinsics вызывать - та ещё шаблонная магия, но топик-то интересный..

http://software.intel.com/en-us/articles/a-guide-to-auto-vectorization-w...
кстати, вот тут в pdf'ке есть:
#pragma vector nontemporal
"gives a hint to the compiler that data will not be reused, and therefore to use streaming stores that bypass cache"
может пригодится

Пытался Intel C++ cкормить некоторые из этих прагм - не работают, нужен новый..
В общем, как я понял мне нужен и новый Intel, и gcc. А у меня CentOS 5, надо что-то мэйнстримовое поставить.

надо сделать lamda'ы которые

надо сделать lamda'ы которые могут быть провёрнуты как simd, которые сами будут нужные intrinsics вызывать - та ещё шаблонная магия, но топик-то интересный..

Troll mode: А фортран векторизует уже лет 20 минимум. Если не 30. И кучу кода для этого писать не надо.

P.S. Не получается удержаться от писания в столбик.

А он как векторизует?

А он как векторизует? Встроенные типы массивы?
Или именно циклы векторизует?

про столбики: я никуда не спешу - подожду.

и кстати: Troll mode: если он

и кстати:
Troll mode: если он уже 20 лет векторизует, зачем эти sse копания у вас в блоге? можно же статически сликоваться с фортраном, а он вроде как на всех платформах есть

А не спасет. Одно дело

А не спасет. Одно дело параметризованный массив, а другое - какая-то левая аллокация снаружи, невыровненая и неизвестно сколько там чего.

А вот OpenCL (который векторизуется) или ispc - да, оно самое и есть.

То есть со внешней аллокацией

То есть с внешней аллокацией 100% качественно его подружить нельзя?
А нельзя ли просить аллоцировать fortran, а заполнять уже снаружи? Или это неприемлемо для данных которые приходят из вне?

Мы ходим по

Мы ходим по кругу.

Автовекторизации (и распараллеливанию) нужны три вещи
- знание размера данных
- выравнивание
- отсутствие алиасинга.
С внешней аллокацией проблемны - все три. И если размер мы еще передадим, то заложиться на выравнивание-отсутствие алиасинга компилятор совсем не может.

Плюс к этому, 90-й фортран я знаю еще хуже C++, меня учили когда 77-й был новинкой.

Поэтому если уж внешнее исполнение, то, в зависимости от размера данных/арифметической интенсивности - OpenCL и/или ispc. Ispc я нежно люблю, хотя его тоже постепенно перетяжеляют.

кстати, gcc при

кстати, gcc при -ftree-vectorizer-verbose=7 говорит что он вставляет рантайм проверки на алиасинг. по ассемблеру видно что в цикле проверок вообще нет. То есть по-идеи, там где критично, умный компилятор должен уметь делать проверки вне цикла. И я не против дуплицирования бинарного кода для нескольких случаев.

Troll mode: А фортран

Troll mode: А фортран векторизует уже лет 20 минимум. Если не 30. И кучу кода для этого писать не надо.

на C++ вся куча кода может быть спрятана в библиотеку.

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.41.7266&rep=rep...

на 4 странице - чтобы Fortran 77 обогнал Blitz++, пришлось написать почти в пять раз больше кода и несколько дней ручного тюнинга.
Fortran 90 с его векторами требует более чем в два раза больше кода и более чем на 20% медленней.

статья конечно старая, но всё равно показательная.

Статья интересная, но

Статья интересная, но совершенно нерелевантная нашей дискуссии. RS/6000 43P - это RISC-процессор, а вовсе не векторный. Векторные же типы в F90 сделаны для векторных юнитов (и, соответственно, других компьютеров).

тем не менее не самый свежий

тем не менее не самый свежий gcc4.4 векторизует без особого напряга.
как и ожидалось прирост от векторизации небольшой (что-то около 10%) из-за memory bound.
(это mad 3х вектров и запись в 4х).
чуть позже код выложу

Pages