Lỗi kỳ lạ ở Pandas và Numpy liên quan đến đa luồng


25

Hầu hết các chức năng của Numpy sẽ cho phép đa luồng theo mặc định.

ví dụ, tôi làm việc trên máy trạm cpu 8 lõi intel, nếu tôi chạy tập lệnh

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

linux topsẽ hiển thị mức sử dụng cpu 800% trong khi chạy như thế. nhập mô tả hình ảnh ở đây Điều đó có nghĩa là numpy tự động phát hiện ra rằng máy trạm của tôi có 8 lõi và np.sqrttự động sử dụng tất cả 8 lõi để tăng tốc tính toán.

Tuy nhiên, tôi tìm thấy một lỗi kỳ lạ. Nếu tôi chạy một kịch bản

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)

việc sử dụng cpu là 100% !! Điều đó có nghĩa là nếu bạn cộng với hai gấu trúc DataFrame trước khi chạy bất kỳ chức năng numpy nào, tính năng đa luồng tự động của numpy sẽ biến mất mà không có bất kỳ cảnh báo nào! Điều này là hoàn toàn không hợp lý, tại sao tính toán DataFrame của Pandas sẽ ảnh hưởng đến cài đặt luồng Numpy? Có phải là một lỗi? Làm thế nào để làm việc xung quanh này?nhập mô tả hình ảnh ở đây


Tái bút

Tôi đào thêm bằng cách sử dụng perfcông cụ Linux .

chạy chương trình kịch bản đầu tiên

nhập mô tả hình ảnh ở đây

Trong khi chạy tập lệnh thứ hai cho thấy

nhập mô tả hình ảnh ở đây

Vì vậy, cả hai tập lệnh đều liên quan libmkl_vml_avx2.so, trong khi tập lệnh đầu tiên liên quan đến phần bổ sung libiomp5.sodường như có liên quan đến openMP.

Và vì vml có nghĩa là thư viện toán học intel vector, vì vậy theo vml doc, tôi đoán ít nhất các hàm bên dưới đều tự động đa luồng

nhập mô tả hình ảnh ở đây


Tôi không chắc là tôi hiểu câu hỏi của bạn. Bạn có thể xây dựng?
AMC

@AMC Tôi đã cập nhật bài viết của mình, hy vọng nó đã rõ ràng
user15964

Tôi nghĩ rằng cần thêm thông tin như np, gấu trúc, phiên bản, CPU, loại hệ điều hành ... Tôi không thể sao chép trên máy của mình. Nó không sử dụng nhiều CPU trong cả hai mã.
hunzter

@hunzter OK, đây là thông tin: 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. Tái bút Tôi sử dụng anaconda
dùng15964

1
Bạn có thể vui lòng kiểm tra điều này: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

Câu trả lời:


13

Pandas sử dụng numexprdưới mui xe để tính toán một số thao tác và numexprđặt số lượng luồng tối đa cho vml thành 1, khi nó được nhập :

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

và nó được nhập bởi gấu trúc khi df+dfđược đánh giá trong biểu thức :

from pandas.core.computation.check import _NUMEXPR_INSTALLED

if _NUMEXPR_INSTALLED:
   import numexpr as ne

Tuy nhiên, phân phối Anaconda cũng sử dụng VML-chức năng cho các chức năng như sqrt, sin, cosvà vân vân - và một khi numexprthiết lập số lượng tối đa của VML-đề để 1, numpy chức năng không còn sử dụng song song.

Vấn đề có thể dễ dàng nhìn thấy trong gdb (sử dụng tập lệnh chậm của bạn):

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

tức là chúng ta có thể thấy, numexprđặt số lượng chủ đề thành 1. Cái này được sử dụng sau khi hàm vml-sqrt được gọi:

