ข้อผิดพลาดแปลก ๆ ใน Pandas และ Numpy เกี่ยวกับมัลติเธรด


25

ฟังก์ชั่นส่วนใหญ่ของ Numpy จะเปิดใช้งานมัลติเธรดโดยค่าเริ่มต้น

ตัวอย่างเช่นฉันทำงานบนเวิร์กสเตชัน intel 8-core intel ถ้าฉันเรียกใช้สคริปต์

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

linux topจะแสดงการใช้งาน cpu 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)

การใช้ cpu 100% !! หมายความว่าถ้าคุณบวกแพนด้าสองตัว DataFrame ก่อนที่จะเรียกใช้ฟังก์ชัน numpy ฟีเจอร์มัลติเธรดอัตโนมัติของ numpy จะหายไปโดยไม่มีการเตือน! สิ่งนี้ไม่สมเหตุสมผลเลยเหตุใดการคำนวณ Pandas dataFrame จึงส่งผลต่อการตั้งค่าเธรดของ Numpy มันเป็นข้อบกพร่องหรือไม่? วิธีแก้ไขปัญหานี้?ป้อนคำอธิบายรูปภาพที่นี่


PS:

ฉันขุดเพิ่มเติมโดยใช้perfเครื่องมือLinux

เรียกใช้สคริปต์แรกที่แสดง

ป้อนคำอธิบายรูปภาพที่นี่

ในขณะที่เรียกใช้สคริปต์ที่สองแสดงให้เห็น

ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้นสคริปต์ทั้งสองเกี่ยวข้องกับlibmkl_vml_avx2.soในขณะที่สคริปต์แรกเกี่ยวข้องกับเพิ่มเติมlibiomp5.soซึ่งดูเหมือนว่าจะเกี่ยวข้องกับ openMP

และเนื่องจาก vml หมายถึงห้องสมุดคณิตศาสตร์เวกเตอร์ของ Intel ดังนั้นตาม vml doc ฉันเดาว่าอย่างน้อยฟังก์ชั่นด้านล่างเป็นแบบมัลติเธรดโดยอัตโนมัติ

ป้อนคำอธิบายรูปภาพที่นี่


ฉันไม่แน่ใจว่าฉันเข้าใจคำถามของคุณ คุณสามารถทำอย่างละเอียด?
AMC

@AMC ฉันอัปเดตโพสต์แล้วหวังว่าตอนนี้จะชัดเจน
user15964

ฉันคิดว่าต้องการข้อมูลเพิ่มเติมเช่น np, pandas, version, CPU, OS type ... ฉันไม่สามารถทำซ้ำบนเครื่องของฉันได้ มันไม่ได้ใช้ CPU หลายตัวในรหัสทั้งสอง
hunzter

@hunzter ตกลงนี่คือข้อมูล: Ubuntu 16.04.5 LTS จำนวน 1.17.2 pand37haad9e8e_0 pandas 0.25.1 py37he6710b0_0 Intel (R) Xeon (R) CPU E5-1680 v4 @ 3.40GHz PS ฉันใช้อนาคอนดา
15964

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ภายใต้ประทุนเพื่อคำนวณการดำเนินการบางอย่างและ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-threadsเช่น:

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เบื้องหลัง
user15964

ตกลง .. ขุดดี! คำถามต่อไป .. ทำไม numexpr push thread นับเป็น 1 อาจเป็นเพราะปัญหาความไม่มั่นคง / ปลอดภัยต่อเธรด? แทนที่จะผลักดันการนับกลับไปสูงสุด 8 อาจปลอดภัยกว่าที่จะไปยังรุ่น NumPy ที่ปลอดภัย / เสถียร อาจดีในการตรวจสอบตัวแปรนี้ด้วย NumPy ล่าสุดและยิ่งใหญ่ที่สุดในกรณีที่ไม่จำเป็นต้องใช้อีกต่อไปดังนั้นจึงเป็นข้อบกพร่องทางเทคนิค
Andrew Atrens

