2011-07-02 34 views
18

Tôi đang bối rối bởi điều nàyTại sao numpy.array quá chậm?

def main(): 
    for i in xrange(2560000): 
     a = [0.0, 0.0, 0.0] 

main() 

$ time python test.py 

real  0m0.793s 

Bây giờ xem với NumPy:

import numpy 

def main(): 
    for i in xrange(2560000): 
     a = numpy.array([0.0, 0.0, 0.0]) 

main() 

$ time python test.py 

real 0m39.338s 

chu kỳ CPU Thánh Batman!

Sử dụng numpy.zeros(3) cải thiện, nhưng vẫn không đủ IMHO

$ time python test.py 

real 0m5.610s 
user 0m5.449s 
sys 0m0.070s 

numpy.version.version = '1.5.1'

Nếu bạn đang tự hỏi nếu việc tạo danh sách được bỏ qua để tối ưu hóa trong lần đầu tiên ví dụ: không phải là:

5   19 LOAD_CONST    2 (0.0) 
      22 LOAD_CONST    2 (0.0) 
      25 LOAD_CONST    2 (0.0) 
      28 BUILD_LIST    3 
      31 STORE_FAST    1 (a) 
+2

Suy nghĩ nhanh: 'numpy.array' thực sự là cấu trúc dữ liệu phức tạp hơn danh sách. Và trong đoạn thứ hai, bạn tạo một danh sách ** và ** một mảng numpy (chỉ trong danh sách đầu tiên). Cho dù đây là lý do duy nhất cho sự khác biệt lớn như vậy, tôi không thể nói. –

+0

@Felix: ok, nhưng việc tạo danh sách rất nhanh, vì vậy ngay cả khi tôi tạo danh sách và mảng có nhiều mảng trong trường hợp thứ hai, nó vẫn là sự sáng tạo gọn gàng đó là điểm nóng ở đây và bất kể cấu trúc có thể phức tạp đến mức nào được, nó vẫn còn damn đắt tiền ... –

+3

Nhưng hãy xem xét: Tạo dữ liệu hiếm khi là nút cổ chai trong một ứng dụng phức tạp đến mức nó sử dụng cục bộ.Tôi không biết điều gì xảy ra dưới mui xe, nhưng rõ ràng là làm cho các chương trình toán học nặng hơn vào cuối ngày, vì vậy không có lý do gì để khiếu nại;) – delnan

Trả lời

34

Numpy được tối ưu hóa cho một lượng lớn dữ liệu. Cung cấp cho nó một mảng 3 chiều dài và, không ngạc nhiên, nó hoạt động kém.

Xem xét một thử nghiệm riêng biệt

import timeit 

reps = 100 

pythonTest = timeit.Timer('a = [0.] * 1000000') 
numpyTest = timeit.Timer('a = numpy.zeros(1000000)', setup='import numpy') 
uninitialised = timeit.Timer('a = numpy.empty(1000000)', setup='import numpy') 
# empty simply allocates the memory. Thus the initial contents of the array 
# is random noise 

print 'python list:', pythonTest.timeit(reps), 'seconds' 
print 'numpy array:', numpyTest.timeit(reps), 'seconds' 
print 'uninitialised array:', uninitialised.timeit(reps), 'seconds' 

Và đầu ra là

python list: 1.22042918205 seconds 
numpy array: 1.05412316322 seconds 
uninitialised array: 0.0016028881073 seconds 

Có vẻ như rằng đó là zeroing của mảng được tham gia tất cả thời gian cho numpy. Vì vậy, trừ khi bạn cần các mảng được khởi tạo sau đó thử sử dụng sản phẩm nào.

+1

Đối với sự công bằng, bạn nên đã thực hiện 'pythonTest = timeit.Timer ('a = [0.] * 1000000')', nó vẫn hoạt động chậm hơn so với numpy nhưng nó khá nhanh hơn LC. Và nó "gần hơn" với một danh sách theo nghĩa đen (như được đưa ra trong câu hỏi) ở chỗ nó không chạy một vòng lặp Python. –

+0

@ Rosh Điểm tốt. Tôi nghĩ tôi luôn tránh xa toán tử '*' cho các danh sách vì nó đặt cùng một đối tượng trong mỗi chỉ mục. Mặc dù các con số là không thay đổi mà không quan trọng trong trường hợp này. Mặc dù thử thực hiện một phép toán khối trên danh sách/mảng sau đó numpy là con đường ra trong dẫn một lần nữa (ví dụ: arr + = 1). – Dunes

+0

