Как dtype влияет на скорость работы строк и столбцов в Numpy?

Я пытаюсь понять, как наилучшим образом использовать C-упорядочение массивов для создания высокопроизводительного кода. Я ожидал, что операции, которые пересекают строки, должны быть быстрее, чем те, которые пересекают столбцы. Действительно, это было верно для первого примера, который я попробовал:

X = np.ones((10000,10000),dtype='int64')
print(X.dtype)
print(X.flags)

%timeit np.sum(X,axis=0)

%timeit np.sum(X,axis=1)

Это производит вывод:

int64
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False
10 loops, best of 3: 79.6 ms per loop
10 loops, best of 3: 61.1 ms per loop

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

Вот где я очень запутался. Если я изменю dtype на float64, то операции со столбцами станут почти в два раза быстрее, чем операции со строками:

X = np.ones((10000,10000),dtype='float')
print(X.dtype)
print(X.flags)

%timeit np.sum(X,axis=0)

%timeit np.sum(X,axis=1)

Производит продукцию:

float64
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False
10 loops, best of 3: 67.7 ms per loop
10 loops, best of 3: 123 ms per loop

Может кто-нибудь уточнить, почему это происходит?

РЕДАКТИРОВАТЬ: Было предложено в комментариях, что я пытаюсь снова с меньшей матрицей (1000,1000). Когда я бегу:

import time
import numpy as np

X = np.ones((1000,1000),dtype='float')
print(X.dtype)
print(X.flags)

%timeit np.sum(X,axis=0)
%timeit np.sum(X,axis=1)

X = np.ones((1000,1000),dtype='int64')
print(X.dtype)
print(X.flags)

%timeit np.sum(X,axis=0)
%timeit np.sum(X,axis=1)

Я получаю вывод:

float64
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False
1000 loops, best of 3: 598 µs per loop
1000 loops, best of 3: 1.06 ms per loop
int64
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False
1000 loops, best of 3: 788 µs per loop
1000 loops, best of 3: 632 µs per loop

Так что эффект сохраняется.

1 ответ

Я не могу подтвердить ваш второй результат в OSX (различные версии Python) - он похож на ваш первый результат:

In [27]: X = np.ones((10000,10000),dtype='float64')
    ...: print(X.dtype)
    ...: print(X.flags)
    ...: 
    ...: %timeit np.sum(X,axis=0)
    ...: 
    ...: %timeit np.sum(X,axis=1)
    ...: 
float64
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False
10 loops, best of 3: 67.6 ms per loop
10 loops, best of 3: 62 ms per loop

РЕДАКТИРОВАТЬ: я повторил все ваши вычисления, используя напрямую timeit.repeat():

import timeit
t = timeit.repeat('np.sum(X,axis=0)', setup="import numpy as np; X = np.ones((10000,10000),dtype='float64')", repeat=50, number=1); print(min(t));
t = timeit.repeat('np.sum(X,axis=1)', setup="import numpy as np; X = np.ones((10000,10000),dtype='float64')", repeat=50, number=1); print(min(t));
t = timeit.repeat('np.sum(X,axis=0)', setup="import numpy as np; X = np.ones((10000,10000),dtype='int64')", repeat=50, number=1); print(min(t));
t = timeit.repeat('np.sum(X,axis=1)', setup="import numpy as np; X = np.ones((10000,10000),dtype='int64')", repeat=50, number=1); print(min(t));

с этими временами:

Python 2.7.13 |Continuum Analytics, Inc.| (default, Dec 20 2016, 23:05:08) 
IPython 5.3.0 -- An enhanced Interactive Python.
numpy 1.12.1

0.0637669563293 # float64, axis=0
0.0558688640594 # float64, axis=1
0.0669782161713 # int64, axis=0
0.0576930046082 # int64, axis=1

а также

Python 3.6.2 |Continuum Analytics, Inc.| (default, Jul 20 2017, 13:14:59) 
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.
numpy 1.13.1

0.06289491400821134
0.05558946297969669
0.0670205659698695
0.057950171001721174

а также

Python 3.5.3 |Continuum Analytics, Inc.| (default, Mar  6 2017, 12:15:08) 
IPython 5.3.0 -- An enhanced Interactive Python.
numpy 1.11.3

0.06345970398979262
0.05561513203429058
0.07043616304872558
0.057934076990932226

Наконец, на моем телефоне Android:

Python 3.6.2 (default, Jul 19 2017, 11:01:41)
IPython 6.1.0
numpy 1.12.0

0.39130385394673795
0.24979593697935343
0.42852322908584028
0.28863119706511497

и система Windows (Python 3.4 32bit):

0.158213707338
0.149441164907
0.365552662475
0.128456460354
Другие вопросы по тегам