为什么numba和numpy在就地操作中表现不同?

时间:2017-12-20 21:04:31

标签: python numpy numba in-place

用numba njit 装饰的函数( jit(nopython=True) 的别名会产生与 numpy不同的结果 on inplace operations(简单 @jit(nopython=False) 也会提供numpy)的不同结果:

In [1]: import numpy as np
        from numba import njit

        def npfun(arr):
            arr -= arr[3]

        @njit
        def jitfun(arr):
            arr -= arr[3]

        arr1 = np.ones((6,2))
        arr2 = arr1.copy()

        npfun(arr1)
        jitfun(arr2)

        arr1 == arr2

Out[1]: array([[ True,  True],
               [ True,  True],
               [ True,  True],
               [ True,  True],
               [False, False],
               [False, False]], dtype=bool)  

看起来 numpy 评估rhs并将其作为副本传递,而 numba 将rhs视为视图。是否有任何技术原因可以这样做?

numpy 1.13.3 numba 0.35

2 个答案:

答案 0 :(得分:3)

您正在进行的操作:

arr -= arr[3]

以前是NumPy中未定义的行为。它最近才被定义,在NumPy 1.13.0中,已发布June 7th this year。新的,定义的行为总是表现为它复制了所有输入,尽管它试图避免在检测到它不需要时实际制作副本。

看起来Numba目前没有尝试模仿新行为,无论是因为它有多新,还是因为Numba特有的担忧。

答案 1 :(得分:2)

解决方法

如果您使用带有out参数的显式就地操作而不是增强赋值,它将帮助Numba:replace

arr -= arr[3] 

np.subtract(arr, arr[3], out=arr)

这使得jitted版本(虽然使用@jit,而不是@njit)的功能与NumPy版本完全相同。值得注意的是,尝试@njit这个函数现在会失败,告诉你比njit不能处理out参数。

我已经看到你了opened an issue on this,因此有可能更改扩展作业的处理以匹配NumPy。

为什么会发生这种情况

正如hpaulj所说,Numba输出相当于循环数组的行。 Python JITter无法对NumPy底层的C代码做任何事情;它需要Python才能使用。支持NumPy方法(在某种程度上)的原因是Numba开发人员遇到了将NumPy数组操作解码为标量对象的显式Python迭代的麻烦,然后可以将其传递给LLVM。来自documentation

  
      
  • 合成实现数组表达式的Python函数:这个新的Python函数本质上就像一个Numpy ufunc,在广播数组参数中的标量值上返回表达式的结果。降低功能通过从数组表达式树转换为Python AST来实现这一点。
  •   
  • 将合成Python函数编译到内核中:此时,reduce函数依赖于现有代码来降低ufunc和DUFunc内核,在定义如何降低对合成函数的调用之后调用numba.targets.numpyimpl.numpy_ufunc_kernel()
  •   
     

最终结果类似于Numba对象模式中的循环提升。

上述numpy_ufunc_kernel收集索引,迭代。如果被迭代的对象在迭代过程中发生变异,则会使事情发生变化。