Бенчмаркинг (python против c ++ с использованием BLAS) и (numpy)

107

Я хотел бы написать программу, которая широко использует функциональные возможности линейной алгебры BLAS и LAPACK. Поскольку производительность - это проблема, я провел несколько тестов и хотел бы знать, законен ли принятый мной подход.

У меня, так сказать, три участника, и я хочу проверить их выступление с помощью простого умножения матрицы на матрицу. Конкурсанты:

  1. Numpy, используя только функциональность dot.
  2. Python, вызывающий функции BLAS через общий объект.
  3. C ++, вызывающий функции BLAS через общий объект.

Сценарий

Я реализовал умножение матрицы на матрицу для разных измерений i. iработает от 5 до 500 с шагом 5 и матрицы m1и m2настроены следующим образом:

m1 = numpy.random.rand(i,i).astype(numpy.float32)
m2 = numpy.random.rand(i,i).astype(numpy.float32)

1. Нумпи

Используемый код выглядит так:

tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1, m2")
rNumpy.append((i, tNumpy.repeat(20, 1)))

2. Python, вызывающий BLAS через общий объект.

С функцией

_blaslib = ctypes.cdll.LoadLibrary("libblas.so")
def Mul(m1, m2, i, r):

    no_trans = c_char("n")
    n = c_int(i)
    one = c_float(1.0)
    zero = c_float(0.0)

    _blaslib.sgemm_(byref(no_trans), byref(no_trans), byref(n), byref(n), byref(n), 
            byref(one), m1.ctypes.data_as(ctypes.c_void_p), byref(n), 
            m2.ctypes.data_as(ctypes.c_void_p), byref(n), byref(zero), 
            r.ctypes.data_as(ctypes.c_void_p), byref(n))

тестовый код выглядит так:

r = numpy.zeros((i,i), numpy.float32)
tBlas = timeit.Timer("Mul(m1, m2, i, r)", "import numpy; from __main__ import i, m1, m2, r, Mul")
rBlas.append((i, tBlas.repeat(20, 1)))

3. c ++, вызов BLAS через общий объект

Теперь код C ++, естественно, немного длиннее, поэтому я сокращаю информацию до минимума.
Я загружаю функцию с помощью

void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");

Я измеряю время gettimeofdayтак:

gettimeofday(&start, NULL);
f(&no_trans, &no_trans, &dim, &dim, &dim, &one, A, &dim, B, &dim, &zero, Return, &dim);
gettimeofday(&end, NULL);
dTimes[j] = CalcTime(start, end);

где jцикл выполняется 20 раз. Я подсчитываю прошедшее время с

double CalcTime(timeval start, timeval end)
{
double factor = 1000000;
return (((double)end.tv_sec) * factor + ((double)end.tv_usec) - (((double)start.tv_sec) * factor + ((double)start.tv_usec))) / factor;
}

Полученные результаты

Результат показан на графике ниже:

введите описание изображения здесь

Вопросы

  1. Как вы думаете, мой подход справедлив, или я могу избежать ненужных накладных расходов?
  2. Ожидаете ли вы, что результат покажет такое огромное расхождение между подходами c ++ и python? Оба используют общие объекты для своих вычислений.
  3. Поскольку я предпочитаю использовать в своей программе python, что я могу сделать для увеличения производительности при вызове подпрограмм BLAS или LAPACK?

Скачать

Полную версию теста можно скачать здесь . (JF Себастьян сделал эту ссылку возможной ^^)

