Рассмотрим следующий фрагмент кода, который генерирует какой-то (потенциально) огромный многомерный массив и выполняет numpy.tensordot
с ним (numpy.tensordot
ли мы один и тот же или два разных массива здесь, на самом деле не имеет значения).
import time
import numpy
L, N = 6, 4
shape = (2*L)*[N,]
A = numpy.arange(numpy.prod(shape)).reshape(shape)
A = A % 256 - 128 # [-127,+127]
axes=(range(1,2*L,2), range(0,2*L,2))
def run(dtype, repeat=1):
A_ = A.astype(dtype)
t = time.time()
for i in range(repeat):
numpy.tensordot(A_, A_, axes)
t = time.time() - t
print(dtype, ' \t%8.2f sec\t%8.2f MB' %(t, A_.nbytes/1e6))
Теперь мы можем сравнить производительность для разных типов данных, например:
run(numpy.float64)
run(numpy.int64)
Поскольку массив состоит только из небольших целых чисел, я хотел бы сохранить некоторую память, используя dtype=int8
. Однако это замедляет матричное умножение A LOT.
Вот некоторые примеры тестов
Первый, важный для моего варианта использования. Остальные только для справки. Использование Numpy 1.13.1 и Python 3.4.2
Большой массив
L, N = 6, 4; A.size = 4**12 = 16777216
<class 'numpy.float64'> 59.58 sec 134.22 MB
<class 'numpy.float32'> 44.19 sec 67.11 MB
<class 'numpy.int16'> 711.16 sec 33.55 MB
<class 'numpy.int8'> 647.40 sec 16.78 MB
Тот же массив с разными типами данных. Память уменьшается, как ожидалось. Но почему большие различия в процессорном времени? Если бы я ожидал, что int
будет быстрее, чем float
.
Большой массив с различной формой
L, N = 1, 4**6; A.size = (4**6)**2 = 16777216
<class 'numpy.float64'> 57.95 sec 134.22 MB
<class 'numpy.float32'> 42.84 sec 67.11 MB
Форма не имеет большого эффекта.
Не так большой массив
L, N = 5, 4
<class 'numpy.float128'> 10.91 sec 16.78 MB
<class 'numpy.float64'> 0.98 sec 8.39 MB
<class 'numpy.float32'> 0.90 sec 4.19 MB
<class 'numpy.float16'> 9.80 sec 2.10 MB
<class 'numpy.int64'> 8.84 sec 8.39 MB
<class 'numpy.int32'> 5.55 sec 4.19 MB
<class 'numpy.int16'> 2.23 sec 2.10 MB
<class 'numpy.int8'> 1.82 sec 1.05 MB
Меньшие значения, но та же странная тенденция.
малый массив, много повторений
L, N = 2, 4; A.size = 4 ** 4 = 256; повторить = 1000000
<class 'numpy.float128'> 17.92 sec 4.10 KB
<class 'numpy.float64'> 14.20 sec 2.05 KB
<class 'numpy.float32'> 12.21 sec 1.02 KB
<class 'numpy.float16'> 41.72 sec 0.51 KB
<class 'numpy.int64'> 14.21 sec 2.05 KB
<class 'numpy.int32'> 14.26 sec 1.02 KB
<class 'numpy.int16'> 13.88 sec 0.51 KB
<class 'numpy.int8'> 13.03 sec 0.26 KB
Кроме того, что float16
намного медленнее, все в порядке.
Вопрос
Почему int8
для очень больших массивов настолько медленнее? Есть ли способ обойти это? Сохранение памяти становится все более важным для больших массивов!