Каково потребление памяти для арифметических выражений numpy, т.е.
vec ** 3 + vec ** 2 + vec
(vec является numpy.ndarray). Сохраняется ли массив для каждой промежуточной операции? Могут ли такие составные выражения иметь в несколько раз больше памяти, чем базовый ndarray?






Вы правы, для каждого промежуточного результата будет выделен новый массив. К счастью, пакет numexpr предназначен для решения этой проблемы. Из описания:
The main reason why NumExpr achieves better performance than NumPy is that it avoids allocating memory for intermediate results. This results in better cache utilization and reduces memory access in general. Due to this, NumExpr works best with large arrays.
Пример:
In [97]: xs = np.random.rand(1_000_000)
In [98]: %timeit xs ** 3 + xs ** 2 + xs
26.8 ms ± 371 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [99]: %timeit numexpr.evaluate('xs ** 3 + xs ** 2 + xs')
1.43 ms ± 20.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Спасибо @ max9111 за указание на то, что numexpr упрощает степень умножения. Похоже, что большая часть расхождения в тесте объясняется оптимизацией xs ** 3.
In [421]: %timeit xs * xs
1.62 ms ± 12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [422]: %timeit xs ** 2
1.63 ms ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [423]: %timeit xs ** 3
22.8 ms ± 283 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [424]: %timeit xs * xs * xs
2.52 ms ± 58.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
В этом случае numexpr также упрощается умножение. Как показывает практика, на двойной ** uint требуется ок. В 40-60 раз длиннее умножения.