Дивна помилка в Pandas і Numpy щодо багатопотокових


25

Більшість функцій 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 впливало на параметр Numpy threadading? Це помилка? Як обійти це?введіть тут опис зображення


PS:

Я копаю далі за допомогою perfінструмента Linux .

запуск перших показів сценарію

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

Під час запуску показує другий сценарій

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

Таким чином, обидва сценарії включають libmkl_vml_avx2.so, тоді як перший сценарій включає додатковий, libiomp5.soякий, здається, пов'язаний з openMP.

А оскільки vml означає векторну математичну бібліотеку Intel, то, згідно з vml doc, я припускаю, що принаймні нижче функції всі автоматично багатопотокові

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


Я не впевнений, що розумію ваше запитання. Чи можете ви докладно?
— AMC

@AMC Я оновив свою публікацію, сподіваюся, що тепер зрозуміло
— користувач15964

Я думаю, що потрібна додаткова інформація, наприклад, np, панди, версія, процесор, тип ОС ... Я не можу відтворити на своїй машині. Він не використовує декілька процесорів в обох кодах.
— мисливець

@hunzter Добре, ось інформація: Ubuntu 16.04.5 LTS numpy 1.17.2 py37haad9e8e_0 pandas 0.25.1 py37he6710b0_0 Intel (R) Xeon (R) CPU E5-1680 v4 @ 3.40GHz. PS. Я використовую anaconda
— user15964

1
Чи можете ви, будь ласка, перевірити це: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)
— Стас Бузулук

Відповіді:


13

Pandas використовує numexprпід кришкою обчислення деяких операцій і numexprвстановлює максимальну кількість потоків для vml до 1, коли він імпортується :

# The default for VML is 1 thread (see #39)
set_vml_num_threads(1)

і він імпортується пандами, коли df+dfвін оцінюється в expressions.py :

from pandas.core.computation.check import _NUMEXPR_INSTALLED

if _NUMEXPR_INSTALLED:
   import numexpr as ne

Тим НЕ менше, розподіл Анаконда також використовує VML-функціональність для таких функцій , як sqrt, sin, cosі так далі - і один раз numexprвстановити максимальне число VML-ниток в 1, Numpy-функції більше не використовувати розпаралелювання.

Проблему можна легко побачити в gdb (використовуючи повільний сценарій):

>>> gdb --args python slow.py
(gdb) b mkl_serv_domain_set_num_threads
function "mkl_serv_domain_set_num_threads" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (mkl_serv_domain_set_num_threads) pending.
(gbd) run
Thread 1 "python" hit Breakpoint 1, 0x00007fffee65cd70 in mkl_serv_domain_set_num_threads () from /home/ed/anaconda37/lib/python3.7/site-packages/numpy/../../../libmkl_intel_thread.so
(gdb) bt 
#0  0x00007fffee65cd70 in mkl_serv_domain_set_num_threads () from /home/ed/anaconda37/lib/python3.7/site-packages/numpy/../../../libmkl_intel_thread.so
#1  0x00007fffe978026c in _set_vml_num_threads(_object*, _object*) () from /home/ed/anaconda37/lib/python3.7/site-packages/numexpr/interpreter.cpython-37m-x86_64-linux-gnu.so
#2  0x00005555556cd660 in _PyMethodDef_RawFastCallKeywords () at /tmp/build/80754af9/python_1553721932202/work/Objects/call.c:694
...
(gdb) print $rdi
$1 = 1

тобто ми можемо бачити, numexprвстановлює кількість потоків на 1. Що пізніше використовується, коли функція vml-sqrt називається:

(gbd) b mkl_serv_domain_get_max_threads
Breakpoint 2 at 0x7fffee65a900
(gdb) (gdb) c
Continuing.

Thread 1 "python" hit Breakpoint 2, 0x00007fffee65a900 in mkl_serv_domain_get_max_threads () from /home/ed/anaconda37/lib/python3.7/site-packages/numpy/../../../libmkl_intel_thread.so
(gdb) bt
#0  0x00007fffee65a900 in mkl_serv_domain_get_max_threads () from /home/ed/anaconda37/lib/python3.7/site-packages/numpy/../../../libmkl_intel_thread.so
#1  0x00007ffff01fcea9 in mkl_vml_serv_threader_d_1i_1o () from /home/ed/anaconda37/lib/python3.7/site-packages/numpy/../../../libmkl_intel_thread.so
#2  0x00007fffedf78563 in vdSqrt () from /home/ed/anaconda37/lib/python3.7/site-packages/numpy/../../../libmkl_intel_lp64.so
#3  0x00007ffff5ac04ac in trivial_two_operand_loop () from /home/ed/anaconda37/lib/python3.7/site-packages/numpy/core/_multiarray_umath.cpython-37m-x86_64-linux-gnu.so

Таким чином, ми можемо бачити numpy, що використовує vml, реалізація vdSqrtякого використовує, mkl_vml_serv_threader_d_1i_1oщоб вирішити, чи слід проводити обчислення паралельно, і це виглядає кількість потоків:

(gdb) fin
Run till exit from #0  0x00007fffee65a900 in mkl_serv_domain_get_max_threads () from /home/ed/anaconda37/lib/python3.7/site-packages/numpy/../../../libmkl_intel_thread.so
0x00007ffff01fcea9 in mkl_vml_serv_threader_d_1i_1o () from /home/ed/anaconda37/lib/python3.7/site-packages/numpy/../../../libmkl_intel_thread.so
(gdb) print $rax
$2 = 1