@AndrewAtrens คุณสามารถดูที่github.com/pydata/numexpr/issues/39และgithub.com/pydata/numexpr/issues/355
อี๊ด

2

ดูเป็นจำนวนมากดูเหมือนว่าภายใต้ประทุนที่มีปัญหาเกี่ยวกับการเปิด / ปิดกับ multithreading และขึ้นอยู่กับรุ่นที่คุณใช้คุณอาจคาดว่าจะเริ่มเห็นปัญหาเมื่อคุณชน ne.set_vml_num_threads () ..

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

ฉันต้องเข้าใจก่อนว่าสิ่งนี้ติดอยู่กับตัวแปลภาษาไพ ธ อนหรือไม่เนื่องจากมีตัวอย่างโค้ดของคุณซึ่งดูเหมือนว่าจะช่วยให้การเรียกแบบซิงโครนัส / สั่งซื้อหลายครั้งพร้อมกันไปยัง np.sqrt () ฉันเดาว่าล่ามไพ ธ อนมักจะส่งคืนการอ้างอิงไปยังวัตถุเมื่อมันปรากฏสแต็กและในตัวอย่างของคุณเป็นเพียงการทอยการอ้างอิงเหล่านั้นและไม่กำหนดหรือจัดการมันในทางใดทางหนึ่งมันจะดี แต่ถ้าการวนซ้ำครั้งต่อ ๆ ไปขึ้นอยู่กับการวนซ้ำก่อนหน้านี้ดูเหมือนจะชัดเจนน้อยกว่าว่าการขนานเหล่านี้จะปลอดภัยได้อย่างไร ความล้มเหลวเงียบ / ผลลัพธ์ที่ไม่ถูกต้องนั้นเป็นผลลัพธ์ที่เลวร้ายยิ่งกว่าการขัดข้อง


สวัสดี Andrew Atrens คุณเกือบจะอยู่ที่นั่นแล้ว มันเป็นปัญหาของ ne.set_vml_num_threads () ขอบคุณมากสำหรับช่วงเวลาที่ทุ่มเทในเรื่องของฉัน
user15964

Happy Trails :)
Andrew Atrens

0

ฉันคิดว่าหลักฐานเริ่มต้นของคุณอาจไม่ถูกต้อง -

คุณระบุ: ซึ่งหมายความว่ามีการตรวจพบจำนวน numpy โดยอัตโนมัติว่าเวิร์กสเตชันของฉันมี 8 คอร์และ np.sqrt ใช้ทั้ง 8 คอร์โดยอัตโนมัติเพื่อเร่งการคำนวณ

ฟังก์ชั่นเดียว np.sqrt () ไม่สามารถคาดเดาได้ว่าจะเรียกใช้หรือส่งคืนต่อไปอย่างไรก่อนที่จะเสร็จสมบูรณ์บางส่วน มีกลไกการขนานในไพ ธ อน แต่ก็ไม่เป็นไปโดยอัตโนมัติ

ตอนนี้ต้องบอกว่าล่ามไพ ธ อนอาจจะปรับห่วงสำหรับการขนานซึ่งอาจเป็นสิ่งที่คุณเห็น แต่ฉันสงสัยอย่างยิ่งถ้าคุณดูเวลานาฬิกาแขวนสำหรับลูปนี้เพื่อรันมันจะไม่ แตกต่างกันไม่ว่าคุณจะใช้ 8 คอร์หรือ 1 คอร์ก็ตาม

UPDATE: เมื่ออ่านความคิดเห็นเพิ่มเติมดูเหมือนว่าพฤติกรรมแบบมัลติคอร์ที่คุณเห็นเกี่ยวข้องกับการแจกจ่ายอนาคอนดาของล่ามไพ ธ อน ฉันดู แต่ไม่สามารถหาซอร์สโค้ดของมันได้ แต่ดูเหมือนว่าใบอนุญาตหลามอนุญาตให้หน่วยงาน (เช่น anaconda.com) รวบรวมและแจกจ่ายสัญญาซื้อขายล่วงหน้าของล่ามโดยไม่ต้องมีการเผยแพร่การเปลี่ยนแปลง

