Большая часть функции Numpy по умолчанию включает многопоточность.
например, я работаю на 8-ядерном компьютере Intel Cpu, если я запускаю скрипт
import numpy as np
x=np.random.random(1000000)
for i in range(100000):
np.sqrt(x)
Linux top
покажет 800% загрузки процессора во время работы,
что означает, что numpy автоматически обнаруживает, что моя рабочая станция имеет 8 ядер, и np.sqrt
автоматически использует все 8 ядер для ускорения вычислений.
Однако я обнаружил странную ошибку. Если я запускаю скрипт
import numpy as np
import pandas as pd
df=pd.DataFrame(np.random.random((10,10)))
df+df
x=np.random.random(1000000)
for i in range(100000):
np.sqrt(x)
использование процессора составляет 100%! Это означает, что если вы добавите две панды DataFrame перед запуском любой функции numpy, функция автоматической многопоточности numpy пропадет без предупреждения! Это абсолютно не разумно, почему расчет Pandas dataFrame влияет на настройку потоков Numpy? Это ошибка? Как обойти это?
PS:
Я копаю дальше, используя perf
инструмент Linux .
работает первый скрипт показывает
Во время работы второй скрипт показывает
Таким образом, оба сценария включают в себя libmkl_vml_avx2.so
, в то время как первый сценарий включает дополнительный, libiomp5.so
который, похоже, связан с openMP.
А так как vml означает библиотеку математики для векторов Intel, то, согласно vml doc, я предполагаю, что по крайней мере ниже функции автоматически являются многопоточными.
import numpy as np import pandas as pd import os os.environ["MKL_NUM_THREADS"] = '4' print(os.environ["MKL_NUM_THREADS"]) df=pd.DataFrame(np.random.random((10,10))) df+df print(os.environ["MKL_NUM_THREADS"]) a = np.random.random((20000000, 3)) b = np.random.random((3, 30)) for _ in range(10): c = np.dot(a, b)
Ответы:
Pandas использует внутреннюю
numexpr
часть для вычисления некоторых операций иnumexpr
устанавливает максимальное количество потоков для vml равным 1, когда оно импортируется :и он импортируется пандами, когда
df+df
оценивается в expressions.py :Тем не менее, распределение Анаконда также использует VML-функциональность для таких функций , как
sqrt
,sin
,cos
и так далее - и один разnumexpr
установить максимальное число VML-нитей в 1, Numpy-функции больше не использовать распараллеливание.Проблему легко увидеть в gdb (используя ваш медленный скрипт):
то есть, мы видим,
numexpr
устанавливает количество потоков равным 1. Что позже используется при вызове функции vml-sqrt:Таким образом, мы можем видеть, что numpy использует реализацию vml,
vdSqrt
которая использует,mkl_vml_serv_threader_d_1i_1o
чтобы решить, следует ли выполнять вычисления параллельно, и выглядит количество потоков:регистр
%rax
имеет максимальное количество потоков и равен 1.Теперь мы можем использовать
numexpr
для увеличения количества vml-потоков , то есть:Теперь используются несколько ядер!
источник
numexpr
за кулисами.Глядя на numpy, это выглядит так, как будто у него были проблемы с вкл / выкл многопоточности, и в зависимости от того, какую версию вы используете, вы можете ожидать сбой при увеличении ne.set_vml_num_threads () ..
http://numpy-discussion.10968.n7.nabble.com/ANN-NumExpr-2-7-0-Release-td47414.html
Мне нужно разобраться, как это приклеено к интерпретатору python, учитывая ваш пример кода, где кажется, что он как-то позволяет нескольким явно синхронным / упорядоченным вызовам np.sqrt () выполняться параллельно. Я предполагаю, что если интерпретатор Python всегда просто возвращает ссылку на объект, когда он выталкивает стек, а в вашем примере это просто передача этих ссылок, а не назначение или манипулирование ими каким-либо образом, это было бы хорошо. Но если последующие итерации цикла зависят от предыдущих, тогда кажется менее ясным, как их можно безопасно распараллелить. Возможно бесшумный отказ / неправильные результаты - результат хуже, чем сбои.
источник
Я думаю, что ваша первоначальная предпосылка может быть неправильной -
Вы заявили: это означает, что numpy автоматически обнаруживает, что моя рабочая станция имеет 8 ядер, а np.sqrt автоматически использует все 8 ядер для ускорения вычислений.
Одиночная функция np.sqrt () не может угадать, как она будет в следующий раз вызываться или возвращаться до того, как она частично завершится. В python есть механизмы параллелизма, но ни один из них не является автоматическим.
Сказав это, интерпретатор python может оптимизировать цикл for для параллелизма, что может быть тем, что вы видите, но я сильно подозреваю, что если вы посмотрите на время настенного времени, чтобы этот цикл выполнялся, он не будет отличается независимо от того, используете ли вы (по-видимому) 8 ядер или 1 ядро.
ОБНОВЛЕНИЕ: прочитав немного больше комментариев, кажется, что многоядерное поведение, которое вы видите, связано с распределением anaconda интерпретатора python. Я посмотрел, но не смог найти никакого исходного кода для него, но похоже, что лицензия python позволяет организациям (таким как anaconda.com) компилировать и распространять производные интерпретатора, не требуя публикации их изменений.
Я предполагаю, что вы можете обратиться к людям из анаконды - поведение, которое вы видите, будет трудно понять, не зная, что / если что-то изменилось в переводчике ..
Также сделайте быструю проверку времени настенных часов с / без оптимизации, чтобы увидеть, действительно ли оно в 8 раз быстрее - даже если у вас действительно работают все 8 ядер вместо 1, было бы полезно узнать, действительно ли результаты 8х. быстрее или если используются спин-блокировки, которые все еще сериализуются на одном мьютексе.
источник