Điểm rất tốt, cảm ơn bạn. Xem xét kết quả, những gì bạn sẽ đề nghị cho mảng nhỏ? Ý tôi là, các danh sách và bộ dữ liệu không thực sự tốt đẹp khi nói đến các hoạt động mảng cơ bản (chẳng hạn như sản phẩm vectơ vector, phép nhân các mảng thời gian, vv), tất nhiên tôi có thể tự mình thực hiện lại các thuật toán, vấn đề lớn ở đây, nhưng nếu đã có một cái gì đó cho điều đó, tôi xem đó là giải pháp ưa thích. –

4

Holy CPU cycles batman!, thực sự.

Nhưng vui lòng xem xét điều gì đó rất cơ bản liên quan đến numpy; chức năng dựa trên đại số tuyến tính tinh vi (như random numbers hoặc singular value decomposition). Bây giờ, hãy xem xét các tính toán đơn giản này:

In []: A= rand(2560000, 3) 
In []: %timeit rand(2560000, 3) 
1 loops, best of 3: 296 ms per loop 
In []: %timeit u, s, v= svd(A, full_matrices= False) 
1 loops, best of 3: 571 ms per loop 

và hãy tin tưởng rằng loại hiệu suất này sẽ không bị đánh bại đáng kể bởi bất kỳ gói nào hiện có sẵn.

Vì vậy, hãy mô tả vấn đề thực sự của bạn và tôi sẽ cố gắng tìm ra giải pháp dựa trên phong nha numpy cho nó.

Cập nhật:
Dưới đây là một số cách đơn giản mã cho ngã tư cầu ray:

import numpy as np 

def mag(X): 
    # magnitude 
    return (X** 2).sum(0)** .5 

def closest(R, c): 
    # closest point on ray to center and its distance 
    P= np.dot(c.T, R)* R 
    return P, mag(P- c) 

def intersect(R, P, h, r): 
    # intersection of rays and sphere 
    return P- (h* (2* r- h))** .5* R 

# set up 
c, r= np.array([10, 10, 10])[:, None], 2. # center, radius 
n= 5e5 
R= np.random.rand(3, n) # some random rays in first octant 
R= R/ mag(R) # normalized to unit length 

# find rays which will intersect sphere 
P, b= closest(R, c) 
wi= b<= r 

# and for those which will, find the intersection 
X= intersect(R[:, wi], P[:, wi], r- b[wi], r) 

Rõ ràng chúng ta tính toán một cách chính xác:

In []: allclose(mag(X- c), r) 
Out[]: True 

Và một số timings:

In []: % timeit P, b= closest(R, c) 
10 loops, best of 3: 93.4 ms per loop 
In []: n/ 0.0934 
Out[]: 5353319 #=> more than 5 million detection's of possible intersections/ s 
In []: %timeit X= intersect(R[:, wi], P[:, wi], r- b[wi]) 
10 loops, best of 3: 32.7 ms per loop 
In []: X.shape[1]/ 0.0327 
Out[]: 874037 #=> almost 1 million actual intersections/ s 

Những thời gian được thực hiện với máy rất khiêm tốn. Với máy móc hiện đại, một tốc độ đáng kể có thể vẫn được mong đợi.

Dù sao, đây chỉ là một minh chứng ngắn về cách mã hóa với numpy.

+0

vấn đề thực sự của tôi: http://stackoverflow.com/questions/6528214/improving-performance-of-raytracing-hit-function –

+0

@Stefano Borini: Cập nhật câu trả lời của tôi. Cảm ơn – eat

+0

tốt. Tuy nhiên, nó không thực sự cho phép bạn xử lý trực tiếp đối tượng Sphere theo cách này. Bạn phải có một chương trình phụ trợ chuyển đổi thiết kế cấp cao thành tập hợp tọa độ tổng hợp mà sau đó được đưa vào vùng nhớ. –

2

Trả lời muộn nhưng có thể quan trọng đối với những người xem khác.

Vấn đề này cũng đã được xem xét trong dự án kwant. Thực tế các mảng nhỏ không được tối ưu hóa trong các mảng nhỏ và khá thường xuyên là chính xác những gì bạn cần. Về vấn đề này, họ tạo ra một thay thế cho các mảng nhỏ hoạt động và cùng tồn tại với các mảng cố định (bất kỳ hoạt động không được thực hiện nào trong kiểu dữ liệu mới được xử lý bởi numpy).

Bạn nên xem xét dự án này:
https://pypi.python.org/pypi/tinyarray/1.0.5
mà mục đích chính là để hành xử độc đáo cho mảng nhỏ. Tất nhiên một số trong những điều ưa thích hơn bạn có thể làm với numpy không được hỗ trợ bởi điều này. Nhưng số học dường như là yêu cầu của bạn.

Tôi đã thực hiện một số xét nghiệm nhỏ:

python

Tôi đã thêm nhập khẩu NumPy để có được thời gian tải đúng

import numpy 