(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

Vì vậy, chúng ta có thể thấy numpy sử dụng triển khai vml vdSqrtsử dụng mkl_vml_serv_threader_d_1i_1ođể quyết định xem có nên thực hiện tính toán song song hay không và có vẻ như số lượng luồng:

(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

thanh ghi %raxcó số lượng chủ đề tối đa và nó là 1.

Bây giờ chúng ta có thể sử dụng numexprđể tăng số lượng chủ đề vml , tức là:

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

Bây giờ nhiều lõi được sử dụng!


Cảm ơn bạn rất nhiều! Cuối cùng, một câu trả lời tuyệt vời giải thích tất cả. Cuối cùng, đó là numexprhậu trường.
dùng15964

Đồng ý .. đào tốt! Câu hỏi tiếp theo mặc dù .. tại sao numexpr đẩy chủ đề đếm lên 1? Có thể do sự không ổn định / vấn đề an toàn chủ đề? Thay vì đẩy số đếm trở lại lên 8, có thể an toàn hơn khi chuyển sang phiên bản NumPy an toàn / ổn định của luồng. Có lẽ cũng tốt để kiểm tra biến này với NumPy mới nhất và lớn nhất trong trường hợp điều này thực sự không cần thiết nữa, do đó về mặt kỹ thuật là một lỗi.
Andrew Atren


2

Nhìn vào numpy, có vẻ như, dưới mui xe, nó có vấn đề bật / tắt với đa luồng, và tùy thuộc vào phiên bản bạn đang sử dụng, bạn có thể bắt đầu thấy sự cố khi bạn gặp phải ne.set_vml_num_threads () ..

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

Tôi cần phải suy nghĩ về cách điều này được dán vào trình thông dịch python, đưa ra ví dụ mã của bạn trong đó dường như bằng cách nào đó cho phép nhiều cuộc gọi được đặt hàng / đồng bộ rõ ràng đến np.sqrt () tiến hành song song. Tôi đoán nếu trình thông dịch python luôn chỉ trả về một tham chiếu đến một đối tượng khi nó bật ngăn xếp, và trong ví dụ của bạn chỉ là đưa ra các tham chiếu đó và không gán hoặc thao tác chúng theo bất kỳ cách nào nó sẽ ổn. Nhưng nếu các vòng lặp tiếp theo phụ thuộc vào các lần lặp trước thì có vẻ như không rõ ràng làm thế nào chúng có thể được song song hóa một cách an toàn. Thất bại thầm lặng / kết quả sai là kết quả tồi tệ hơn so với tai nạn.


Xin chào, Andrew Atrens, Bạn gần như ở đó. Đây là vấn đề của ne.set_vml_num_threads (). Cảm ơn bạn rất nhiều vì đã dành thời gian cho vấn đề của tôi.
dùng15964

Happy Trails :)
Andrew Atren

0

Tôi nghĩ rằng tiền đề ban đầu của bạn có thể không chính xác -

Bạn đã nói: Điều đó có nghĩa là numpy tự động phát hiện ra rằng máy trạm của tôi có 8 lõi và np.sqrt tự động sử dụng tất cả 8 lõi để tăng tốc tính toán.

Một hàm duy nhất np.sqrt () không thể đoán nó sẽ được gọi hoặc trả về như thế nào trước khi nó hoàn thành một phần. Có các cơ chế song song trong python, nhưng không có cơ chế nào là tự động.

Bây giờ, đã nói rằng, trình thông dịch python có thể tối ưu hóa vòng lặp for để song song, đó có thể là những gì bạn đang thấy, nhưng tôi nghi ngờ nếu bạn nhìn vào thời gian đồng hồ treo tường để vòng lặp này thực thi thì sẽ không khác nhau bất kể bạn (rõ ràng) sử dụng 8 lõi hay 1 lõi.

CẬP NHẬT: Đã đọc thêm một chút các bình luận, có vẻ như hành vi đa lõi mà bạn đang thấy có liên quan đến phân phối anaconda của trình thông dịch python. Tôi đã xem nhưng không thể tìm thấy bất kỳ mã nguồn nào cho nó, nhưng có vẻ như giấy phép python cho phép các thực thể (như anaconda.com) biên dịch và phân phối các dẫn xuất của trình thông dịch mà không yêu cầu thay đổi của chúng được công bố.

Tôi đoán rằng bạn có thể tiếp cận với những người anaconda - hành vi bạn đang thấy sẽ khó hình dung mà không biết điều gì / nếu có bất cứ điều gì họ đã thay đổi trong trình thông dịch ..

Ngoài ra, hãy kiểm tra nhanh thời gian đồng hồ treo tường có / không có tối ưu hóa để xem liệu nó có thực sự nhanh hơn 8 lần hay không - ngay cả khi bạn thực sự có tất cả 8 lõi hoạt động thay vì 1 thì sẽ rất tốt nếu biết kết quả thực sự là 8 nhanh hơn hoặc nếu có spinlocks đang sử dụng vẫn đang tuần tự hóa trên một mutex duy nhất.


1
Xin chào, Andrew Atren. Nhưng việc song song không được thực hiện bởi python, nó được thực hiện bởi phần phụ trợ của anaconda numpy là intel MKL. Và vâng, tôi đã mở một vấn đề về numpy, họ đề nghị tôi mở một vấn đề về anaconda, và tôi đã làm điều đó. Tuy nhiên, tôi đã không nhận được một câu trả lời từ anaconda trong một tuần. Vì vậy, có lẽ họ chỉ bỏ qua báo cáo của tôi ...
user15964

Đó là một vấn đề với phiên bản anaconda / phát hành trình thông dịch python - phiên bản của họ sử dụng openmp trong khi phiên bản python tiêu chuẩn thì không.
Andrew Atren

Đó là một vấn đề với phiên bản anaconda / phát hành trình thông dịch python - phiên bản của chúng liên kết đến / sử dụng openmp apis trong khi trình thông dịch phát hành python tiêu chuẩn thì không. khi tôi nói sử dụng theo nghĩa đen tôi có nghĩa là gọi các hàm api openmp 'dưới mui xe'. Như với bất kỳ tối ưu hóa ngầm nào mà chúng tôi không thể thấy mã nguồn, chúng tôi chỉ có thể báo cáo nó (như bạn có) và nếu có thể hãy cố gắng khắc phục nó.
Andrew Atren

Một suy nghĩ khác về điều này .. bạn có thể mã hóa lại ứng dụng của mình để sử dụng rõ ràng các thư viện đa luồng python và không dựa vào trình tối ưu hóa trình thông dịch để làm điều đó cho bạn .. Tôi đang nghĩ về các nhóm luồng .. tùy thuộc vào mức độ phức tạp của ứng dụng của bạn, và nếu đây không phải là lần đầu tiên bạn lập trình luồng, điều này có thể không quá khó khăn .. Để duy trì việc sử dụng tính di động có lẽ nên cố gắng tránh bất cứ điều gì cụ thể đối với anaconda hoặc openmp - Tôi sẽ để lại cho bạn vì tôi không có thời gian để tìm hiểu sâu về nó ... :) Dù sao thì cũng may mắn nhất và hy vọng điều này sẽ giúp loại bỏ sương mù những gì bạn đang thấy. :) :)
Andrew Atren
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.