Woltan
источник
в вашем подходе ctypes у вас есть распределение памяти внутри измеряемой функции. Соответствует ли ваш код на C ++ этому подходу? Но по сравнению с матричным умножением это не должно иметь большого значения ....
Rockportrocker
@rocksportrocker Вы правы. Распределение памяти для rматрицы несправедливо. Решаю "вопрос" прямо сейчас и выкладываю новые результаты.
Woltan
1. Убедитесь, что массивы имеют одинаковую структуру памяти np.ascontiguousarray()(рассмотрите порядок C и Fortran). 2. Убедитесь, что np.dot()использует то же самое libblas.so.
jfs
@JFSebastian Оба массива m1и m2имеют ascontiguousarrayфлаг как True. И numpy использует тот же общий объект, что и C. Что касается порядка массива: в настоящее время меня не интересует результат вычисления, поэтому порядок не имеет значения.
Woltan
1
@Woltan: не используйте filefactory, сервис ужасный. Я добавил ваш тест на github: woltan-benchmark . Если вы используете github, я могу добавить вас в качестве соавтора.
jfs

Ответы:

58

Я проверил ваш тест . На моей машине нет разницы между C ++ и numpy:

эталон волтана

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

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

Ожидаете ли вы, что результат покажет такое огромное расхождение между подходами c ++ и python? Оба используют общие объекты для своих вычислений.

Нет.

Поскольку я предпочитаю использовать в своей программе python, что я могу сделать для увеличения производительности при вызове подпрограмм BLAS или LAPACK?

Убедитесь, что numpy использует оптимизированную версию библиотек BLAS / LAPACK в вашей системе.

jfs
источник
4
Так что же не так в оригинальном плакате? Я бы хотел, чтобы он прокомментировал этот пост. Подтверждает ли он, что Numpy работает так же быстро, как C ++?
wmac
Ваш код на C ++ работает медленнее, чем исходные плакаты. Вы компилировали под оптимизацию?
cdcdcd
@cdcdcd это не мой код. Щелкните ссылку и запустите тест самостоятельно с различными параметрами оптимизации (см. Makefile). Хотя код не перекомпилирует ни blas, ни lapack.
jfs
73

ОБНОВЛЕНИЕ (30.07.2014):

Я повторно запустил тест на нашем новом HPC. Как аппаратное обеспечение, так и программный стек изменились по сравнению с настройкой в ​​исходном ответе.

Я помещаю результаты в электронную таблицу Google (содержит также результаты из исходного ответа).

Оборудование

Наш HPC имеет два разных узла: один с процессорами Intel Sandy Bridge, а другой - с новыми процессорами Ivy Bridge:

Сэнди (MKL, OpenBLAS, ATLAS):

  • Процессор : 2 x 16 Intel (R) Xeon (R) E2560 Sandy Bridge @ 2,00 ГГц (16 ядер)
  • Оперативная память : 64 ГБ

Плющ (MKL, OpenBLAS, ATLAS):

  • ЦП : 2 x 20 Intel (R) Xeon (R) E2680 V2 Ivy Bridge @ 2,80 ГГц (20 ядер, HT = 40 ядер)
  • Оперативная память : 256 ГБ

Программное обеспечение

Программный стек предназначен для обоих узлов sam. Вместо того , чтобы GotoBLAS2 , OpenBLAS используется , и есть также многопоточный ATLAS BLAS , который установлен в 8 потоков (зашиты).

  • ОС : Suse
  • Компилятор Intel : ictce-5.3.0
  • Numpy: 1.8.0
  • OpenBLAS: 0.2.6
  • АТЛАС :: 3.8.4

Тест точечного продукта

Код теста такой же, как показано ниже. Однако для новых машин я также провел тест для размеров матрицы 5000 и 8000 .
В таблице ниже представлены результаты тестов из исходного ответа (переименованные: MKL -> Nehalem MKL, Netlib Blas -> Nehalem Netlib BLAS и т. Д.)

Умножение матриц (размеры = [1000,2000,3000,5000,8000])

Однопоточная производительность: однопоточная производительность

Многопоточная производительность (8 потоков): многопоточная (8 потоков) производительность

Потоки и размер матрицы (Ivy Bridge MKL) : Размер матрицы против потоков

Benchmark Suite

набор тестов

Однопоточная производительность: введите описание изображения здесь

Многопоточная (8 потоков) производительность: введите описание изображения здесь

Вывод

