멀티 스레딩에 관한 판다와 너 피의 이상한 버그


25

Numpy의 기능 대부분은 기본적으로 멀티 스레딩을 활성화합니다.

예를 들어 스크립트를 실행하면 8 코어 인텔 CPU 워크 스테이션에서 작업합니다.

import numpy as np    
x=np.random.random(1000000)
for i in range(100000):
    np.sqrt(x)

리눅스 top는 실행 중 800 % CPU 사용량을 보여줍니다. 여기에 이미지 설명을 입력하십시오 즉, 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)

CPU 사용량은 100 %입니다. 그것은 numpy 함수를 실행하기 전에 두 개의 pandas DataFrame을 더하면 numpy의 자동 멀티 스레딩 기능은 경고없이 사라집니다! 이것은 절대적으로 합리적이지 않습니다. 왜 Pandas dataFrame 계산이 Numpy 스레딩 설정에 영향을 미칩니 까? 버그입니까? 이 문제를 해결하는 방법?여기에 이미지 설명을 입력하십시오


추신:

나는 리눅스 perf도구를 사용하여 더 파다 .

첫 스크립트 실행

여기에 이미지 설명을 입력하십시오

두 번째 스크립트 쇼를 실행하는 동안

여기에 이미지 설명을 입력하십시오

따라서 두 스크립트는 모두 포함 libmkl_vml_avx2.so하고 첫 번째 스크립트는 libiomp5.soopenMP와 관련된 추가 스크립트를 포함 합니다.

그리고 vml은 인텔 벡터 수학 라이브러리를 의미하므로 vml doc에 따르면 적어도 아래 함수는 모두 자동으로 멀티 스레드됩니다

여기에 이미지 설명을 입력하십시오


귀하의 질문을 이해하지 못했습니다. 정교하게 할 수 있습니까?
AMC

@AMC 나는, 내 게시물을 업데이트 지금 분명 희망
user15964

나는 np, 팬더, 버전, CPU, OS 유형과 같은 더 많은 정보가 필요하다고 생각합니다 ... 내 컴퓨터에서 재생할 수 없습니다. 두 코드 모두에서 여러 CPU를 사용하지 않습니다.
hunzter

@hunzter OK, 여기 정보가 있습니다 : 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. 추신. 나는 아나콘다 사용
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)
Stas Buzuluk

답변:


13

Pandas는 numexpr후드 아래를 사용 하여 일부 작업을 계산하고 numexprvml을 가져올 때 최대 스레드 수를 1로 설정합니다 .

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

그리고 expressions.pydf+df 에서 평가 될 때 팬더가 가져옵니다 .

from pandas.core.computation.check import _NUMEXPR_INSTALLED

if _NUMEXPR_INSTALLED:
   import numexpr as ne

그러나, 아나콘다 분포는 또한 같은 기능 VML-기능을 사용 sqrt, sin, cos등 - 한 번 numexpr1 VML - 스레드의 최대 수를 설정하십시오 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입니다.

이제 vml-threads 수numexpr늘리는 데 사용할 수 있습니다 .

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

동의 .. 좋은 파기! 다음 질문은 .. 왜 numexpr이 스레드를 1로 카운트합니까? 아마도 불안정성 / 스레드 안전 문제로 인해? 카운트를 다시 8로 올리는 대신 스레드 안전 / 안정 버전의 NumPy로 이동하는 것이 더 안전 할 수 있습니다. 어쩌면 이것이 실제로 더 이상 필요하지 않은 경우를 대비하여 최신 및 가장 큰 NumPy 로이 변수를 확인하는 것이 좋을 수도 있습니다. 기술적으로 버그입니다.
Andrew Atrens


2

numpy를 살펴보면 멀티 스레딩과 관련하여 온 / 오프 문제가있는 것처럼 보이며 사용중인 버전에 따라 ne.set_vml_num_threads ()를 충돌시킬 때 충돌이 발생할 수 있습니다.

http://numpy-discussion.10968.n7.nabble.com/ANN-NumExpr-2-7-0-Release-td47414.html

어떻게 든 np.sqrt ()에 대한 여러 개의 호출을 병렬로 진행하도록 허용하는 것처럼 보이는 코드 예제를 고려할 때 파이썬 인터프리터에 어떻게 붙어 있는지에 대해 알아야합니다. 파이썬 인터프리터가 스택에 팝 될 때 항상 객체에 대한 참조를 반환하는지, 그리고 예제에서 그러한 참조를 지정하고 어떤 식 으로든 할당하거나 조작하지 않는 것이 좋습니다. 그러나 후속 루프 반복이 이전 루프 반복에 의존하는 경우 이러한 루프 반복이 어떻게 안전하게 병렬화 될 수 있는지 명확하지 않은 것 같습니다. 틀림없이 조용한 실패 / 잘못된 결과는 충돌보다 더 나쁜 결과입니다.


