Бенчмаркинг (Python против C ++ с использованием BLAS) и (Numpy) - PullRequest
104 голосов
/ 29 сентября 2011

Я хотел бы написать программу, которая широко использует функции линейной алгебры 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. Numpy

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

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 через общий объект

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

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;
}

Результаты

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

enter image description here

Вопросы

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

Скачать

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

Ответы [ 4 ]

72 голосов
/ 04 октября 2011

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

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

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

Аппаратное обеспечение

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

Sandy (MKL, OpenBLAS, ATLAS):

  • CPU : 2 x 16 Intel (R) Xeon (R) E2560 Sandy Bridge @ 2,00 ГГц (16 ядер)
  • RAM : 64 ГБ

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

  • CPU : 2x 20 Intel (R) Xeon (R) E2680 V2 Ivy Bridge @ 2,80 ГГц (20 ядер, с HT = 40 ядер)
  • RAM : 256 ГБ

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

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

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

Точка-Производственный эталон

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

Matrix multiplication (sizes=[1000,2000,3000,5000,8000])

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

Многопоточная производительность (8 потоков): multi-threaded (8 threads) performance

Потоки против размера матрицы (Ivy Bridge MKL) : Matrix-size vs threads

Benchmark Suite

benchmark suite

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

Многопоточная (8 нитей) производительность: enter image description here

Заключение

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

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

Также примерно на 30% увеличена производительность с Sandy Bridge до Ivy Bridge , что может быть связано с более высокой тактовой частотой (+ 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):

  • OS : Ubuntu Lucid 10,4 64 бит.
  • Процессор : 2 x 4 Intel (R) Xeon (R) E5504 @ 2,00 ГГц (8 ядер)
  • RAM : 24 ГБ
  • IntelКомпилятор : 11,1 / 073
  • Scipy : 0,8
  • Numpy : 1,5

Mac Book Pro (Accelerate Framework):

  • ОС : Mac OS X Snow Leopard (10,6)
  • CPU : 1 Intel Core 2 Duo 2,93 ГГц (2 ядра)
  • RAM : 4 ГБ
  • Scipy : 0,7
  • Numpy : 1,3

Mac-сервер (Accelerate Framework):

  • ОС : сервер Mac OS X Snow Leopard (10,6)
  • Процессор : 4 X Intel® R Xeon® R5555 @ 2,26 ГГц (8 ядер)
  • RAM : 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)

Результаты :

    System        |  size = 1000  | size = 2000 | size = 3000 |
netlib BLAS       |  1350 ms      |   10900 ms  |  39200 ms   |    
ATLAS (1 CPU)     |   314 ms      |    2560 ms  |   8700 ms   |     
MKL (1 CPUs)      |   268 ms      |    2110 ms  |   7120 ms   |
MKL (2 CPUs)      |    -          |       -     |   3660 ms   |
MKL (8 CPUs)      |    39 ms      |     319 ms  |   1000 ms   |
GotoBlas2 (1 CPU) |   266 ms      |    2100 ms  |   7280 ms   |
GotoBlas2 (2 CPUs)|   139 ms      |    1009 ms  |   3690 ms   |
GotoBlas2 (8 CPUs)|    54 ms      |     389 ms  |   1250 ms   |
Mac OS X (1 CPU)  |   143 ms      |    1060 ms  |   3605 ms   |
Mac Server (1 CPU)|    92 ms      |     714 ms  |   2130 ms   |

Dot product benchmark - chart

Benchmark Suite

Код :
Для получения дополнительной информации о наборе тестов см. здесь .

Результаты :

    System        | eigenvalues   |    svd   |   det  |   inv   |   dot   |
netlib BLAS       |  1688 ms      | 13102 ms | 438 ms | 2155 ms | 3522 ms |
ATLAS (1 CPU)     |   1210 ms     |  5897 ms | 170 ms |  560 ms |  893 ms |
MKL (1 CPUs)      |   691 ms      |  4475 ms | 141 ms |  450 ms |  736 ms |
MKL (2 CPUs)      |   552 ms      |  2718 ms |  96 ms |  267 ms |  423 ms |
MKL (8 CPUs)      |   525 ms      |  1679 ms |  60 ms |  137 ms |  197 ms |  
GotoBlas2 (1 CPU) |  2124 ms      |  4636 ms | 147 ms |  456 ms |  743 ms |
GotoBlas2 (2 CPUs)|  1560 ms      |  3278 ms | 116 ms |  295 ms |  460 ms |
GotoBlas2 (8 CPUs)|   741 ms      |  2914 ms |  82 ms |  262 ms |  192 ms |
Mac OS X (1 CPU)  |   948 ms      |  4339 ms | 151 ms |  318 ms |  566 ms |
Mac Server (1 CPU)|  1033 ms      |  3645 ms |  99 ms |  232 ms |  342 ms |

Benchmark suite - chart

Установка

Установка 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 .
В тесте eigenvalue GotoBlas2 работает на удивление хуже, чем ожидалось.Не уверен, почему это так.
Apple Accelerate Framework работает действительно хорошо, особенно в однопоточном режиме (по сравнению с другими реализациями BLAS).

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

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

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

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

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

53 голосов
/ 30 сентября 2011

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

woltan's benchmark

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

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

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

номер

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

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

20 голосов
/ 29 сентября 2011

Вот еще один тест (в 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
Python 2.7.2+ (default, Aug 16 2011, 07:24:41) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import 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 (имя может отличаться).

9 голосов
/ 29 сентября 2011

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

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

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

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

Вы скомпилировали numy с помощью 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/, с долгом благодарности Кристофу Гольке в Лаборатории для динамики флуоресценции, UC Irvine.

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

...