Почему при использовании списков вычисление суммы с помощью numba происходит медленнее? - PullRequest
1 голос
/ 11 апреля 2019

Это мой код:

@numba.jit( )
def dis4(x1,x2):
    s=0.0
    for i in range(len(x1)):
        s+=(x1[i]-x2[i])**2
    return math.sqrt(s)
x1=[random.random() for _ in range(m)]
x2=[random.random() for _ in range(m)]
%timeit dis4(x1,x2)

3,32 мс ± 37,8 мкс на цикл (среднее ± стандартное отклонение из 7 циклов, 100 циклов в каждом)

Напротив, это будет быстрее без jit.

137 мкс ± 1,62 мкс на цикл (среднее ± стандартное отклонение из 7 циклов, по 10000 циклов каждый)

1 Ответ

1 голос
/ 13 апреля 2019

Это медленнее, потому что numba (без вывода сообщений) скопировал списки.

Чтобы понять , почему это происходит, вам нужно знать, что numba имеет объектный режим и nopython-режим.В объектном режиме он может работать со структурами данных Python, однако он не будет намного быстрее, чем обычная функция Python, или даже медленнее (по крайней мере, в целом, есть очень редкие исключения).В режиме nopython numba не может работать с структурами данных Python, такими как list, поэтому для работы list s необходимо использовать список не-Python.Чтобы создать такой список не-Python (он называется отраженным списком) из списка Python, он должен скопировать и преобразовать содержимое списков.

Это копирование и преобразование делают его намного медленнее в вашем случае.

Это также причина, по которой обычно следует избегать использования аргументов без массивов или возвратов с функциями numba.Содержимое массивов не нужно конвертировать, по крайней мере, если dumb массива поддерживается numba, поэтому они «безопасны».

Если эти структуры данных (списки, кортежи, наборы) ограничены в numba, то они в порядке - однако, когда они пересекают границу Python numbaumb, их необходимо скопировать, что (почти) всегда сводит на нет все приросты производительности.


Просто чтобы показать, как функция работает с массивами:

import math
import random
import numba as nb
import numpy as np

def dis4_plain(x1,x2):
    s=0.0
    for i in range(len(x1)):
        s+=(x1[i]-x2[i])**2
    return math.sqrt(s)

@nb.jit
def dis4(x1,x2):
    s=0.0
    for i in range(len(x1)):
        s+=(x1[i]-x2[i])**2
    return math.sqrt(s)

m = 10_000
x1 = [random.random() for _ in range(m)]
x2 = [random.random() for _ in range(m)]
a1 = np.array(x1)
a2 = np.array(x2)

Время:

dis4(x1, x2)
dis4(a1, a2)

%timeit dis4_plain(x1, x2)
# 2.71 ms ± 178 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit dis4(x1, x2)
# 24.1 ms ± 279 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit dis4(a1, a2)
# 14 µs ± 608 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Так что в 10 раз медленнее со списками иnumba.jit, функция jited с массивами почти в 200 раз быстрее, чем функция Python со списками.

...