Pull to refresh

Comments 42

Я правильно понял что в итоге самые быстрые результаты показал C и либа которая стартует из питона написанная на C/C++? В итоге вывод такой — в частных случаях использование готового специализированного ПО занимает меньше времени а показывает те же результаты.
Я не совсем понял Ваш вопрос. И Numba и PyCUDA — это библиотеки для питона. Синтаксис их использования отличается. В Numbe ядро (функция выполняемая на GPU) вы пишете по сути на питоне. В PyCUDA вы пишете ядро на C и вставляете в свой код на питоне. В итоге самым быстрым оказался C и PyCUDA. Про какое «готовое специализированое ПО» Вы спрашиваете я не очень понял, весь код тут самописный и используются библиотеки для jit компиляции.
Как я понял PyCUDA облегчает жизнь и позволяет писать меньше кода, хотя по сути пишешь(и в результате всё работает) на том же C, её я и назвал спец решением, а реализацию на чистом C велосипедом. Вывод что я написал как бы обобщающий — что вообще в любых задачах не редко лучше использовать готовое решение(или решение заточенное под конкретную задачу) а не писать велосипеды и вывод данной статьи говорит о том же.
Я не совсем понял Ваш вопрос. И Numba и PyCUDA — это библиотеки для питона. Синтаксис их использования отличается. В Numbe ядро (функция выполняемая на GPU) вы пишете по сути на питоне. В PyCUDA вы пишете ядро на C и вставляете в свой код на питоне. В итоге самым быстрым оказался C и PyCUDA. Про какое «готовое специализированое ПО» Вы спрашиваете я не очень понял, весь код тут самописный и используются библиотеки для jit компиляции.
Так на каком способе вы в итоге остановились для решения ваших задач? Numba с одномерным массивом вроде выглядит вполне разумным компромиссом.
Мое чисто субъективное мнение состоит в том, что для расчетов занимающих время в пределах 1-10-30 минут на один кейс Numba с одномерными массивами мне подойдет т.к. время на разработку, а также анализ результатов и т.п. нивелирует несколько большее время расчетов. Для расчетов занимающих часы я бы уже использовал PyCUDA или C. Поэтому для ближайшей задачи — PyCUDA.
Хотел замечание по самому графику сделать. Неудачная раскладка цветов. Неразличимы напрочь. Воспринимаются как красный и какие-то сине-серо-фиолетовые. Seaborn не смотрели в качестве библиотеки для визуализации?
Я думал что дальтоником стал…
Да, согласен, учту. На Seaborn посмотрю.
При этом, однако, очень важно не терять в скорости выполнения программ, поскольку некоторые расчеты могут занимать от нескольких часов до суток.

Очевидно, в данном случае или С, или РуCUDA. Сравнить только по трудозатратам.
После исследования производительности, представленного в статье именно такой вывод я для себя и сделал. Однако изначально результат для меня был не очевиден. Во-первых, я не был уверен что на питоне мне удастся настолько приблизится к C. Во-вторых, была надежда, что если это возможно, то и Numba позволит получить приемлемое время по отношению к C
По трудозатратам на мой взгляд питон менее трудозаиратен, но вообще все конечно будет зависеть от конкретных задач. Мне полученные результаты дали ориентир.
1. Библиотеки являются частью Python
2. Из сотен возможных способов реализовать данный расчет, выбираем самую быструю библотеку
3. Получаем идентичные результаты по скорости расчета, в сравнении с С

В чем же тогда:
«известная всем медлительность питона» ?

На Python есть несколько потенциальных способов реализации расчета -> выбираем самый быстрый
На C есть несколько потенциальных способов реализации расчета -> выбираем самый быстрый

Скорость зависит от метода, а не от языка. Как вы сами только что доказали.

Медлительность в том, что самым быстрым вариантом оказывается не нативная реализация, а ffi в C.

Программа на Си преобразуется в машинный код, а программа на питоне интерпретируется. Поэтому сравнивать надо Numba с РуCUDA. Из тестов видно что Numba медленнее в 3-5 раз.
То есть скорость зависит от языка значительно.

