2d dizisi (veya nd dizisi) C- veya F-bitişik olduğunda, bir işlevi bir 2d dizisine eşleme görevi pratik olarak bir işlevi 1d dizisine eşleme göreviyle aynıdır - biz sadece bu şekilde görüntülemek zorunda, örneğin aracılığıyla np.ravel(A,'K')
.
1d dizisi için olası çözüm, örneğin burada tartışılmıştır .
Bununla birlikte, 2d dizisinin belleği bitişik olmadığında, durum biraz daha karmaşıktır, çünkü eksen yanlış sırada işlenirse olası önbellek kayıplarından kaçınmak ister.
Numpy, eksenleri mümkün olan en iyi sırayla işlemek için halihazırda bir makineye sahip. Bu makineyi kullanmanın bir yolu np.vectorize
. Bununla birlikte, numpy'nin dokümantasyonu np.vectorize
, "performans için değil, öncelikli olarak kolaylık sağlamak için sağlandığını" belirtir - yavaş bir python işlevi, tüm ilişkili ek yük ile yavaş bir python işlevi olarak kalır! Diğer bir sorun da bellek tüketimidir - örneğin bu SO-postasına bakın .
Kişi bir C işlevinin performansına sahip olmak ama numpy'nin makinesini kullanmak istediğinde, ufunc'lar oluşturmak için numba kullanmak iyi bir çözümdür, örneğin:
# runtime generated C-function as ufunc
import numba as nb
@nb.vectorize(target="cpu")
def nb_vf(x):
return x+2*x*x+4*x*x*x
Kolayca atar, np.vectorize
ancak aynı işlev numpy-array çarpma / toplama olarak gerçekleştirildiğinde, yani
# numpy-functionality
def f(x):
return x+2*x*x+4*x*x*x
# python-function as ufunc
import numpy as np
vf=np.vectorize(f)
vf.__name__="vf"
Zaman ölçüm kodu için bu cevabın ekine bakınız:
Numba'nın sürümü (yeşil), np.vectorize
şaşırtıcı olmayan python işlevinden (yani ) yaklaşık 100 kat daha hızlıdır . Ama aynı zamanda numpy işlevselliğinden yaklaşık 10 kat daha hızlıdır, çünkü numbas sürümü ara dizilere ihtiyaç duymaz ve bu nedenle önbelleği daha verimli kullanır.
Numba'nın ufunc yaklaşımı, kullanılabilirlik ve performans arasında iyi bir değiş tokuş olsa da, yine de yapabileceğimizin en iyisi değil. Yine de herhangi bir görev için en iyi sihirli değnek veya yaklaşım yoktur - sınırlamanın ne olduğunu ve nasıl azaltılabileceğini anlamak gerekir.
Örneğin, transandantal fonksiyonlar için (örneğin exp
, sin
, cos
) numba üzerinde herhangi bir avantaja sağlamaz numpy en np.exp
(yaratılmış hiçbir geçici diziler vardır - hız-up ana kaynağıdır). Ancak Anaconda kurulumum, 8192'den büyük vektörler için Intel'in VML'sini kullanıyor - bellek bitişik değilse bunu yapamaz. Bu nedenle, Intel'in VML'sini kullanabilmek için öğeleri bitişik bir belleğe kopyalamak daha iyi olabilir:
import numba as nb
@nb.vectorize(target="cpu")
def nb_vexp(x):
return np.exp(x)
def np_copy_exp(x):
copy = np.ravel(x, 'K')
return np.exp(copy).reshape(x.shape)
Karşılaştırmanın adil olması için, VML'nin paralelleştirmesini kapattım (ekteki koda bakın):
Görüldüğü gibi, VML devreye girdiğinde, kopyalamanın ek yükü telafi edilenden daha fazladır. Yine de, veriler L3 önbelleği için çok büyük hale geldiğinde, görev bir kez daha bellek bant genişliğine bağlı hale geldiğinden avantaj minimumdur.
Öte yandan numba, bu yazıda açıklandığı gibi Intel'in SVML'sini de kullanabilir :
from llvmlite import binding
# set before import
binding.set_option('SVML', '-vector-library=SVML')
import numba as nb
@nb.vectorize(target="cpu")
def nb_vexp_svml(x):
return np.exp(x)
ve paralelleştirme verimiyle VML kullanma:
numba'nın sürümünün daha az ek yükü vardır, ancak bazı boyutlar için VML, ek kopyalama ek yüküne rağmen SVML'yi yener - bu, numba'nın ufuncları paralelleştirilmediğinden biraz sürpriz değildir.
Listeler:
A. polinom fonksiyonunun karşılaştırılması:
import perfplot
perfplot.show(
setup=lambda n: np.random.rand(n,n)[::2,::2],
n_range=[2**k for k in range(0,12)],
kernels=[
f,
vf,
nb_vf
],
logx=True,
logy=True,
xlabel='len(x)'
)
B. karşılaştırması exp
:
import perfplot
import numexpr as ne # using ne is the easiest way to set vml_num_threads
ne.set_vml_num_threads(1)
perfplot.show(
setup=lambda n: np.random.rand(n,n)[::2,::2],
n_range=[2**k for k in range(0,12)],
kernels=[
nb_vexp,
np.exp,
np_copy_exp,
],
logx=True,
logy=True,
xlabel='len(x)',
)