在python-Numba中正确地使用列表(list)

在python-Numba中正确地使用列表(list)

一、实验代码

import numpy as np
import time
NUM = 160
from numba import jit
a = [] # a为空列表,全局变量
@jit(nopython=True)
def fun1():
	# 表明a为全局变量
    global a 
    for i in range(NUM):
        a.append(i)
        
fun1()

代码要实现的功能是在jit编译器修饰下实现对外部/全局列表的修改

二、实验改进与效果展示

2.1
上述代码会报错,报错信息如下:
在python-Numba中正确地使用列表(list)_第1张图片
意思就是说编译器不知道这个空列表是什么类型的。

2.2
对于上面的报错信息,我的修改是在声明列表a的时候先塞一个元素,即

a = [0]

但是还是会报错,报错信息如下:

The use of a reflected list(int64) type, assigned to variable ‘a’ in globals, is not supported as globals are considered compile-time constants and there is no known way to compile a reflected list(int64) type as a constant.

意思就是说使用了一个反射的列表,它还是全局变量。在这篇博客中讲到numba编译全局变量,会把全局变量编译成常量,导致不可以修改。
在这里我要说一下reflected list,官方文档中有下面这么一段话

In nopython mode, Numba does not operate on Python objects. list are compiled into an internal representation. Any list arguments must be converted into this representation on the way in to nopython mode and their contained elements must be restored in the original Python objects via a process called reflection. Reflection is required to maintain the same semantics as found in regular Python code. However, the reflection process can be expensive for large lists and it is not supported for lists that contain reflected data types. Users cannot use list-of-list as an argument because of this limitation.

机翻如下:
在NOPYTHON模式下,NUMBA不在Python对象上运行。列表被编译为内部表示。任何列表参数都必须在nopython模式的方式转换为此表示,并且必须通过称为反射的过程在原始Python对象中恢复它们所包含的元素。需要反射以维持在常规Python代码中发现的相同语义。然而,反射过程对于大型列表可能是昂贵的,并且不支持包含反射数据类型的列表。由于此限制,用户无法使用列表列表作为参数。

大概的意思就是说当python列表作为参数,传进jit修饰的函数时,函数里面的列表与函数外面的列表需要通过一定的转换,而且在函数里面的列表被修改时,python解释器并不知道列表哪里被修改了,导致那个时刻函数里面的列表和函数外面的列表是不一样的。

2.3
接下来是将列表作为函数参数进行实验,实验代码如下:

import numpy as np
import time
NUM = 160
from numba import jit
a = [0]

@jit(nopython=True)
def fun1(a):
    for i in range(NUM):
        a.append(i)

fun1(a)

结果是不会报错,有输出结果,但是有警告
在python-Numba中正确地使用列表(list)_第2张图片
问题到这里似乎已经解决了,如果忽视警告的话是可以实现功能的,但reflect的过程也是要花费时间的嘛。
经过5轮循环测试得出的时间如下

0.34407591819763184 # 编译的时间久
0.0009503364562988281
0.0
0.0
0.001032114028930664

2.4
避免全局变量或许是一种好的解决方法,可以在函数中声明a列表,当然记得要先让编译器知道a列表是什么数据类型,具体代码可以看下图:
在python-Numba中正确地使用列表(list)_第3张图片
这种方法的相关博客
对于某些情况下这种方法是可行的。

三、解决方法

使用numba官方的list,即numba.typed.List,这是在numba 0.45 之后新添加的特性。具体的使用方法:numba.typed.List的使用
使用这种方式的代码如下:

import numpy as np
import time
NUM = 160
from numba import jit
from numba.typed import List

a = List()
a.append(1) # 同样需要指定数据类型,塞个1,数据类型就是int

@jit(nopython=True)
def fun1(a):
    for i in range(NUM):
        a.append(i)
for i in range(5):
    start = time.time()
    fun1(a)
    print(time.time()-start)

5轮耗时如下:

0.06183624267578125 #编译比较快
0.0
0.0
0.0
0.0

你可能感兴趣的:(python,python,list,开发语言,numba)