Bug aneh di Panda dan Numpy tentang multithreading

25

Sebagian besar fungsi Numpy akan mengaktifkan multithreading secara default.

misalnya, saya bekerja pada workstation intel cpu 8-core, jika saya menjalankan skrip

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

linux topakan menunjukkan 800% penggunaan CPU selama menjalankan seperti masukkan deskripsi gambar di sini Yang berarti numpy secara otomatis mendeteksi bahwa workstation saya memiliki 8 core, dan np.sqrtsecara otomatis menggunakan semua 8 core untuk mempercepat perhitungan.

Namun, saya menemukan bug aneh. Jika saya menjalankan skrip

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)

penggunaan cpu adalah 100% !!. Ini berarti bahwa jika Anda ditambah dua panda DataFrame sebelum menjalankan fungsi numpy, fitur multithreading otomatis numpy hilang tanpa peringatan! Ini sama sekali tidak masuk akal, mengapa perhitungan dataFrame Pandas memengaruhi pengaturan threading Numpy? Apakah ini bug? Bagaimana cara mengatasinya?masukkan deskripsi gambar di sini


PS:

Saya menggali lebih jauh menggunakan perfalat Linux .

menjalankan tampilan skrip pertama

masukkan deskripsi gambar di sini

Saat menjalankan skrip kedua menunjukkan

masukkan deskripsi gambar di sini

Jadi kedua skrip melibatkan libmkl_vml_avx2.so, sedangkan skrip pertama melibatkan tambahan libiomp5.soyang tampaknya terkait dengan openMP.

Dan karena vml berarti perpustakaan vektor vektor intel, jadi menurut vml doc saya kira setidaknya fungsi di bawah ini semuanya otomatis multithreaded

masukkan deskripsi gambar di sini

pengguna15964
sumber
Saya tidak yakin saya mengerti pertanyaan Anda. Bisakah Anda menguraikan?
AMC
@AMC Saya memperbarui posting saya, harap sekarang jelas
user15964
Saya pikir diperlukan lebih banyak informasi seperti np, panda, versi, CPU, tipe OS ... Saya tidak dapat mereproduksi di komputer saya. Itu tidak menggunakan banyak CPU di kedua kode.
hunzter
@hunzter OK, berikut adalah informasi: Ubuntu 16.04.5 LTS numpy 1.17.2 py37haad9e8e_0 panda 0.25.1 py37he6710b0_0 Intel (R) Xeon (R) CPU E5-1680 v4 @ 3.40GHz. PS. Saya menggunakan anaconda
user15964
1
Bisakah Anda memeriksa ini: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

Jawaban:

13

Panda menggunakan di numexprbawah tenda untuk menghitung beberapa operasi, dan numexprmenetapkan jumlah maksimal utas untuk vml ke 1, saat diimpor :

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

dan itu diimpor oleh panda saat df+dfdievaluasi dalam expressions.py :

from pandas.core.computation.check import _NUMEXPR_INSTALLED

if _NUMEXPR_INSTALLED:
   import numexpr as ne

Namun, distribusi Anaconda juga menggunakan vml-fungsi untuk fungsi-fungsi seperti sqrt, sin, cosdan sebagainya - dan sekali numexprmengatur jumlah maksimal vml-benang ke 1, numpy-fungsi tidak lagi digunakan paralelisasi.

Masalahnya dapat dengan mudah dilihat di gdb (menggunakan skrip lambat Anda):

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

yaitu kita bisa melihat, numexprmengatur jumlah utas ke 1. Yang kemudian digunakan ketika fungsi vml-sqrt disebut:

(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

Jadi kita bisa melihat implementasi nml menggunakan nml vdSqrtyang digunakan mkl_vml_serv_threader_d_1i_1ountuk memutuskan apakah perhitungan harus dilakukan secara paralel dan terlihat jumlah utas:

(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

register %raxmemiliki jumlah utas maksimal dan 1.

Sekarang kita bisa menggunakan numexpruntuk menambah jumlah vml-threads , yaitu:

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

Sekarang banyak core digunakan!

benar-benar
sumber
Terima kasih banyak! Akhirnya, jawaban yang bagus menjelaskan semuanya. Pada akhirnya, itu adalah di numexprbelakang layar.
user15964
Setuju .. penggalian bagus! Pertanyaan selanjutnya .. mengapa numexpr push thread dihitung menjadi 1? Mungkin karena masalah ketidakstabilan / aman-utas? Alih-alih mendorong hitungan mundur hingga 8, mungkin lebih aman untuk beralih ke versi NumPy yang aman dan stabil. Mungkin juga bagus untuk memeriksa variabel ini dengan NumPy terbaru dan terhebat jika ini sebenarnya tidak diperlukan lagi, maka secara teknis bug.
Andrew Atrens
@AndrewAtrens Anda dapat melihat di github.com/pydata/numexpr/issues/39 dan github.com/pydata/numexpr/issues/355
benar menyebalkan
2

Melihat numpy, sepertinya, di bawah tenda itu memiliki masalah on / off dengan multithreading, dan tergantung pada versi apa yang Anda gunakan, Anda mungkin akan mulai melihat crash ketika Anda bertemu ne.set_vml_num_threads () ..

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

Saya perlu mencari tahu bagaimana ini terpaku pada interpreter python, memberikan contoh kode Anda di mana tampaknya entah bagaimana memungkinkan beberapa panggilan yang tampaknya sinkron / dipesan ke np.sqrt () untuk melanjutkan secara paralel. Saya kira jika interpreter python selalu hanya mengembalikan referensi ke objek ketika muncul tumpukan, dan dalam contoh Anda hanya melempar referensi tersebut dan tidak menetapkan atau memanipulasi mereka dengan cara apa pun itu akan baik-baik saja. Tetapi jika iterasi loop berikutnya tergantung pada yang sebelumnya maka tampaknya kurang jelas bagaimana ini dapat diparalelkan dengan aman. Kegagalan diam-diam / hasil yang salah adalah hasil yang lebih buruk daripada crash.

Andrew Atrens
sumber
Hai, Andrew Atrens, Anda hampir sampai. Ini adalah masalah ne.set_vml_num_threads (). Terima kasih banyak atas waktu yang Anda dedikasikan untuk masalah saya.
user15964
Happy Trails :)
Andrew Atrens
0

Saya pikir premis awal Anda mungkin salah -

Anda menyatakan: Yang berarti numpy secara otomatis mendeteksi bahwa workstation saya memiliki 8 core, dan np.sqrt secara otomatis menggunakan semua 8 core untuk mempercepat perhitungan.

Satu fungsi np.sqrt () tidak dapat menebak bagaimana selanjutnya akan dipanggil atau kembali sebelum sebagian selesai. Ada mekanisme paralelisme dalam python, tetapi tidak ada yang otomatis.

Sekarang, setelah mengatakan itu, interpreter python mungkin dapat mengoptimalkan loop for untuk paralelisme, yang mungkin apa yang Anda lihat, tapi saya sangat curiga jika Anda melihat waktu jam dinding untuk menjalankan loop ini tidak akan ada berbeda terlepas jika Anda (tampaknya) menggunakan 8 core atau 1 core.

UPDATE: Setelah membaca sedikit lebih banyak komentar, sepertinya perilaku multi-core yang Anda lihat terkait dengan distribusi anaconda dari interpreter python. Saya melihat tetapi tidak dapat menemukan kode sumber untuk itu, tetapi tampaknya lisensi python mengizinkan entitas (seperti anaconda.com) untuk mengkompilasi dan mendistribusikan turunan dari juru bahasa tanpa mengharuskan perubahan mereka untuk dipublikasikan.

Saya kira Anda dapat menjangkau orang-orang anaconda - perilaku yang Anda lihat akan sulit untuk dipecahkan tanpa mengetahui apa / jika ada sesuatu yang telah mereka ubah pada penerjemah.

Juga lakukan pemeriksaan cepat dari waktu jam dinding dengan / tanpa optimasi untuk melihat apakah memang 8x lebih cepat - bahkan jika Anda benar-benar memiliki semua 8 core bekerja, bukan 1 akan lebih baik untuk mengetahui apakah hasilnya benar-benar 8x lebih cepat atau jika ada spinlocks yang digunakan yang masih bersambung pada satu mutex.

Andrew Atrens
sumber
1
Hai, Andrew Atrens. Tetapi paralelisasi tidak dilakukan oleh python, itu dilakukan oleh backend dari anaconda numpy yang merupakan intel MKL. Dan ya saya telah membuka masalah pada numpy, mereka menyarankan saya untuk membuka masalah pada anaconda, dan saya melakukannya. Namun, saya belum mendapat satu balasan dari anaconda selama seminggu. Jadi mungkin mereka mengabaikan laporan saya ...
user15964
Ini masalah dengan versi anaconda / rilis interpreter python - versi mereka menggunakan openmp sedangkan rilis python standar tidak.
Andrew Atrens
Ini adalah masalah dengan versi anaconda / rilis interpreter python - versi mereka menghubungkan ke / menggunakan openmp apis sedangkan interpreter rilis python standar tidak. ketika saya mengatakan menggunakan maksud saya memanggil fungsi api openmp 'di bawah tenda'. Seperti halnya optimasi implisit di mana kami tidak dapat melihat kode sumber, kami hanya dapat melaporkannya (seperti yang Anda miliki) dan jika mungkin berupaya untuk mengatasinya.
Andrew Atrens
Pemikiran lain mengenai hal ini .. Anda bisa mengkode ulang aplikasi Anda untuk secara eksplisit menggunakan pustaka multithreading python dan tidak bergantung pada pengoptimal penerjemah untuk melakukannya untuk Anda .. Saya memikirkan thread pool .. tergantung pada seberapa rumitnya aplikasi Anda, dan jika ini bukan yang pertama Anda lakukan di pemrograman berulir, ini mungkin tidak terlalu sulit .. Untuk mempertahankan portabilitas gunakan mungkin harus mencoba untuk menghindari sesuatu yang spesifik untuk anaconda atau openmp - Saya akan menyerahkan ini kepada Anda karena saya tidak punya waktu untuk menggali lebih dalam ... :) Bagaimanapun juga keberuntungan dan harap ini membantu menghilangkan kabut apa yang Anda lihat. :) :)
Andrew Atrens