регістр %raxмає максимальну кількість потоків і він дорівнює 1.

Тепер ми можемо використовувати numexprдля збільшення кількості vml-потоків , тобто:

import numpy as np
import numexpr as ne
import pandas as pd
df=pd.DataFrame(np.random.random((10,10)))
df+df

#HERE: reset number of vml-threads
ne.set_vml_num_threads(8)

x=np.random.random(1000000)
for i in range(10000):
    np.sqrt(x)     # now in parallel

Зараз використовується декілька ядер!


Дуже дякую! Нарешті, чудова відповідь пояснює все. Зрештою, це numexprпозаду сцени.
— користувач15964

Домовились .. гарне копання! Наступне запитання, хоча .. чому numxpr натискає нитку до 1? Можливо, через проблеми з нестабільністю та безпечністю для потоків? Замість того, щоб підштовхувати підрахунок до 8, може бути безпечніше перейти до безпечної / поточної версії NumPy. Можливо, також добре перевірити цю змінну за допомогою останнього та найкращого NumPy на випадок, якщо вона насправді вже не потрібна, отже технічно помилка.
— Ендрю Атренс

@AndrewAtrens ви можете подивитися на github.com/pydata/numexpr/issues/39 і github.com/pydata/numexpr/issues/355
— EAD

2

Дивлячись на 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 завжди просто повертає посилання на об'єкт, коли він вискакує стек, і у вашому прикладі є просто виведення цих посилань і не присвоєння їм чи жодним чином їх маніпулювання, було б добре. Але якщо наступні ітерації циклу залежать від попередніх, то видається менш зрозумілим, як їх можна було б безпечно паралелізувати. Можливо, тихий збій / неправильні результати - результат гірший, ніж збої.


Привіт, Ендрю Атренс, ти майже там. Це проблема ne.set_vml_num_threads (). Дуже дякую за ваш присвячений час моєму питанню.
— користувач15964

Happy Trails :)
— Андрій Атренс

0

Я думаю, що ваша початкова передумова може бути неправильною -

Ви заявили: Що означає numpy автоматично виявляє, що моя робоча станція має 8 ядер, і np.sqrt автоматично використовує всі 8 ядер для прискорення обчислення.

Одна функція np.sqrt () не може здогадатися, як її буде викликано або повернути далі, перш ніж вона буде частково виконана. У python є механізми паралелізму, але жоден не є автоматичним.

Тепер, сказавши це, інтерпретатору python, можливо, вдасться оптимізувати цикл for для паралелізму, який може бути тим, що ви бачите, але я сильно підозрюю, якщо ви подивитесь на настінний час для виконання цього циклу, це не буде відрізняється незалежно від того, якщо ви (мабуть) використовуєте 8 ядер або 1 ядро.

ОНОВЛЕННЯ: Прочитавши трохи більше коментарів, схоже, що багатоядерне поведінка, яке ви бачите, пов'язане з розподілом анаконди інтерпретатора python. Я подивився, але не зміг знайти жодного вихідного коду для нього, але, схоже, ліцензія python дозволяє суб'єктам (наприклад, anaconda.com) збирати та поширювати похідні інтерпретатора, не вимагаючи публікації їх змін.

Я здогадуюсь, що ви можете зв’язатися з анакондою - поведінку, яку ви бачите, буде важко зрозуміти, не знаючи, що / чи щось вони змінили в перекладачі ..

Також швидко зробіть перевірку часу настінного годинника з / без оптимізації, щоб побачити, чи дійсно це 8 разів швидше - навіть якщо у вас справді працюють усі 8 ядер замість 1, було б добре знати, чи результати насправді 8x швидше або якщо у використанні є спінкі, які все ще серіалізуються на одній мутексі.


1
Привіт, Ендрю Атренс. Але паралелізація не проводиться пітоном, це робиться за допомогою ананади, що є Intel MKL. І так, я відкрив питання про numpy, вони запропонували мені відкрити питання про анаконда, і я це зробив. Однак я не отримав жодної відповіді від анаконди протягом тижня. Тож, можливо, вони просто проігнорували мій звіт ...
— user15964

Проблема з анакондовою версією / випуском інтерпретатора python - їх версія використовує openmp, тоді як стандартний випуск python цього не робить.
— Андрій Атренс

Це проблема з анакондовою версією / випуском інтерпретатора python - їх версія посилається на / використовує openmp apis, тоді як стандартний інтерпретатор випуску python цього не робить. коли я кажу, що використовує, я буквально маю на увазі виклик функцій appi openmp "під капотом". Як і у випадку будь-якої неявної оптимізації, де ми не можемо побачити вихідний код, ми можемо повідомити про нього (як у вас є) і, якщо можливо, спробувати обійти його.
— Ендрю Атренс

Ще одна думка з цього приводу. Ви можете перекодувати свою програму, щоб явно використовувати багатопотокові бібліотеки python, а не покладатися на оптимізатор інтерпретатора, щоб зробити це за вас .. Я думаю про пулове потоків .. залежно від того, наскільки складна ваша програма, і якщо це не ваш перший перехід на потокове програмування, це може бути не надто складно. Для підтримки портативності використання, ймовірно, слід намагатися уникати чогось конкретного для анаконда або openmp - я залишаю це вам, оскільки у мене немає часу копатись у ньому ... :) У будь-якому разі найкраще покладаємо удачу та сподіваємось, що це допоможе знебарвити те, що ви бачите. :) :)
— Андрій Атренс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.