Результаты нового теста аналогичны результатам в исходном ответе. OpenBLAS и MKL работают на одном уровне, за исключением теста на собственное значение . В собственных значениях тест выполняет только достаточно хорошо на OpenBLAS в режиме однотридового . В многопоточном режиме производительность хуже.

Таблица "Размер матрицы и количество нитей" также показывает, что, хотя MKL и OpenBLAS обычно хорошо масштабируются с количеством ядер / потоков, это зависит от размера матрицы. Для небольших матриц добавление большего количества ядер не сильно улучшит производительность.

При переходе от Sandy Bridge к Ivy Bridge производительность увеличивается примерно на 30%, что может быть связано либо с более высокой тактовой частотой (+ 0,8 ГГц), либо с лучшей архитектурой.


Исходный ответ (04.10.2011):

Некоторое время назад мне пришлось оптимизировать некоторые вычисления / алгоритмы линейной алгебры, которые были написаны на Python с использованием numpy и BLAS, поэтому я протестировал / протестировал различные конфигурации numpy / BLAS.

В частности, я тестировал:

  • Numpy с ATLAS
  • Numpy с GotoBlas2 (1.13)
  • Numpy с MKL (11.1 / 073)
  • Numpy с Accelerate Framework (Mac OS X)

Я провел два разных теста:

  1. простое скалярное произведение матриц разного размера
  2. Набор тестов, который можно найти здесь .

Вот мои результаты:

Машины

Linux (MKL, ATLAS, No-MKL, GotoBlas2):

  • ОС : Ubuntu Lucid 10.4 64 бит.
  • Процессор : 2 x 4 Intel (R) Xeon (R) E5504 @ 2,00 ГГц (8 ядер)
  • Оперативная память : 24 ГБ
  • Компилятор Intel : 11.1 / 073
  • Scipy : 0,8
  • Numpy : 1.5

Mac Book Pro (Accelerate Framework):

  • ОС : Mac OS X Snow Leopard (10.6)
  • Процессор : 1 Intel Core 2 Duo 2,93 ГГц (2 ядра)
  • Оперативная память : 4 ГБ
  • Scipy : 0,7
  • Numpy : 1.3

Mac Server (Accelerate Framework):

  • ОС : Mac OS X Snow Leopard Server (10.6)
  • Процессор : 4 процессора Intel (R) Xeon (R) E5520 @ 2,26 ГГц (8 ядер)
  • Оперативная память : 4 ГБ
  • Scipy : 0,8
  • Numpy : 1.5.1

Тест точечного продукта

Код :

import numpy as np
a = np.random.random_sample((size,size))
b = np.random.random_sample((size,size))
%timeit np.dot(a,b)

Результаты :

    Система | size = 1000 | size = 2000 | size = 3000 |
netlib BLAS | 1350 мс | 10900 мс | 39200 мс |    
ATLAS (1 CPU) | 314 мс | 2560 мс | 8700 мс |     
MKL (1 CPU) | 268 мс | 2110 мс | 7120 мс |
MKL (2 процессора) | - | - | 3660 мс |
MKL (8 процессоров) | 39 мс | 319 мс | 1000 мс |
GotoBlas2 (1 CPU) | 266 мс | 2100 мс | 7280 мс |
GotoBlas2 (2 процессора) | 139 мс | 1009 мс | 3690 мс |
GotoBlas2 (8 процессоров) | 54 мс | 389 мс | 1250 мс |
Mac OS X (1 ЦП) | 143 мс | 1060 мс | 3605 мс |
Mac-сервер (1 ЦП) | 92 мс | 714 мс | 2130 мс |

Тест точечного продукта - диаграмма

Benchmark Suite

Код :
дополнительную информацию о наборе тестов см. Здесь .

Результаты :

    Система | собственные значения | svd | det | inv | точка |
