Бенчмаркінг (python vs. 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 Себастьян зробив це посилання можливим ^^)


у вашому підході до типів ви маєте розподіл пам'яті всередині вимірюваної функції. Чи відповідає ваш код c ++ такому підходу? Але порівняно з множенням матриць це не повинно мати великої різниці ....
rocksportrocker

@rocksportrocker Ви маєте рацію. Розподіл пам'яті для rматриці несправедливий. Я вирішую "питання" зараз і публікую нові результати.
Волтан

1. переконайтеся, що масиви мають однаковий макет пам'яті np.ascontiguousarray()(врахуйте порядок C проти Fortran). 2. переконайтеся, що np.dot()використовує те саме libblas.so.
jfs

@JFSebastian Обидва масиву m1і m2має ascontiguousarrayпрапор як True. І numpy використовує той самий спільний об'єкт, що і C. Щодо порядку масиву: На даний момент я не зацікавлений результатом обчислення, отже, порядок не має значення.
Волтан

1
@Woltan: не використовуйте filefactory, послуга жахлива. Я додав ваш тест до github: woltan-benchmark . Якщо ви використовуєте github, я можу додати вас як співавтора.
jfs

Відповіді:


58

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

орієнтир волтана

Як ви вважаєте, мій підхід справедливий, чи є якісь непотрібні накладні витрати, яких я можу уникнути?

Це здається справедливим через відсутність різниці в результатах.

Чи очікуєте ви, що результат покаже таке величезне розбіжність між підходом c ++ та python? Обидва використовують спільні об'єкти для своїх розрахунків.

Немає.

Оскільки я вважаю за краще використовувати python для своєї програми, що я можу зробити для підвищення продуктивності під час виклику підпрограм BLAS або LAPACK?

Переконайтесь, що numpy використовує оптимізовану версію бібліотек BLAS / LAPACK у вашій системі.


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 ГБ

Програмне забезпечення

Програмний стек для обох вузлів сам. Замість того , щоб GotoBLAS2 , OpenBLAS використовується , і є також багато-ATLAS BLAS , який встановлений в 8 потоків (зашиті).

  • ОС : Suse
  • Intel Compiler : 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) : Матричний розмір проти ниток

Бенчмарк люкс

люкс із орієнтирами

Продуктивність одного потоку: введіть тут опис зображення

Продуктивність багатопотокових (8 ниток): введіть тут опис зображення

Висновок

Нові результати порівняння схожі на результати оригінальної відповіді. OpenBLAS і MKL працюють на одному рівні, за винятком тесту Eigenvalue . У власних значеннях тест виконує тільки досить добре на OpenBLAS в режимі однотрідового . У багатопотоковому режимі продуктивність гірша.

«Розмір матриці проти потоків графіка» також показує , що , хоча МКЛО, а також OpenBLAS як правило , добре масштабується з числом ядер / потоків, це залежить від розміру матриці. Для малих матриць додавання більше ядер не дуже покращить продуктивність.

Також спостерігається приблизно на 30% підвищення продуктивності від Sandy Bridge до Ivy Bridge, що може бути пов'язано з більш високою тактовою частотою (+ 0,8 ГГц) та / або кращою архітектурою.


Оригінальна відповідь (04.10.2011):

Деякий час тому мені довелося оптимізувати деякі лінійні алгебри обчислення / алгоритми, які були написані в python, використовуючи numpy та BLAS, тому я орієнтував / перевіряв різні конфігурації numpy / BLAS.

Спеціально я тестував:

  • Опудало з ATLAS
  • Numpy з GotoBlas2 (1,13)
  • Галька з 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
  • Спійпі : 0,8
  • Нюміт : 1,5

Mac Book Pro (Accelerate Framework):

  • ОС : Mac OS X Snow Leopard (10.6)
  • Процесор : 1 Intel Core 2 Duo 2,93 ГГц (2 ядра)
  • ОЗУ : 4 Гб
  • Науковий шум : 0,7
  • Нюм : 1.3

Mac Server (Accelerate Framework):

  • ОС : Mac OS X Snow Leopard Server (10.6)
  • Процесор : 4 X Intel (R) Xeon (R) E5520 @ 2,26 ГГц (8 ядер)
  • ОЗУ : 4 Гб
  • Спійпі : 0,8
  • Нюміт : 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)

Результати :

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

Точковий орієнтир продукту - діаграма

Бенчмарк люкс

Код :
Додаткову інформацію про набір тестів див. Тут .

Результати :

    Система | власні значення | svd | det | інв | крапка |