И 3-5 раз это ещё очень хороший результат. Например на The Computer Language Benchmarks Game ( http://benchmarksgame.alioth.debian.org/ ) в тесте mandelbrot ( http://benchmarksgame.alioth.debian.org/u64q/performance.php?test=mandelbrot ) для однопоточной программы C++ g++ #5 отставание от лучшего результата оказалось 7,8 раза, а для Python 3 #7 отставание в 94 раза, при полной загрузке четырёх ядер. То есть разница, по сравнению с однопоточной программой 12 кратная.

Чертовски хотел бы увидеть данное сравнение на современных GPU, а то на GTX 580 стоит чип GF110, в котором поддерживается CUDA версии 2.0 в то время как текущая 6.1.
Да тут, судя по коду ядра, вообще без разницы, каким образом получен байт-код для GPU. Более того, код на питоне выглядит абсолютно также, как код на C, ничуть не проще и не короче. По-моему на самом деле здесь вообще измерялась скорость запуска в цикле одного и того же ядра на GPU из разных окружений.
На 580 fp64-блоки официально отключены (производительность в 8 раз ниже fp32)
А на GTX 950 FP64 = 1/32 FP32 это как назвать?
FP64=1572 GFLOPS
FP32=49 GFLOPS

Вот табличка, сам инфу по сети собирал
Скорее, не CUDA 2.0, а Compute Capability 2.0. Немного разные вещи
Немножечко критики:
Расчеты показывают, что для N = 512 время выполнения C-программы распараллеленной на GPU составляет 0.27 секунд против 33.06 секунд для последовательной реализации алгоритма на CPU. То есть ускорение CPU/GPU составляет около 120 раз. С ростом N величина ускорения не убывает.

Немного некорректное сравнение. Следовало бы сравнивать OpenMP (или что-то еще) CPU с GPU. Иначе вы от процессора используете в лучшем случае только четверть, тогда как от GPU — гораздо больше. Иначе создается впечатление, что данная GPU — в 120 раз быстрее процессора, что далеко не так.

+ всегда очень хочется видеть графики в log-log формате. Это стандартный вариант представление времени исполения от величины неизвестных.

Насколько я понимаю в GPGPU, важно чтобы размер данных вмещался в память видеокарты. Таким образом, имеем дисбаланс в 1.5 Gb по видеопамяти и 8 Gb оперативной. Что произойдет, когда памяти в системе еще будет достаточно, а вот видеопамяти уже хавтать не будет? (точно — для N=16384). Произойдет резкий спад производительности GPU — или программа просто вылетит с ошибкой malloc при попытке cudaMemcpy()?

Кроме того, было бы тоже интересно посмотреть, как себя ведет cuda и различные варианты написанной программы на сетках меньших размеров (именно там эффективность параллелизации падает значительно): 256, 128, 64… Ибо чем больше данных — пока они влезают в память единого времени доступа — параллелизация будет только выигрывать.

Уточнение: вылетит на cudaMalloc. По поводу OpenMP/Cilk/etc, какой смысл? Насколько я понимаю, процитированный вами абзац — лишь лирическое отступление. На самом деле автор хотел понять, имеет ли смысл использовать те или иные биндинги к CUDA из Python.
Спасибо за уточнение :)
Пожалуй, вы правы — и это есть лирическое отступление в чистом виде. Я лишь зацепился, ибо в том числе регулярно занимаюсь бенчмаркингом в численных задачах, как на CPU, GPU, мат. сопроцессорах, включая гетерогенные и многоузловые конфигурации. Часто встречаю заявления, что GPU лучше чем CPU — и насколько (без указания стоимости, ваттов или чего бы то еще). Если читать эту статью по диагонали, кроме обзора биндингов CUDA к Python, можно сделать вывод, что был достигнут прирост в 120 раз (при этом справедливо написано, что по отношению к непараллельному CPU) и GPU — лучше в сотню раз.
Должен согласится с Вашими утверждениями, однако, меня интересовало сравнение времени выполнения для задач и сеток характерных для практических задач, которые мне уже доводилось или предстоит решать. В этих задачах я не использую ни очень большие (N>=16384) ни очень маленькие (N<256) сетки. Что касается параллельной реализации на CPU, я проводил тесты с использованием MPI и получал прирост скорости (CPU 4 ядра)/GPU порядка 35-40 раз на интересующих меня сетках. По поводу энергопотребления, это конечно тоже важно, но за свет платим не мы (хотя я понимаю значение этого фактора для экологии). Вообще детальное сравнение CPU vs. GPU тема для отдельной статьи и таких статей можно найти достаточно много, в этой статье я лишь упомянул один результат не претендуя на сколько-нибудь детальное сравнение
Надо сказать, результат для такого примера вполне ожидаемый. У меня один вопрос, почему в PyCUDA параметры int* dN, double* dth2
передаются через аргументы в виде указателей в общую память, а не пишутся в константную память как в примере на C? Этого нельзя было сделать?
К сожалению я не нашел способа как в PyCUDA передать переменные через константную память и поэтому использовал такой способ. Буду очень благодарен если кто-нибудь покажет как это сделать.
Я не знаток PyCUDA, но ответ на этот вопрос гуглится на раз.
1) В коде ядра
__device__ __constant__ int dN;
__device__ __constant__ double dth2;