netlib BLAS | 1688 мс | 13102 мс | 438 мс | 2155 мс | 3522 мс |
ATLAS (1 CPU) | 1210 мс | 5897 мс | 170 мс | 560 мс | 893 мс |
MKL (1 CPU) | 691 мс | 4475 мс | 141 мс | 450 мс | 736 мс |
MKL (2 процессора) | 552 мс | 2718 мс | 96 мс | 267 мс | 423 мс |
MKL (8 процессоров) | 525 мс | 1679 мс | 60 мс | 137 мс | 197 мс |  
GotoBlas2 (1 CPU) | 2124 мс | 4636 мс | 147 мс | 456 мс | 743 мс |
GotoBlas2 (2 процессора) | 1560 мс | 3278 мс | 116 мс | 295 мс | 460 мс |
GotoBlas2 (8 процессоров) | 741 мс | 2914 мс | 82 мс | 262 мс | 192 мс |
Mac OS X (1 ЦП) | 948 мс | 4339 мс | 151 мс | 318 мс | 566 мс |
Mac-сервер (1 ЦП) | 1033 мс | 3645 мс | 99 мс | 232 мс | 342 мс |

Набор тестов - диаграмма

Монтаж

Установка MKL включала установку всего Intel Compiler Suite, что довольно просто. Однако из-за некоторых ошибок / проблем настройка и компиляция numpy с поддержкой MKL была немного хлопотной.

GotoBlas2 - это небольшой пакет, который можно легко скомпилировать как общую библиотеку. Однако из-за ошибки вам необходимо воссоздать общую библиотеку после ее сборки, чтобы использовать ее с numpy.
В дополнение к этому зданию он по какой-то причине не работал для многоцелевой платформы. Поэтому мне пришлось создать файл .so для каждой платформы, для которой я хочу иметь оптимизированный файл libgoto2.so .

Если вы установите numpy из репозитория Ubuntu, он автоматически установит и настроит numpy для использования ATLAS . Установка ATLAS из исходников может занять некоторое время и требует дополнительных действий (fortran и т. Д.).

Если вы установите numpy на компьютер Mac OS X с портами Fink или Mac, он либо настроит numpy для использования ATLAS или Apple Accelerate Framework . Вы можете проверить, запустив ldd в файле numpy.core._dotblas или вызвав numpy.show_config () .

Выводы

MKL работает лучше всего, за ним следует GotoBlas2 .
В тесте на собственные значения GotoBlas2 работает на удивление хуже, чем ожидалось. Не уверен, почему это так.
Apple Accelerate Framework действительно хорошо работает, особенно в однопоточном режиме (по сравнению с другими реализациями BLAS).

И GotoBlas2, и MKL очень хорошо масштабируются с количеством потоков. Так что, если вам приходится иметь дело с большими матрицами, запуск его в нескольких потоках очень поможет.

В любом случае не используйте реализацию netlib blas по умолчанию, потому что она слишком медленная для любой серьезной вычислительной работы.

В нашем кластере я также установил ACML от AMD, и производительность была аналогична MKL и GotoBlas2 . У меня нет жестких цифр.

Я лично рекомендую использовать GotoBlas2, потому что его проще установить и он бесплатный.

Если вы хотите писать код на C ++ / C, также обратите внимание на Eigen3, который в некоторых случаях должен превзойти MKL / GotoBlas2, а также довольно прост в использовании.

Юмит
источник
Большое спасибо за подробный ответ!
Woltan
Очень подробно, спасибо! Интересно, три года спустя, будет ли OpenBLAS (насколько я знаю, это потомок GotoBLAS) работать лучше. Я где-то читал, что он превосходит MKL, но сейчас не могу найти источник.
Спасибо! Это мое впечатление to0 (мне было интересно, была ли это просто моя установка): OpenBLAS не так хорошо работает в многопоточном режиме, когда дело доходит до диагонализации матриц (я диагонализирую в scipy, который связан с OpenBLAS).
@William: обычно вам не нужно специально связывать scipy с openblas, потому что он будет использовать конфигурацию numpy во время установки, и на самом деле большинство вызовов BLAS / Lapack в любом случае будут перенаправлены на numpy. Поэтому, если numpy правильно связан с openblas, все должно работать нормально.
Юмит
@ Юмит: Спасибо! Сейчас я пытаюсь настроить numpy для ссылки на MKL.
20

