2014-12-13 22 views
14

Sonunda kodumda bir performans darboğazı buldum ama nedeninin ne olduğu konusunda kafam karıştı. Bunu çözmek için numpy.zeros kullanmak yerine numpy.zeros_like çağrılarımı değiştirdim. Ama neden zeros_like sooooo çok daha yavaş? Örneğin (zeros çağrı e-05 unutmayın) içinNeden numpy.zeros ve numpy.zeros_like arasındaki performans farkı?

: garip ardından

>>> timeit.timeit('np.zeros((12488, 7588, 3), np.uint8)', 'import numpy as np', number = 10) 
5.2928924560546875e-05 
>>> timeit.timeit('np.zeros_like(x)', 'import numpy as np; x = np.zeros((12488, 7588, 3), np.uint8)', number = 10) 
1.4402990341186523 

Ama zeros ile oluşturulmuş bir dizi yazılı zeros_like ile oluşturulmuş bir dizi daha belirgin bir şekilde yavaştır:

>>> timeit.timeit('x[100:-100, 100:-100] = 1', 'import numpy as np; x = np.zeros((12488, 7588, 3), np.uint8)', number = 10) 
0.4310588836669922 
>>> timeit.timeit('x[100:-100, 100:-100] = 1', 'import numpy as np; x = np.zeros_like(np.zeros((12488, 7588, 3), np.uint8))', number = 10) 
0.33325695991516113 

Benim sanırım zeros, bazı CPU hilelerini kullanıyor ve aslında tahsis etmek için belleğe yazmıyor. Bu yazıldığı zaman anında yapılır. Ama bu hala dizi oluşturma zamanlarında büyük bir tutarsızlığı açıklamıyor.

>>> numpy.__version__ 
'1.9.1' 
+0

'zeros' 'memset' kullanır; 'zeros_like', bir ton saçma bir' dolum 'yapmakta etkili gibi görünüyor. Asıl yürütmeyi kovalamayı denedim ama gereksiz yere geniş. – Veedrac

+0

'zeros', memset kullanmaz. –

cevap

10

ipython My zamanlamaları (a simplier sürümüyle gelen timeit arayüzü ile):

In [57]: timeit np.zeros_like(x) 
1 loops, best of 3: 420 ms per loop 

In [58]: timeit np.zeros((12488, 7588, 3), np.uint8) 
100000 loops, best of 3: 15.1 µs per loop 

ben bakmak

Ben şimdiki numpy sürümü ile Mac OS X Yosemite koşuyorum ipython ile kodu (np.zeros_like??) görüyorum:

res = empty_like(a, dtype=dtype, order=order, subok=subok) 
multiarray.copyto(res, 0, casting='unsafe') 

iken 210 bir kara kutu - salt derlenmiş koddur. empty için

Zamanlamaları şunlardır:

In [63]: timeit np.empty_like(x) 
100000 loops, best of 3: 13.6 µs per loop 

In [64]: timeit np.empty((12488, 7588, 3), np.uint8) 
100000 loops, best of 3: 14.9 µs per loop 

Yani zeros_like ekstra zaman o copy içindedir.

Testlerimde, atama zamanlarındaki fark (x[]=1) ihmal edilebilir.

Benim tahminim bu zeros, ones, empty tüm erken derlenmiş kreasyonlar. empty_like, kolaylık sağlamak için eklenmiştir, sadece şekil ve şekil bilgisi girişinden alınmıştır. zeros_like, kolay programlama bakımına (empty_like'u tekrar kullanma) hıza göre daha fazla gözle yazılmıştır.

np.ones ve np.full ayrıca np.empty ... copyto dizisini kullanın ve benzer zamanlamaları gösterir.


https://github.com/numpy/numpy/blob/master/numpy/core/src/multiarray/array_assign_scalar.c

kopyalayan bir dizi için bir skaler (örneğin 0) dosyası gibi görünmektedir. memset kullanımını göremiyorum.

https://github.com/numpy/numpy/blob/master/numpy/core/src/multiarray/alloc.c, malloc ve calloc numaralarına sahiptir.

https://github.com/numpy/numpy/blob/master/numpy/core/src/multiarray/ctors.c - zeros ve empty için kaynak.Her ikisi de PyArray_NewFromDescr_int'u arar, ancak biri npy_alloc_cache_zero ve diğer npy_alloc_cache'u kullanarak biter. alloc.c aramalar alloc yılında

npy_alloc_cache . npy_alloc_cache_zero, npy_alloc_cache numaralı telefonu arayarak bir memset izlemektedir. alloc.c'daki kod ayrıca bir DİŞLİ seçeneği ile karıştırılır. En calloc v malloc+memset fark

Daha: Why malloc+memset is slower than calloc?

Ama önbelleğe alma ve çöp toplama ile , ben calloc/memset ayrım geçerli olup olmadığını merak ediyorum.

N = (1000, 1000) 
M = (slice(None, 500, None), slice(500, None, None)) 

Line # Mem usage Increment Line Contents 
================================================ 
    2 17.699 MiB 0.000 MiB @profile 
    3        def test1(N, M): 
    4 17.699 MiB 0.000 MiB  print(N, M) 
    5 17.699 MiB 0.000 MiB  x = np.zeros(N) # no memory jump 
    6 17.699 MiB 0.000 MiB  y = np.empty(N) 
    7 25.230 MiB 7.531 MiB  z = np.zeros_like(x) # initial jump 
    8 29.098 MiB 3.867 MiB  x[M] = 1  # jump on usage 
    9 32.965 MiB 3.867 MiB  y[M] = 1 
    10 32.965 MiB 0.000 MiB  z[M] = 1 
    11 32.965 MiB 0.000 MiB  return x,y,z 
+0

İnternallere bakmak doğru gitmek için doğruydu. "Zeros_like" nin sadece "sıfır" kelimelerini aramayacağı gariptir. Ödev testlerini birden çok kez çalıştırdım ve her zaman küçük farklar elde ettim, ancak zeros_like dizileri için farkedilir derecede daha hızlı zamanlarım var. –

+3

Yukarıdaki tahminler sebebi değildir. Asıl fark, sıfırlama belleğinin, işletim sisteminin VM alt sistemine bırakılıp bırakılmadığı veya işlemin kendisi tarafından yapılıp yapılmadığıdır. –

+0

"Calloc/memset" açıklaması mantıklı geliyor, ancak "numpy" kodunda bunu doğrulamakta zorlanıyorum. – hpaulj

10

Modern OS neredeyse bellek ayrılamadı: zeros_like ön herşeyi ayırır iken


memory_profile paketi ile Bu basit test, zeros ve empty 'on-the-fly' bellek ayrılamadı iddiasını destekleyen yani, bellek, sadece ilk kullanıldığı zaman bir işleme verilir. zeros işletim sisteminden bellek alır, böylece OS ilk kullanıldığında sıfırlar. Diğer yandan zeros_like, tahsis edilen belleği tek başına sıfırlarla doldurur. Her iki yol da aynı miktarda işi gerektirir --- sadece zeros_like ile sıfırlama işlemi yapılırken, zeros bunu anında yapıyor.

Teknik olarak C farkı calloc VS. malloc+memset çağırıyor.

İlgili konular