Дело в том, что для выполнения многих операций numpy вызывает функции из библиотеки линейной алгебры. Вот в ней-то обычно и кроется проблема. К счастью, все довольно легко исправимо.
Итак, возможно три ситуации:
- у вас не установлены никакие библиотеки линейной алгебры и тогда numpy использует встроенную библиотеку, и она, надо сказать, весьма медленная;
- у вас уже установлены классические библиотеки типа ATLAS и BLAS, и они умеют использовать только один процессор;
- у вас установлена современная библиотека OpenBLAS, MKL и им подобные.
Проведем простой тест. Запустите вот эту программу:
import numpy as np
size = 10000
a = np.random.random_sample((size, size))
b = np.random.random_sample((size, size))
n = np.dot(a,b)
После чего, если работаете в Linux, то запустите top, а если вы работаете в Windows, зайдите на вкладку в “Быстродействие” в диспетчере задач (вызывается по Ctrl+Shift+Esc)… Если top показывает загруженность на уровне 100%, а индикатор “Загрузка ЦП” на вкладке “Быстродействие”, наоборот, показывает значение многократно ниже 100%, значит вычислениями занято лишь одно ядро — и эта статья для вас. Те, у кого задействованы все процессоры, могут радоваться — у них все в порядке — и дальше можно не читать.
Решение для Windows
Теоретически, можно, конечно, найти исходники библиотек, перекомпилировать их и пересобрать numpy. Я даже слышал, что кто-то писал, что он видел людей, которые говорили, что им это удалось… В общем, самый простой способ — это установить научный дистрибутив Python, например, Anaconda или Canopy. В дистрибутив входит не только python и numpy, но и целая куча полезных библиотек для расчетов и визуализации.
После чего можете перезапустить начальный тест, чтобы убедиться, что скорость возросла в разы.
Решение для Linux
На самом деле вы также можете установить готовый дистрибутив Anaconda, Canopy или что-то другое сразу со всеми библиотеками. Но если предпочитаете собирать своими руками, то читайте дальше — там есть все рецепты.
Проверка библиотек
Как вы помните, возможны два варианта:
- у вас установлены “олдскульные” (или “устаревшие”, кому как нравится) библиотеки (например, ATLAS);
- у вас не установлены библиотеки, и numpy использует встроенную библиотеку (которая еще медленнее)
Если у вас стоит свежая версия numpy (>1.10), то, зайдя в каталог, куда установлен numpy (обычно это /usr/local/lib/python2.7/dist-packages/numpy, но в зависимости от версии Linux и Python путь может меняться) и выполните следующие команды в консоли:
cd core
ldd multiarray.so
В более ранних версиях numpy библиотеки multiarray.so нет, зато есть _dotblas.so:
ldd _dotblas.so
Вывод команды ldd покажет вам, использует ли numpy сторонние библиотеки линейной алгебры.
linux-vdso.so.1 => (0x00007fffe58a2000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8adbff4000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f8adbdd6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8adba10000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8adc68c000)
Если в листинге вы не видите libblas.so, значит ваша numpy использует свою внутреннюю библиотеку. Если видите, значит у вас стоит ATLAS или BLAS.
В любом случае сначала вам нужна правильна библиотека линейной алгебры.
Установка OpenBLAS
OpenBLAS — хорошая библиотека алгоритмов и функций линейной алгебры, которые лежат в основе современных методов анализа данных и машинного обучения.
Прежде всего вам потребуется компилятор Фортрана, поскольку OpenBLAS не совместим со стандартным компилятором g77.
sudo apt-get install gfortran
Загрузите OpenBLAS с github’а (предварительно вернувшись в подходящий для установки каталог):
git clone https://github.com/xianyi/OpenBLAS.git
Теперь зайдите в каталог и запустите сборку:
cd OpenBLAS
make FC=gfortran
Когда компиляция и сборка успешно завершатся, установите библиотеку.
sudo make install
По умолчанию, библиотека будет установлена в /opt/OpenBLAS. Если вы хотите установить ее в другое место, запустите make install с ключом PREFIX:
sudo make install PREFIX=/your/preferred/location
Переназначение библиотек
Если ранее вы выяснили, что у вас уже установлена какая-то библиотека линейной алгебры, то вам достаточно запустить команду переназначения библиотек:
sudo update-alternatives --install /usr/lib/libblas.so.3 libblas.so.3 /opt/OpenBLAS/lib/libopenblas.so 50
После этого OpenBLAS по умолчанию станет библиотекой линейной алегбры не только для numpy, а вообще для всех ваших программ и библиотек.
И снова запустите тест, чтобы увидеть, как у вас при вычислениях теперь задействованы все процессоры.
Собираем правильный numpy
Если у вас numpy работал на встроенной библиотеке, то вам придется его пересобрать, чтобы он подхватил только что установленный OpenBLAS.
Сначала избавьтесь от дефектной библиотеки:
sudo pip uninstall numpy
После чего создайте в домашнем каталоге файл .numpy-site.cfg следующего содержания:
[default]
include_dirs = /opt/OpenBLAS/include
library_dirs = /opt/OpenBLAS/lib
[openblas]
openblas_libs = openblas
include_dirs = /opt/OpenBLAS/include
library_dirs = /opt/OpenBLAS/lib
[lapack]
lapack_libs = openblas
[atlas]
atlas_libs = openblas
libraries = openblas
Если вы ранее выбрали нестандартное расположение для OpenBLAS, то измените пути в файле. А теперь установите numpy заново:
sudo pip install numpy
Когда компиляция и установка завершатся, запустите начальный тест, чтобы убедиться, что теперь процессоры не простаивают. Вот и все.
Комментарии (14)
masai
30.12.2015 19:54+2Теоретически, можно, конечно, найти исходники библиотек, перекомпилировать их и пересобрать numpy. Я даже слышал, что кто-то писал, что он видел людей, которые говорили, что им это удалось… В общем, самый простой способ — это установить научный дистрибутив Python, например, Anaconda или Canopy.
Если не хочется ставить анаконду ради одной библиотеки, можно просто скачать Wheel-пакет для 32- или 64-битной версии Windows, собранный с MKL. Устанавливается одной командой. Например,
pip install numpy-1.10.2+mkl-cp35-none-win_amd64.whl
установит Numpy 1.10.2, собранный с MKL, для 64-битного python 3.5.
На сайте полно скомпилированных пакетов для научных вычислений, пожалуйста, не положите его ненароком. ;)Roman_Kh
30.12.2015 22:29Спасибо за отличное дополнение.
Раньше бесплатный MKL тоже был однопоточным. Но Intel в итоге сделала community лицензию для своих библиотек без ограничений по потокам — software.intel.com/sites/campaigns/nest
masai
30.12.2015 23:45+2Забыл добавить. Посмотреть информацию о используемых библиотеках можно проще:
import numpy as np np.show_config()
Если установить dev-пакеты для OpenBLAS, то numpy при сборке сам подхватит библиотеку. Например, в Ubuntu нужно установить libopenblas-dev и libopenblas-base. Если достаточно версии из репозитория, то ничего самому компилировать не нужно. Достаточно установить пакеты и переустановить numpy через pip.
Использовать pip с sudo тоже не стоит, по-моему. Лучше, когда есть только один общесистемный менеджер пакетов. Я свежие математические библиотеки устанавливаю в виртуальное окружение. Довольно удобно, легко обновлять и доустанавливать что-то, а главное — не нужны права root.
Psychopompe
31.12.2015 15:03А совет для тех, у кого не deb-based?
masai
31.12.2015 17:40Если кратко, суть статьи — перекомпилировать Numpy. Это отлично делает pip, но надо в своём дистрибутиве сперва установить OpenBLAS или MKL.
Psychopompe
04.01.2016 00:34Имею следующий выхлоп:
$ dnf list installed | grep openblas openblas.x86_64 0.2.15-2.fc23 @updates openblas-devel.x86_64 0.2.15-2.fc23 @updates openblas-openmp.x86_64 0.2.15-2.fc23 @updates openblas-openmp64.x86_64 0.2.15-2.fc23 @updates openblas-openmp64_.x86_64 0.2.15-2.fc23 @updates openblas-serial64.x86_64 0.2.15-2.fc23 @updates openblas-serial64_.x86_64 0.2.15-2.fc23 @updates openblas-threads.x86_64 0.2.15-2.fc23 @updates openblas-threads64.x86_64 0.2.15-2.fc23 @updates openblas-threads64_.x86_64 0.2.15-2.fc23 @updates
$ ldd multiarray.so linux-vdso.so.1 (0x00007ffc94ee8000) libopenblas.so.0 => /lib64/libopenblas.so.0 (0x00007f9316707000) libm.so.6 => /lib64/libm.so.6 (0x00007f9316405000) libpython2.7.so.1.0 => /lib64/libpython2.7.so.1.0 (0x00007f931603b000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f9315e1e000) libc.so.6 => /lib64/libc.so.6 (0x00007f9315a5d000) /lib64/ld-linux-x86-64.so.2 (0x0000555786b82000) libgfortran.so.3 => /lib64/libgfortran.so.3 (0x00007f9315730000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f931552c000) libutil.so.1 => /lib64/libutil.so.1 (0x00007f9315329000) libquadmath.so.0 => /lib64/libquadmath.so.0 (0x00007f93150e9000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f9314ed2000)
Что-то не могу разобраться в значениях top, на которое из двух смотреть?
%Cpu(s): 79.1 us, 10.4 sy, 0.0 ni, 9.3 id, 1.2 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 8010052 total, 141700 free, 4756624 used, 3111728 buff/cache KiB Swap: 2095100 total, 2060716 free, 34384 used. 2810328 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 10185 USER 20 0 2642864 2.276g 11056 R 100.0 29.8 3:54.40 python
Не пинайте сильноя только начал разбираться в проге основательноRoman_Kh
04.01.2016 13:18После запуска top нажмите «1», тогда вместо строки "%Cpu(s)" отобразятся данные отдельно по каждому процессору.
Psychopompe
04.01.2016 19:30Ага, спасибо. Одно ядро загружено полностью, второе — процентов на 20. Так и должно быть?
Roman_Kh
04.01.2016 21:56Если вы запускаете пример из статьи, то он должен грузить на 100% все имеющиеся ядра, а у вас загружено только одно.
Psychopompe
04.01.2016 22:28Странно, именно его запускаю. Что может быть не так?
Roman_Kh
05.01.2016 00:24Причин может быть много.
У вас Linux установлен прямо на «железо» или на виртуальную машину?
Попробуйте запустить тест вот так:
$ OMP_NUM_THREADS=4 python test.py
barabashka
Есть альтернативный способ добавить многопоточность к numpy: библиотека numexpr
chersanya
Там же вроде нет линейной алгебры?