Дивна помилка в 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.