Вот еще один тест (в Linux просто введите make): http://dl.dropbox.com/u/5453551/blas_call_benchmark.zip

http://dl.dropbox.com/u/5453551/blas_call_benchmark.png

Я не вижу существенной разницы между разными методами для больших матриц, между Numpy, Ctypes и Fortran. (Fortran вместо C ++ --- и если это важно, вероятно, ваш тест не работает.)

CalcTimeКажется, что ваша функция на C ++ имеет знаковую ошибку. ... + ((double)start.tv_usec))должно быть вместо этого ... - ((double)start.tv_usec)). Возможно, в вашем тесте есть и другие ошибки, например, сравнение между разными библиотеками BLAS или разными настройками BLAS, такими как количество потоков, или между реальным временем и временем процессора?

РЕДАКТИРОВАТЬ : не удалось подсчитать фигурные скобки в CalcTimeфункции - все в порядке.

В качестве ориентира: если вы проводите тест, всегда публикуйте где-нибудь весь код. Комментирование тестов, особенно когда это удивительно, без полного кода обычно непродуктивно.


Чтобы узнать, с каким BLAS Numpy связана ссылка, выполните:

$ питон
Python 2.7.2+ (по умолчанию, 16 августа 2011 г., 07:24:41) 
[GCC 4.6.1] в linux2
Для получения дополнительной информации введите «помощь», «авторские права», «кредиты» или «лицензия».
>>> импортировать numpy.core._dotblas
>>> numpy.core._dotblas .__ file__
'/usr/lib/pymodules/python2.7/numpy/core/_dotblas.so'
>>> 
$ ldd /usr/lib/pymodules/python2.7/numpy/core/_dotblas.so
    linux-vdso.so.1 => (0x00007fff5ebff000)
    libblas.so.3gf => /usr/lib/libblas.so.3gf (0x00007fbe618b3000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbe61514000)

ОБНОВЛЕНИЕ : если вы не можете импортировать numpy.core._dotblas, ваш Numpy использует свою внутреннюю резервную копию BLAS, которая работает медленнее и не предназначена для использования в вычислениях производительности! Ответ от @Woltan ниже указывает на то, что это объяснение разницы, которую он / она видит в Numpy против Ctypes + BLAS.

Чтобы исправить ситуацию, вам понадобится ATLAS или MKL --- ознакомьтесь с этими инструкциями: http://scipy.org/Installing_SciPy/Linux Большинство дистрибутивов Linux поставляются с ATLAS, поэтому лучший вариант - установить их libatlas-devпакет (название может отличаться) .

pv.
источник
Я проверил ваш тест; результаты такие же
jfs
Большое спасибо за ваш пост. Я провел ваш тест с этим результатом. Поэтому я не могу воспроизвести ваше. Чтобы проверить, какой BLAS использует мой numpy: я не могу import numpy.core._dotblas. В чем может быть проблема? Я постараюсь очистить свой тест и написать make-файл, чтобы другие могли его протестировать.
Woltan
2
@Woltan: тот факт, что вы не можете импортировать numpy.core._dotblas, означает, что ваш Numpy использует свою внутреннюю резервную копию BLAS ( более медленную и не предназначенную для использования в вычислениях производительности!), А не библиотеку BLAS, которая у вас есть в вашей системе. Это объясняет результаты, полученные вами при тестировании. Чтобы исправить ситуацию, вам необходимо установить версию BLAS, с которой может работать Numpy, что означает ATLAS или MKL. Вот набор инструкций: scipy.org/Installing_SciPy/Linux
pv.
@pv .: Не могли бы вы запустить тест Woltan для сравнения результатов.
jfs
1
На Mac вы можете использовать otool -Lвместо lddLinux
RichVel
9

Учитывая строгость, которую вы продемонстрировали в своем анализе, я удивлен результатами, полученными на данный момент. Я поставил это как «ответ», но только потому, что он слишком длинный для комментария и дает возможность (хотя я полагаю, вы это учли).