ฉันเดาว่าคุณสามารถติดต่อกับอนาคอนด้าได้ - พฤติกรรมที่คุณเห็นจะเป็นเรื่องยากที่จะคิดออกโดยไม่ทราบว่ามีการเปลี่ยนแปลงอะไรในล่าม ..

ตรวจสอบเวลาของนาฬิกาแขวนด้วยอย่างรวดเร็วด้วย / โดยไม่ต้องปรับให้เหมาะสมเพื่อดูว่ามันเร็วกว่า 8x จริงหรือไม่แม้ว่าคุณจะได้แกนทั้ง 8 แกนทำงานจริงแทนที่จะเป็น 1 มันก็เป็นการดีที่จะรู้ว่าผลลัพธ์เป็น 8x จริงหรือไม่ เร็วขึ้นหรือหากมีการใช้งาน spinlocks ซึ่งยังคงเป็นอนุกรมใน mutex เดียว


1
สวัสดี Andrew Atrens แต่การทำให้ขนานนั้นไม่ได้กระทำโดยไพ ธ อนมันทำโดยแบ็กเอนด์ของอนาคอนด้าซึ่งเป็น intel MKL และใช่ฉันได้เปิดปัญหากับ numpy พวกเขาแนะนำให้ฉันเปิด anaconda ปัญหาและฉันทำมัน อย่างไรก็ตามฉันไม่ได้รับคำตอบเดียวจากอนาคอนดามาเป็นเวลาหนึ่งสัปดาห์ ดังนั้นพวกเขาอาจเพิกเฉยต่อรายงานของฉัน ...
user15964

มันเป็นปัญหาของตัวแปลภาษางูใหญ่ / รุ่นของงูหลาม - รุ่นของพวกเขาใช้ openmp ในขณะที่งูเหลือมรุ่นมาตรฐานไม่ได้
Andrew Atrens

มันเป็นปัญหากับ anaconda version / release ของ python interpreter - เวอร์ชันของมันเชื่อมโยงไปยัง / ใช้ openmp apis ในขณะที่ interpreter python แบบมาตรฐานนั้นไม่มีอยู่ เมื่อฉันพูดใช้ฉันหมายถึงการเรียกฟังก์ชั่น openmp api 'ภายใต้ประทุน' เช่นเดียวกับการเพิ่มประสิทธิภาพโดยนัยที่เราไม่สามารถมองเห็นซอร์สโค้ดเราสามารถรายงานได้ (เท่าที่คุณมี) และหากเป็นไปได้ที่จะพยายามแก้ไขมัน
Andrew Atrens

ความคิดอื่นเกี่ยวกับเรื่องนี้ .. คุณสามารถเขียนโค้ดแอปพลิเคชันของคุณใหม่เพื่อใช้ไพ ธ อนมัลติไพ ธ อลไลบรารี่อย่างชัดเจนและไม่ต้องพึ่งพาเครื่องมือเพิ่มประสิทธิภาพของล่ามเพื่อทำเพื่อคุณ .. ฉันคิดถึงเธรดพูล .. และถ้านี่ไม่ใช่ครั้งแรกของคุณในการเขียนโปรแกรมแบบเธรดนี่อาจไม่ใช่เรื่องยากเกินไป .. ในการรักษาการใช้งานพอร์ตแบบพกพาน่าจะพยายามหลีกเลี่ยงสิ่งใด ๆ ที่เฉพาะเจาะจงกับแอนาคอนดาหรือ openmp - ฉันจะทิ้งเรื่องนี้ไว้กับคุณ เพื่อขุดลงไป ... :) ขอให้โชคดีและหวังว่านี่จะช่วยขจัดสิ่งที่คุณเห็น :) :)
แอนดรู At
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.