numba fibonacci 函数速度较慢并且不会 return 相同的结果

The numba fibonacci function is slower and does not return the same result

此代码 return 速度慢且输出不同:


from numba import jit
from timeit import default_timer as timer
def fibonacci(n):
    a, b = 1, 1
    for i in range(n):
        a, b = a+b, a
    return a
fibonacci_jit = jit(fibonacci)

开始测试

start = timer()
print fibonacci(100)
duration = timer() - start

开始测试

startnext = timer()
print fibonacci_jit(100)
durationnext = timer() - startnext


print(duration, durationnext)

结果:

C:\Python27>python numba_test_003.py
927372692193078999176
1445263496
(0.00038264393810854576, 0.17378674127528523)

#下一个

C:\Python27>python numba_test_003.py
927372692193078999176
1445263496
(0.0004830358514597401, 0.19266426987655644)

因为您只 运行 一次 Numba jitted 函数,您看到的是 jit 编译时间和 运行 时间的总和。下次你 运行 numba 函数时,你只会看到 运行 时间,而且它会更快,因为 numba 缓存了每组唯一输入参数类型的编译代码:

startnext = timer()
print fibonacci_jit(100)
durationnext = timer() - startnext
print(duration, durationnext)

#5035488507601418376
#(0.0003879070281982422, 0.14705300331115723)

startnext = timer()
print fibonacci_jit(100)
durationnext = timer() - startnext

print(duration, durationnext)

#5035488507601418376
#(0.0003879070281982422, 0.0002810955047607422)

答案的差异是由于 Python 本机对象 int 是无限精度的,而 numba 使用的是容量有限的类 C 本机 int并且可以溢出。如果您 运行 具有较小输入的函数,您应该会看到它一致,直到您溢出 numba int。

速度变慢的原因是编译时间。第一次调用未签名的 numba-jitted 函数时,它将检查类型并为这些参数编译函数。后续运行会更快,因为已经编译好了:

for _ in range(5):
    start = timer()
    fibonacci_jit(100)
    print(timer() - start)

0.18958417814776496      # first run - includes compilation
6.1441049545862825e-06
3.3513299761978033e-06
3.3513299761978033e-06
3.3513299761978033e-06

但是,由于 numba 使用 C 类型,因此您的整数可能会溢出。您可以轻松检查类型:

fibonacci_jit.inspect_types()

fibonacci (int64,)
--------------------------------------------------------------------------------
# File: <ipython-input-19-a73271f1a552>
# --- LINE 3 --- 
# label 0
#   del $const0.1
#   del [=11=].4
#   del [=11=].2
#   del [=11=].3

def fibonacci(n):

    # --- LINE 4 --- 
    #   n = arg(0, name=n)  :: int64
    #   $const0.1 = const(tuple, (1, 1))  :: (int64 x 2)
    #   [=11=].4 = exhaust_iter(value=$const0.1, count=2)  :: (int64 x 2)
    #   [=11=].2 = static_getitem(value=[=11=].4, index=0, index_var=None)  :: int64
    #   [=11=].3 = static_getitem(value=[=11=].4, index=1, index_var=None)  :: int64
    #   a = [=11=].2  :: int64
    #   b = [=11=].3  :: int64
    #   jump 8
    # label 8

    a, b = 1, 1

    # --- LINE 5 --- 
    #   jump 10
    # label 10
    #   .1 = global(range: <class 'range'>)  :: Function(<class 'range'>)
    #   .3 = call .1(n, func=.1, args=[Var(n, <ipython-input-19-a73271f1a552> (4))], kws=(), vararg=None)  :: (int64,) -> range_state_int64
    #   del n
    #   del .1
    #   .4 = getiter(value=.3)  :: range_iter_int64
    #   del .3
    #   $phi18.1 = .4  :: range_iter_int64
    #   del .4
    #   jump 18
    # label 18
    #   .2 = iternext(value=$phi18.1)  :: pair<int64, bool>
    #   .3 = pair_first(value=.2)  :: int64
    #   .4 = pair_second(value=.2)  :: bool
    #   del .2
    #   $phi20.1 = .3  :: int64
    #   $phi38.1 = .3  :: int64
    #   del $phi38.1
    #   del .3
    #   $phi38.2 = $phi18.1  :: range_iter_int64
    #   del $phi38.2
    #   branch .4, 20, 38
    # label 20
    #   del .4
    #   i = $phi20.1  :: int64
    #   del i
    #   del $phi20.1
    #   del .4
    #   del $a20.5

    for i in range(n):

        # --- LINE 6 --- 
        #   .4 = a + b  :: int64
        #   $a20.5 = a  :: int64
        #   a = .4  :: int64
        #   b = $a20.5  :: int64
        #   jump 18
        # label 38
        #   del b
        #   del $phi20.1
        #   del $phi18.1
        #   del .4
        #   jump 40
        # label 40
        #   del a

        a, b = a+b, a

    # --- LINE 7 --- 
    #   .2 = cast(value=a)  :: int64
    #   return .2

    return a


================================================================================

至少在我的电脑上它使用 int64,所以最大可能值为 9223372036854775807。你不能用 numba 解决这个问题。如果你需要任意精度的整数,你必须坚持 Python.