Pandalar ve Numpy'de çoklu iş parçacığıyla ilgili garip hata


25

Numpy işlevinin çoğu varsayılan olarak çoklu okuma özelliğini etkinleştirir.

örneğin, bir komut dosyası çalıştırırsam, 8 çekirdekli bir intel cpu iş istasyonunda çalışıyorum

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

linux topgibi çalışan sırasında% 800 CPU kullanımını gösterecektir resim açıklamasını buraya girin benim iş istasyonu 8 çekirdeğe sahip olduğunu otomatik olarak algılar hangi araçlar numpy ve np.sqrthesaplama hızlandırmak için 8 çekirdek kullanmaya otomatik.

Ancak, garip bir hata buldum. Senaryo çalıştırırsam

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 kullanımı% 100 !!. Herhangi bir numpy işlevini çalıştırmadan önce iki panda DataFrame eklerseniz, numpy'nin otomatik çoklu okuma özelliği herhangi bir uyarı yapılmadan gitti demektir! Bu kesinlikle mantıklı değil, Panda'nın dataFrame hesaplaması neden Numpy diş açma ayarını etkiler? Bu bir hata mı? Bu sorunu nasıl çözebilirim?resim açıklamasını buraya girin


Not:

Linux perfaracını kullanarak daha fazla kazıyorum .

ilk senaryo gösterilerini çalıştırma

resim açıklamasını buraya girin

İkinci senaryo şovlarını çalıştırırken

resim açıklamasını buraya girin

Bu yüzden her iki komut dosyası da dahil libmkl_vml_avx2.soedilirken, ilk komut dosyası libiomp5.soopenMP ile ilişkili gibi görünen ek içerir .

Ve vml intel vektör matematik kütüphanesi anlamına geldiğinden, vml doc'a göre en azından aşağıdaki fonksiyonların hepsi otomatik olarak çok iş parçacıklı

resim açıklamasını buraya girin


Sorunu anladığımdan emin değilim. Detaylandırabilir misin?
AMC

@AMC Yazımı güncelledim, şimdi açık olduğunu umuyorum
user15964

Sanırım np, pandalar, versiyon, CPU, OS tipi gibi daha fazla bilgiye ihtiyaç var ... Makinemde çoğaltamıyorum. Her iki kodda birden fazla CPU kullanılmaz.
hunzter

@hunzter Tamam, işte bilgiler: Ubuntu 16.04.5 LTS numpy 1.17.2 py37haad9e8e_0 pandalar 0.25.1 py37he6710b0_0 Intel (R) Xeon (R) CPU E5-1680 v4 @ 3.40GHz. PS.
Anaconda

1
Lütfen bunu kontrol edebilir misiniz: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

Yanıtlar:


13

Pandalar numexprbazı işlemleri hesaplamak için başlık altında kullanır ve içe aktarıldığındanumexpr vml için maksimum iş parçacığı sayısını 1 olarak ayarlar :

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

ve ne zaman pandalar tarafından ithal alır df+dfiçinde değerlendirilir expressions.py :

from pandas.core.computation.check import _NUMEXPR_INSTALLED

if _NUMEXPR_INSTALLED:
   import numexpr as ne

Bununla birlikte, Anaconda dağılımı gibi fonksiyonlar için vml-özelliğini kullanan sqrt, sin, cosve böylece - ve bir kez numexpr1 vml ipliklerin maksimum sayısını ayarlamak, numpy-fonksiyonları artık kullanımı paralelleştirme.

Sorun gdb'de kolayca görülebilir (yavaş komut dosyanızı kullanarak):

>>> 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

yani görebiliyoruz, numexpriş parçacığı sayısını 1 olarak ayarlar. Daha sonra vml-sqrt işlevi çağrıldığında kullanılır:

(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

Biz numpy kullandýnýz VML'de uygulanması konusunda görebilirsiniz Yani bir vdSqrthangi kullanan mkl_vml_serv_threader_d_1i_1ohesaplama paralel olarak yapılmalıdır karar vermek ve onu parçacığı sayısını arar:

(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

kayıt %raxmaksimum sayıda iş parçacığına sahiptir ve 1'dir.

Şimdi kullanabilirsiniz numexpretmek vml-parçacığı sayısını artırmak , yani:

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

Şimdi birden fazla çekirdek kullanılıyor!


Çok teşekkür ederim! Sonunda harika bir cevap her şeyi açıklıyor. Sonunda, numexprsahne arkası.
user15964

Kabul etti ... iyi kazma! Sonraki soru .. numexpr itme iş parçacığı neden 1 sayılır? Muhtemelen dengesizlik / iplik güvenliği sorunları nedeniyle? Sayıyı 8'e kadar geri itmek yerine, NumPy'nin iş parçacığı için güvenli / kararlı bir versiyonuna geçmek daha güvenli olabilir. Belki de artık gerekli olmadığı için bu değişkeni en son ve en büyük NumPy ile kontrol etmek iyi, dolayısıyla teknik olarak bir hata.
Andrew Atrens


2

Numpy'ye bakıldığında, kaputun altında çoklu iş parçacığıyla açma / kapama sorunları vardı ve kullandığınız sürüme bağlı olarak ne.set_vml_num_threads () çarptığınızda çökmeleri görmeye başlayabilirsiniz.

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

Nasıl bir şekilde paralel devam etmek için np.sqrt () birden çok görünüşte senkron / sipariş çağrıları izin gibi görünüyor kod örneğiniz göz önüne alındığında, bu python yorumlayıcı yapıştırılmış etrafında kafamı almak gerekir. Sanırım python yorumlayıcı her zaman sadece bir nesne yığını yığarken bir referans döndürüyor ve örnekte sadece bu referansları yunuslama ve onları herhangi bir şekilde atama veya manipüle etmiyor. Ancak, sonraki döngü yinelemeleri öncekilere bağlıysa, bunların nasıl güvenli bir şekilde paralelleştirilebileceği daha az net görünmektedir. Muhtemelen sessiz başarısızlık / yanlış sonuçlar, çökmelerden daha kötü bir sonuçtur.


Merhaba Andrew Atrens, Neredeyse bitti. Bu ne.set_vml_num_threads () sorunudur. Sorunumla ilgili ayırdığınız zaman için çok teşekkür ederim.
user15964

Happy Trails :)
Andrew Atrens

0

Bence ilk öncülünüz yanlış olabilir -

Şunu söylediniz: Bu, numpy'nin iş istasyonumun 8 çekirdeğe sahip olduğunu otomatik olarak algıladığı ve np.sqrt'in hesaplamayı hızlandırmak için otomatik olarak 8 çekirdeğin tümünü kullandığını gösterir.

Tek bir işlev np.sqrt (), kısmen tamamlanmadan önce nasıl çağrılacağını veya geri döneceğini tahmin edemez. Python'da paralellik mekanizmaları vardır, ancak hiçbiri otomatik değildir.

Şimdi, bunu söyledikten sonra, python yorumlayıcısı, for döngüsünü paralellik için optimize edebilir, bu gördüğünüz şey olabilir, ancak bu döngünün yürütmesi için duvar saati zamanına bakarsanız, hayır 8 çekirdekli veya 1 çekirdekli (görünüşe göre) farklı olmanıza rağmen.

GÜNCELLEME: Yorumlardan biraz daha fazlasını okuduğunuzda, gördüğünüz çok çekirdekli davranış, python yorumlayıcısının anaconda dağılımı ile ilişkili gibi görünüyor. Bir göz attım, ancak bunun için herhangi bir kaynak kodu bulamadım, ancak python lisansının (anaconda.com gibi) varlıkların değişikliklerinin yayınlanmasını gerektirmeden yorumlayıcı türevlerini derlemesine ve dağıtmasına izin verdiği görülüyor.

Sanırım anakonda milletlerine ulaşabilirsiniz - gördüğünüz davranışı tercümanda neyi değiştirip değiştirmediklerini bilmeden anlamak zor olacaktır.

Ayrıca, gerçekten 8 kat daha hızlı olup olmadığını görmek için optimizasyon ile / olmadan duvar saati süresini hızlı bir şekilde kontrol edin - 1 yerine 8 çekirdeğin tamamını çalıştırsanız bile, sonuçların gerçekten 8x olup olmadığını bilmek iyi olacaktır daha hızlı veya hala tek bir muteks üzerinde serileştiren spinlocks varsa.


1
Merhaba Andrew Atrens. Ancak paralelleştirme python tarafından yapılmaz, intel MKL olan anaconda numpy'nin arka ucu tarafından yapılır. Ve evet numpy ile ilgili bir konu açtım, anakonda ile ilgili bir konu açmamı önerdiler ve yaptım. Ancak, anaconda'dan henüz bir hafta boyunca tek bir cevap almadım. Belki de raporumu görmezden
geldiler

Bu, python yorumlayıcının anaconda sürümü / sürümü ile ilgili bir sorundur - sürümleri openmp kullanır, ancak standart python sürümü kullanmaz.
Andrew Atrens

Bu, python yorumlayıcısının anaconda sürümü / sürümü ile ilgili bir sorundur - sürümleri openmp apis'e bağlanır / kullanır, oysa standart python sürüm yorumlayıcısı değildir. yararları dediğimde tam anlamıyla 'kaputun altında' openmp api işlevlerini çağırmak demek. Kaynak kodunu göremediğimiz herhangi bir örtülü optimizasyonda olduğu gibi, sadece (sizin gibi) raporlayabiliriz ve mümkünse bu sorunu çözmeye çalışabiliriz.
Andrew Atrens

Bu konuda başka bir düşünce .. Açıkça python multithreading kütüphaneleri kullanmak ve sizin için yapmak için tercüman optimizer güvenmek değil uygulamanızı yeniden kodlayabilirsiniz .. Ben iş parçacığı havuzları düşünüyorum .. Uygulamanızın ne kadar karmaşık bağlı olarak, ve bu dişli programlamaya ilk gitmeniz değilse, bu çok zor olmayabilir .. Taşınabilirliği korumak için muhtemelen anaconda veya openmp'a özgü herhangi bir şeyden kaçınmaya çalışmalısınız - Zamanım olmadığından bunu size bırakacağım içine kazmak için ... :) Her neyse, iyi şanslar ve umarım bu gördüğün sis buğu gidermek yardımcı olur. :) :)
Andrew Atrens
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.