2) В питоне
from pycuda.compiler import SourceModule
module = SourceModule(kernelCode)

dN = module.get_global('dN')[0]
cuda.memcpy_htod(dN,  valueOnHost)

В любом случае такой подход с внедрением сишного исходника в Python-скрипт выглядит как-то жутковато :)
Должен добавить, что т.к. константная память более быстрая, то после изменения кода в соответствии с Вашим комментарием отношение PyCuda/CudaC стало не более 2-2.5%
Я вообще ожидал разницу в пределах погрешности измерений :) Я уже писал об этом где-то выше. Вы запускаете идентичный код ядра из разных окружений.
Согласен, но на мой взгляд как минимум выполнение цикла for питоном может снижать скорость
По сравнению с запуском ядра на GPU и последующей синхронизацией, эти издержки пренебрежимо малы.
И еще. По коду я не хотел критиковать. Но все же скажу, что учет условий Дирихле следовало сделать без if-else. Скорее всего, это несколько ускорило бы программу.
Я это к чему. Если не трудно, попробуйте, пожалуйста в сишном коде поменять выражения if-else на тернарный оператор. Чисто теоретически можете получить выигрыш (зависит от того, насколько умным стал nvcc)
Спасибо, я проверил, выигрыша не получил, результаты в пределах погрешности причем в медленную сторону.
Интересно. На днях доберусь до машины с GeForce, тоже попробую.
Я сразу извиняюсь, что не совсем по теме, но может от явной разностной схемы перейти к неявной? Конечно, при одинаковом N для ЯРС приходится делать в два раза меньше вычислений, чем для НРС, но зато можно ограничиться меньшим N.
Преимущество неявной схемы заключается не в меньшем значении N, потому что N пространственное разрешение сетки и соответственно задает точность (грубо говоря) решения, а в возможности снять ограничение на шаг по времени. Поэтому, что бы просчитать одинаковые времена НРС требует сделать меньше временных шагов, однако при этом число операций на шаг больше. Выбор ЯРС для тестовой задачи был связан с тем, какую реальную задачу мы собирались решать, т.к. мы хотели смоделировать те условия которые будут там. Так вот, в этих задачах есть сильно нелинейные члены и как показали предыдущие сравнения сильно увеличить шаг по времени с использованием НРС не удается и большее количество операций, требующихся на обращения матриц, не окупается.
Вы правы в том, что для других задач НРС — хороший выход и конечно мы будем сравнивать производительность операций с разреженными матрицами в дальнейшем и по мере необходимости
Спасибо за хороший ответ и интересную статью!))
Я заранее напишу что спорить не хочу.
И вообщем-то всё неплохо но не знаю почему ни одного слова про OpenCL не было, даже в сравнении. Это ведь открытый стандарт и как показывает практика более эффективный для математических вычислений.
OpenCL гарантирует conformance (переносимость кода), но не performance (производительность). По-настоящему быстрые решения всегда привязаны к аппаратному вендору.
а для OpenCL подобные библиотеки для Python есть?
Only those users with full accounts are able to leave comments. Log in, please.