앤드류 아 렌스, 거의 다 왔어요 ne.set_vml_num_threads ()의 문제입니다. 내 문제에 헌신적 인 시간을 보내 주셔서 대단히 감사합니다.
user15964

Happy Trails :)
Andrew Atrens

0

귀하의 초기 전제가 잘못되었다고 생각합니다.

numpy는 내 워크 스테이션에 8 개의 코어가 있음을 자동으로 감지하고 np.sqrt는 자동으로 8 개의 코어를 모두 사용하여 계산을 가속화합니다.

단일 함수 np.sqrt ()는 부분적으로 완료되기 전에 다음에 호출되거나 리턴되는 방법을 추측 할 수 없습니다. 파이썬에는 병렬 메커니즘이 있지만 자동적 인 것은 없습니다.

자, 파이썬 인터프리터는 병렬 처리를 위해 for 루프를 최적화 할 수 있다고 말했을 것입니다. 8 개의 코어 또는 1 개의 코어를 사용하는지에 관계없이 다릅니다.

업데이트 : 주석을 조금 더 읽은 것은보고있는 멀티 코어 동작이 파이썬 인터프리터의 아나콘다 배포와 관련이있는 것처럼 보입니다. 나는 살펴 보았지만 소스 코드를 찾을 수는 없었지만 파이썬 라이센스는 anaconda.com과 같은 엔티티가 변경 사항을 게시하지 않고도 인터프리터의 파생물을 컴파일하고 배포 할 수있게합니다.

나는 당신이 아나콘다 사람들에게 연락 할 수 있다고 생각합니다-당신이보고있는 행동은 그들이 통역사에서 무엇을 바꾸 었는지 알지 못하면 파악하기 어려울 것입니다 ..

또한 최적화가 있거나없는 벽 시계 시간을 신속하게 확인하여 실제로 8 배 더 빠르지 않은지 확인하십시오. 실제로 8 개의 코어가 모두 1 대 대신 작동하더라도 결과가 실제로 8 배인지 아는 것이 좋습니다 더 빠르거나 단일 뮤텍스에서 직렬화되는 스핀 락이있는 경우.


1
Andrew Atrens 안녕하세요. 그러나 병렬 처리는 파이썬에 의해 수행되지 않으며 인텔 MKL 인 아나콘다 numpy의 백엔드에 의해 수행됩니다. 그리고 네, numpy에 관한 문제를 열었습니다. 그들은 아나콘다에 관한 문제를 열도록 제안했습니다. 그러나 나는 일주일 동안 아나콘다로부터 단일 답변을 얻지 못했습니다. 아마 그들은 내 보고서를 무시했을 것입니다.
user15964

파이썬 인터프리터의 아나콘다 버전 / 릴리스의 문제입니다. 버전은 openmp를 사용하지만 표준 파이썬 릴리스는 그렇지 않습니다.
Andrew Atrens

파이썬 인터프리터의 아나콘다 버전 / 릴리스의 문제입니다. 버전은 openmp api에 링크 / 활용하지만 표준 파이썬 릴리스 인터프리터는 그렇지 않습니다. 내가 말할 때 나는 문자 그대로 '언더 후드'에서 openmp api 함수를 호출하는 것을 의미합니다. 소스 코드를 볼 수없는 암시 적 최적화와 마찬가지로 소스 코드 만보고 할 수 있으며 가능한 경우 해결 방법을 시도 할 수 있습니다.
Andrew Atrens

이것에 대한 또 다른 생각 .. 당신은 파이썬 멀티 스레딩 라이브러리를 명시 적으로 사용하도록 응용 프로그램을 다시 코딩 할 수 있으며 인터프리터의 옵티 마이저에 의존하지 않아도 응용 프로그램의 복잡도에 따라 스레드 풀을 생각하고 있습니다. 그리고 이것이 처음으로 쓰레드 프로그래밍에 들어 가지 않는다면, 그렇게 어렵지는 않을 것입니다. 이식성을 유지하기 위해서는 아마도 아나콘다 나 openmp에 특정한 것을 피하려고 노력할 것입니다-시간이 없어서 이걸 남겨 두겠습니다 그것을 파헤 치기 위해 ... :) 어쨌든 행운을 빕니다. :) :)
Andrew Atrens
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.