Я бы подумал, что подход numpy / python не добавит много накладных расходов для матрицы разумной сложности, поскольку по мере увеличения сложности доля, в которой участвует python, должна быть небольшой. Меня больше интересуют результаты в правой части графика, но показанное там несоответствие на порядки будет тревожным.

Интересно, используете ли вы лучшие алгоритмы, которые может использовать numpy. Из руководства по компиляции для linux:

"Build FFTW (3.1.2): SciPy Versions> = 0.7 и Numpy> = 1.2: из-за проблем с лицензией, конфигурацией и обслуживанием поддержка FFTW была удалена в версиях SciPy> = 0.7 и NumPy> = 1.2. Вместо этого теперь используется встроенная версия fftpack. Есть несколько способов воспользоваться скоростью FFTW, если это необходимо для анализа. Перейдите на версию Numpy / Scipy, которая включает поддержку. Установите или создайте свою собственную оболочку FFTW. См. http: //developer.berlios.de/projects/pyfftw/ в качестве неподтвержденного примера ".

Вы скомпилировали numpy с помощью mkl? ( http://software.intel.com/en-us/articles/intel-mkl/ ). Если вы работаете в Linux, инструкции по компиляции numpy с помощью mkl находятся здесь: http://www.scipy.org/Installing_SciPy/Linux#head-7ce43956a69ec51c6f2cedd894a4715d5bfff974 (несмотря на URL-адрес). Ключевая часть:

[mkl]
library_dirs = /opt/intel/composer_xe_2011_sp1.6.233/mkl/lib/intel64
include_dirs = /opt/intel/composer_xe_2011_sp1.6.233/mkl/include
mkl_libs = mkl_intel_lp64,mkl_intel_thread,mkl_core 

Если вы работаете в Windows, вы можете получить скомпилированный двоичный файл с помощью mkl (а также получить pyfftw и многие другие связанные алгоритмы) по адресу: http://www.lfd.uci.edu/~gohlke/pythonlibs/ , с Я хочу поблагодарить Кристофа Гольке из лаборатории динамики флуоресценции Калифорнийского университета в Ирвине.

Предостережение, в любом случае есть много проблем с лицензированием и т. Д., О которых следует знать, но страница Intel объясняет их. Опять же, я полагаю, вы об этом думали, но если вы соответствуете требованиям лицензирования (что в Linux очень легко сделать), это значительно ускорит работу с numpy по сравнению с использованием простой автоматической сборки, даже без FFTW. Мне будет интересно подписаться на эту тему и узнать, что думают другие. Тем не менее, отличная строгость и отличный вопрос. Спасибо, что разместили это.

Нечестивый
источник
Спасибо за подробный "комментарий" ^^. Чтобы прояснить мою настройку python / numpy / BLAS: я выполнил это руководство по установке. Я использую ОС Linux, и версии: Python 2.7, Scipy 0.9 Numpy 1.6. К сожалению, я не создавал FFTW
заранее
В каком-то смысле это удачно. Это означает, что есть огромные возможности для улучшения результатов python, и похоже, что вы хотели бы использовать python. Я думаю, что если вы измените свою сборку на ту, которая показана по ссылке, вы будете намного счастливее со скоростью numpy, хотя мне все равно будет интересно увидеть, как она по сравнению с вашей реализацией на C ++.
Profane
Вы также можете попробовать собрать ATLAS, но это звучало как слишком большая головная боль для моих потребностей в производительности, поэтому у меня нет никакого опыта. Я предполагаю, что если вы заинтересованы в использовании python, но можете использовать C ++, в какой-то момент стоимость настройки выполнения большого количества специальной компиляции перевесит экономию языка, и будет проще сделать C ++. Но mkl и fftw должны быть довольно простыми.
Profane
1
В настоящее время MKL, Accelerate и OpenBLAS схожи по производительности. Однако OpenBLAS более масштабируем, чем MKL.
Стурла Молден