netlib BLAS | 1688 мс | 13102 мс | 438 мс | 2155 мс | 3522 мс |
ATLAS (1 процесор) | 1210 мс | 5897 мс | 170 мс | 560 мс | 893 мс |
MKL (1 процесор) | 691 мс | 4475 мс | 141 мс | 450 мс | 736 мс |
MKL (2 процесора) | 552 мс | 2718 мс | 96 мс | 267 мс | 423 мс |
MKL (8 процесорів) | 525 мс | 1679 мс | 60 мс | 137 мс | 197 мс |  
GotoBlas2 (1 процесор) | 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 Server (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 Ports, вона налаштує 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, а також досить простий у використанні.


Дуже дякую за цю ретельну відповідь!
Волтан

Дуже всебічно, дякую! Цікаво, що через три роки, чи OpenBLAS (наскільки я знаю, це нащадок GotoBLAS), зробив би кращі результати. Я десь читав, що він перевершує MKL, але зараз не можу знайти джерело.

Дякую! Це моє враження до 0 (мені було цікаво, чи це була лише моя установка): OpenBLAS не працює так добре в багатопотоковому режимі, коли мова йде про діагоналізацію матриць (я діагоналізую в scipy, що пов'язано з OpenBLAS).

@William: зазвичай вам не потрібно спеціально пов'язувати scipy з openblas, оскільки він буде використовувати конфігурацію numpy під час встановлення, і фактично більшість викликів BLAS / Lapack будуть перенаправлені на numpy у будь-якому випадку. Тож якщо numpy правильно пов’язаний з openblas, все повинно працювати нормально.
Ümit

@ Ümit: Дякую! Я зараз намагаюся налаштувати 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, такими як кількість потоків, або між режимом реального часу та процесором?

EDIT : не вдалося підрахувати дужки у CalcTimeфункції - це нормально.

Як настанова: якщо ви робите орієнтир, будь ласка, завжди публікуйте весь код десь. Коментувати орієнтири, особливо коли дивно, без повного коду, як правило, не є результативним.


Щоб дізнатися, з чим пов’язаний BLAS Numpy, виконайте:

$ пітон
Python 2.7.2+ (за замовчуванням, 16 серпня 2011, 07:24:41) 
[GCC 4.6.1] на linux2
Введіть "довідка", "авторське право", "кредити" або "ліцензія" для отримання додаткової інформації.
>>> імпорт numpy.core._dotblas
>>> numpy.core._dotblas .__ файл__
'/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 vs. Ctypes + BLAS.

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


Я запустив ваш орієнтир; результати однакові
jfs

Дуже дякую за вашу публікацію. Я оцінив ваш показник з цим результатом. Тому я не можу відтворити твою. Щоб перевірити, який BLAS використовує мій рядок: Я не можу import numpy.core._dotblas. Яка тут може бути проблема? Я спробую очистити свій бенчмарк із запису макіяжу, щоб інші перевірили його.
Волтан

2
@Woltan: той факт, що ви не можете імпортувати numpy.core._dotblas, означає, що ваш Numpy використовує свою внутрішню резервну копію BLAS ( повільніше і не призначена для використання в обчисленні продуктивності!), А не бібліотеку BLAS у вас у вашій системі. Це пояснює результати, які ви отримали від еталону. Щоб виправити ситуацію, вам потрібно встановити версію BLAS. Numpy може працювати з --- що означає ATLAS або MKL. Ось набір інструкцій: scipy.org/Installing_SciPy/Linux
pv.

@pv. Чи можете ви запустити показник Волтана для порівняння результатів.
jfs

1
На Mac можна використовувати otool -Lзамість lddLinux
RichVel

9

Враховуючи суворість, яку ви продемонстрували під час свого аналізу, я здивований результатами, що були досягнуті. Я ставлю це як "відповідь", але тільки тому, що це занадто довго для коментарів і дає можливість (хоча, я думаю, ви це врахували).

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

Цікаво, чи використовуєте ви найкращі алгоритми, якими може користуватися нуме. З посібника з компіляції для Linux:

"Build FFTW (3.1.2): SciPy Versions> = 0.7 and Numpy> = 1.2: Через проблеми з ліцензією, конфігурацією та технічним обслуговуванням підтримку FFTW було видалено у версіях SciPy> = 0.7 та NumPy> = 1.2. Натомість тепер використовується вбудована версія fftpack. Існує кілька способів скористатись швидкістю FFTW, якщо це необхідно для аналізу. Перейдіть до версії Numpy / Scipy, яка включає підтримку. Встановіть або створіть власну обгортку FFTW. Див. http: //developer.berlios.de/projects/pyfftw/ як непідтверджений приклад. "

Ви склали nummy з 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/ , з a вдячність Крістофу Голке в Лабораторії флуоресцентної динаміки, УК Ірвайн.

У будь-якому випадку у Caveat існує багато питань, пов'язаних з ліцензуванням, і так далі, про що слід знати, але сторінка Intel пояснює це. Знову ж таки, я думаю, ви це врахували, але якщо ви дотримаєтесь ліцензійних вимог (що в Linux є дуже простим зробити), це значно прискорить нумерну частину щодо використання простої автоматичної збірки, навіть не FFTW. Мені буде цікаво прослідкувати за цією темою і подивитися, що думають інші. Незалежно, відмінна суворість та відмінне запитання. Дякуємо за публікацію


Дякую за твій ретельний "коментар" ^^. Щоб уточнити налаштування python / numpy / BLAS: я дотримувався цього посібника з установки. Я на ОС Linux і версії: Python 2.7, Scipy 0.9 Numpy 1.6. На жаль, я не створив FFTW раніше, а також не використав
mkl

Певним чином, пощастило. Це означає, що є багато можливостей для покращення результатів python, і це здається, що ви хочете використовувати python. Я думаю, що якщо ви зміните свою збірку на ту, що показана на посиланні, ви будете набагато щасливішими зі швидкістю numpy, хоча я все одно захоплююсь, як це порівняти з вашою реалізацією на C ++.
Профан

Ви також можете спробувати створити ATLAS, але це звучало як занадто багато головних болів для моїх потреб у виконанні, тому я не маю жодного досвіду. Я думаю, якщо вам цікаво використовувати python, але зможете використовувати C ++, можливо, в якийсь момент вартість установки, що робить багато спеціальних компіляцій, буде перевищувати економію мови, і це буде простіше зробити c ++. Але mkl і fftw мають бути досить простими.
Профан

1
Наразі MKL, Accelerate та OpenBLAS схожі за своєю ефективністю. Однак OpenBLAS є більш масштабованим, ніж MKL.
Стурла Молден
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.