def main(): 
    for i in xrange(2560000): 
     a = [0.0, 0.0, 0.0] 

main() 

NumPy

import numpy 

def main(): 
    for i in xrange(2560000): 
     a = numpy.array([0.0, 0.0, 0.0]) 

main() 

NumPy-zero

import numpy 

def main(): 
    for i in xrange(2560000): 
     a = numpy.zeros((3,1)) 

main() 

tinyarray

import numpy,tinyarray 

def main(): 
    for i in xrange(2560000): 
     a = tinyarray.array([0.0, 0.0, 0.0]) 

main() 

tinyarray-zero

import numpy,tinyarray 

def main(): 
    for i in xrange(2560000): 
     a = tinyarray.zeros((3,1)) 

main() 

Tôi chạy này:

for f in python numpy numpy_zero tiny tiny_zero ; do 
    echo $f 
    for i in `seq 5` ; do 
     time python ${f}_test.py 
    done 
done 

Và có:

python 
python ${f}_test.py 0.31s user 0.02s system 99% cpu 0.339 total 
python ${f}_test.py 0.29s user 0.03s system 98% cpu 0.328 total 
python ${f}_test.py 0.33s user 0.01s system 98% cpu 0.345 total 
python ${f}_test.py 0.31s user 0.01s system 98% cpu 0.325 total 
python ${f}_test.py 0.32s user 0.00s system 98% cpu 0.326 total 
numpy 
python ${f}_test.py 2.79s user 0.01s system 99% cpu 2.812 total 
python ${f}_test.py 2.80s user 0.02s system 99% cpu 2.832 total 
python ${f}_test.py 3.01s user 0.02s system 99% cpu 3.033 total 
python ${f}_test.py 2.99s user 0.01s system 99% cpu 3.012 total 
python ${f}_test.py 3.20s user 0.01s system 99% cpu 3.221 total 
numpy_zero 
python ${f}_test.py 1.04s user 0.02s system 99% cpu 1.075 total 
python ${f}_test.py 1.08s user 0.02s system 99% cpu 1.106 total 
python ${f}_test.py 1.04s user 0.02s system 99% cpu 1.065 total 
python ${f}_test.py 1.03s user 0.02s system 99% cpu 1.059 total 
python ${f}_test.py 1.05s user 0.01s system 99% cpu 1.064 total 
tiny 
python ${f}_test.py 0.93s user 0.02s system 99% cpu 0.955 total 
python ${f}_test.py 0.98s user 0.01s system 99% cpu 0.993 total 
python ${f}_test.py 0.93s user 0.02s system 99% cpu 0.953 total 
python ${f}_test.py 0.92s user 0.02s system 99% cpu 0.944 total 
python ${f}_test.py 0.96s user 0.01s system 99% cpu 0.978 total 
tiny_zero 
python ${f}_test.py 0.71s user 0.03s system 99% cpu 0.739 total 
python ${f}_test.py 0.68s user 0.02s system 99% cpu 0.711 total 
python ${f}_test.py 0.70s user 0.01s system 99% cpu 0.721 total 
python ${f}_test.py 0.70s user 0.02s system 99% cpu 0.721 total 
python ${f}_test.py 0.67s user 0.01s system 99% cpu 0.687 total 

Bây giờ các bài kiểm tra này (như đã được chỉ ra) không phải là bài kiểm tra tốt nhất. Tuy nhiên, họ vẫn cho thấy rằng tinyarray là phù hợp hơn cho các mảng nhỏ.
Một thực tế khác là các hoạt động phổ biến nhất sẽ nhanh hơn với tinyarray. Vì vậy, nó có thể có lợi ích tốt hơn của việc sử dụng hơn là chỉ tạo ra dữ liệu.

Tôi chưa bao giờ thử nó trong một dự án có đầy đủ tư, nhưng dự án kwant đang sử dụng nó

+0

trên một lưu ý phụ, nếu một số chức năng 'numpy' đang tạo quá nhiều chi phí, đôi khi có thể có lợi để trì hoãn nó bằng một hàm duy nhất, thay vì tìm kiếm nó trong mô-đun, tức là' d = numpy.array; a = d ([0. 0. 0.]) '. – zeroth

0

Tất nhiên NumPy tiêu thụ nhiều thời gian hơn trong trường hợp này, vì: a = np.array([0.0, 0.0, 0.0]) < = ~ =>a = [0.0, 0.0, 0.0]; a = np.array(a), phải mất hai bước . Nhưng mảng có nhiều chất lượng tốt, tốc độ cao của nó có thể được nhìn thấy trong các hoạt động trên chúng, chứ không phải là tạo ra chúng. Một phần suy nghĩ cá nhân của tôi :).

Các vấn đề liên quan