我的 numba 代码能比 numpy 快吗

Can my numba code be faster than numpy

我是 Numba 的新手,我正在尝试加速一些已证明对 numpy 来说太笨重的计算。我在下面给出的示例比较了一个包含我的计算子集的函数,该函数使用函数的 vectorized/numpy 和 numba 版本,后者也通过注释掉 @autojit 装饰器被测试为纯 python .

我发现 numba 和 numpy 版本相对于纯 python 提供了相似的加速,两者都是大约 10 倍的速度提升。 numpy 版本实际上比我的 numba 函数稍快,但由于此计算的 4D 性质,当 numpy 函数中的数组的大小比这个玩具示例大得多时,我很快 运行 内存不足。

这种提速很好,但我经常在网络上看到从纯 python 迁移到 numba 时提速超过 100 倍。

我想知道在 nopython 模式下移动到 numba 时是否有普遍预期的速度提升。我还想知道我的 numba 化函数中是否有任何组件会限制进一步的速度提升。

import numpy as np                                                                    
from timeit import default_timer as timer                                             
from numba import  autojit                                                            
import math                                                                           


def vecRadCalcs(slope,  skyz, solz, skya, sola):                                      

    nloc = len(slope)                                                                 
    ntime =  len(solz)                                                                
    [lenz, lena] = skyz.shape                                                         
    asolz = np.tile(np.reshape(solz,[ntime,1,1,1]),[1,nloc,lenz,lena])                
    asola = np.tile(np.reshape(sola,[ntime,1,1,1]),[1,nloc,lenz,lena])                
    askyz = np.tile(np.reshape(skyz,[1,1,lenz,lena]),[ntime,nloc,1,1])                
    askya = np.tile(np.reshape(skya,[1,1,lenz,lena]),[ntime,nloc,1,1])                
    phi1 = np.cos(asolz)*np.cos(askyz)                                                
    phi2 = np.sin(asolz)*np.sin(askyz)*np.cos(askya- asola)                           
    phi12 = phi1 + phi2                                                               
    phi12[phi12> 1.0] = 1.0                                                           
    phi = np.arccos(phi12)                                                            

    return(phi)                                                                       


@autojit                                                                              
def RadCalcs(slope,  skyz, solz, skya, sola, phi):                                    

    nloc = len(slope)                                                                 
    ntime =  len(solz)                                                                
    pop = 0.0                                                                         
    [lenz, lena] = skyz.shape                                                         
    for iiT in range(ntime):                                                          
        asolz = solz[iiT]                                                             
        asola = sola[iiT]                                                             
        for iL in range(nloc):                                                        
            for iz in range(lenz):                                                    
                for ia in range(lena):                                                
                    askyz = skyz[iz,ia]                                               
                    askya = skya[iz,ia]                                               
                    phi1 = math.cos(asolz)*math.cos(askyz)                            
                    phi2 = math.sin(asolz)*math.sin(askyz)*math.cos(askya- asola)     
                    phi12 = phi1 + phi2                                               
                    if phi12 > 1.0:                                                   
                        phi12 = 1.0                                                   
                    phi[iz,ia] = math.acos(phi12)                                     
                    pop = pop + 1                                                     

    return(pop)                                                                       


zenith_cells = 90                                                                     
azim_cells = 360                                                                      
nloc = 10        # nominallly ~ 700                                                   
ntim = 10        # nominallly ~ 200000                                                

slope = np.random.rand(nloc) * 10.0                                                   
solz = np.random.rand(ntim) *np.pi/2.0                                                
sola = np.random.rand(ntim) * 1.0*np.pi                                               

base = np.ones([zenith_cells,azim_cells])                                             
skya =  np.deg2rad(np.cumsum(base,axis=1))                                            
skyz =  np.deg2rad(np.cumsum(base,axis=0)*90/zenith_cells)                            

phi = np.zeros(skyz.shape)                                                            

start = timer()                                                                       
outcalc = RadCalcs(slope,  skyz, solz, skya, sola, phi)                               
stop = timer()                                                                        
outcalc2 = vecRadCalcs(slope,  skyz, solz, skya, sola)                                
stopvec = timer()                                                                     

print(outcalc)                                                                        
print(stop-start)                                                                     
print(stopvec-stop)                                                                   

在我的机器上 运行ning numba 0.31.0,Numba 版本比矢量化解决方案快 2 倍。对 numba 函数进行计时时,您需要多次 运行 函数,因为第一次看到 jitting 代码的时间 + 运行 时间。随后的 运行s 将不包括 jit 函数时间的开销,因为 Numba 将 jitted 代码缓存在内存中。

另外,请注意,您的函数计算的不是同一件事——您要小心,您在比较相同的事情时使用类似 np.allclose 的结果。