Вычисление массива Numpy медленнее, чем эквивалентный код Java - PullRequest
0 голосов
/ 12 октября 2019

Я пытаюсь работать с большими двумерными массивами в Python, но это очень медленно. Например:

start = time.time()
result = numpy.empty([5000, 5000])

for i in range(5000):
    for j in range(5000):
        result[i, j] = (i * j) % 10

end = time.time()
print(end - start) # 8.8 s

Та же программа на Java гораздо быстрее:

long start = System.currentTimeMillis();
int[][] result = new int[5000][5000];

for (int i = 0; i < 5000; i++) {
    for (int j = 0; j < 5000; j++) {
        result[i][j] = (i * j) % 10;
    }
}

long end = System.currentTimeMillis();
System.out.println(end - start); // 121 ms

Это потому, что Python интерпретируется как язык? Есть ли способ улучшить это? Или почему Python так популярен для работы с матрицами, искусственным интеллектом и т. Д .?

Ответы [ 5 ]

6 голосов
/ 12 октября 2019

Вы на самом деле не используете мощь NumPy - вы выполняете свои циклы вручную на уровне Python. Это примерно аналогично удивлению, почему все пользуются автомобилями, если ходить в магазин намного быстрее, когда вы тащите машину за собой.

Используйте встроенные операции NumPy, чтобы перенести свою работу в циклы уровня C,Например,

temp = numpy.arange(5000)
result = numpy.outer(temp, temp) % 10
# or result = temp * temp[:, None] % 10

Это будет намного быстрее.

6 голосов
/ 12 октября 2019

Прочитайте до конца, чтобы увидеть, как NumPy может превзойти ваш Java-код в 5 раз.

numpy Сила заключается в векторизованных вычислениях. Ваш код Python основан на интерпретируемых циклах, а интерпретируемые циклы, как правило, работают медленно.

Я переписал ваш код Python как векторизованное вычисление, и это сразу ускорило его в ~ 16 раз:

In [41]: v = np.arange(5000)

In [42]: %timeit np.outer(v, v) % 10
1 loop, best of 3: 544 ms per loop

Вычисление % 10 вместо создания нового массива ускоряет работу еще на 20%:

In [37]: def f(n):
    ...:     v = np.arange(n)
    ...:     a = np.outer(v, v)
    ...:     a %= 10
    ...:     return a
    ...:

In [39]: %timeit f(5000)
1 loop, best of 3: 437 ms per loop

edit 1: Выполнение вычислений в 32 битах вместо 64(чтобы соответствовать вашему коду Java) в основном соответствует производительности Java - h / t @ user2357112 для указания на это:

In [50]: def f(n):
    ...:  v = np.arange(n, dtype=np.int32)
    ...:  a = np.outer(v, v)
    ...:  a %= 10
    ...:  return a
    ...:

In [51]: %timeit f(5000)
10 loops, best of 3: 126 ms per loop

edit 2: И с небольшой работоймы можем сделать этот код примерно в 5 раз быстрее, чем ваша реализация Java (здесь ne относится к numexpr module ):

In [69]: v = np.arange(5000, dtype=np.int32)

In [70]: vt = v[np.newaxis].T

In [71]: %timeit ne.evaluate('v * vt % 10')
10 loops, best of 3: 25.3 ms per loop

edit 3: Пожалуйста, не забудьте также взглянуть на ответ , данный @ max9111 .

2 голосов
/ 12 октября 2019

Другой вариант для примеров @ user2357112 и @NPE, которые уже были показаны, заключается в использовании Numba (Jit-компилятор). Чисто интерпретируемые циклы Python очень медленные и их следует избегать там, где важна производительность.

Пример

import numpy as np
import numba as nb
import numexpr as ne

@nb.njit(parallel=True)
def func_1(num):
    result = np.empty((num, num),dtype=np.int32)
    for i in nb.prange(result.shape[0]):
        for j in range(result.shape[1]):
            result[i, j] = (i * j) % 10
    return result

Время

#The first call has a higher overhead due to compilation
#parallel: @nb.njit(parallel=True)
%timeit res=func_1(5000)
#20.7 ms ± 1.11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
#single threaded: @nb.njit(parallel=True)
%timeit res=func_1(5000)
#71.9 ms ± 521 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

#NPE
%%timeit
v = np.arange(5000, dtype=np.int32)
vt = v[np.newaxis].T
ne.evaluate('v * vt % 10')
#35.5 ms ± 863 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
2 голосов
/ 12 октября 2019

Есть ли способ улучшить его?

См. Разницу во времени:

In [13]: arr = np.empty([5000, 5000])                                                                          

In [14]: %timeit np.multiply(*np.indices(arr.shape)) % 10                                                      
482 ms ± 2.73 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

, где np.inidices представляетиндексы сетки


, почему Python так популярен для работы с матрицами, искусственным интеллектом, ...

В C реализовано множество подпрограмм (которые остаютсяодин из самых (если не один, самый быстрый язык) и использует плотно упакованные массивы. Связанная тема: https://stackoverflow.com/a/8385658/3185459

Вы также можете подразумевать Pandas , популярную и мощную библиотеку для анализа данных / data-science. Многие специалисты предпочитают его за гибкое представление данных, лаконичный синтаксис, широкий набор функций и эффективную обработку больших наборов данных.

0 голосов
/ 12 октября 2019

Python очень популярен для ИИ по многим причинам: - Простота создания прототипа - Множество ML lib / big commu - Использует gpu для массово параллельных вычислений на тензорах с CUDA, например

Для наших задач попробуйте:использовать собственный список на python (вы используете numpy, возможно, он тяжелее

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...