Вы рассчитали, сколько времени требуется, чтобы выделить a
? В массив 10000 x 100000 float64 входит 8 ГБ памяти.
a = np.ones((10000, 100000), np.double)
занимает 6 секунд на моем ноутбуке с 16 ГБ оперативной памяти. Если у вас нет 8 ГБ бесплатно, вы попадете на своп, и дольше будет лот. Так как func
тратит почти все свое время, просто выделяя a
, поэтому параллельная внешняя петля for
может получить лишь небольшое дробное улучшение в общей продолжительности выполнения.
Чтобы продемонстрировать это, я изменил вашу функцию, чтобы принять a
в качестве входа. В tmp.pyx
:
#cython: boundscheck=False, wraparound=False, initializedcheck=False
from cython.parallel cimport prange
def serial(double[:, :] a):
cdef:
double total = 0
int i, j
for i in range(a.shape[0]):
for j in range(a.shape[1]):
total += a[i, j]
return total
def parallel(double[:, :] a):
cdef:
double total = 0
int i, j
for i in prange(a.shape[0], nogil=True, schedule='static', chunksize=1):
for j in range(a.shape[1]):
total += a[i, j]
return total
Например:
In [1]: import tmp
In [2]: r, c = 10000, 100000
In [3]: a = np.random.randn(r, c) # this takes ~6.75 sec
In [4]: %timeit tmp.serial(a)
1 loops, best of 3: 1.25 s per loop
In [5]: %timeit tmp.parallel(a)
1 loops, best of 3: 450 ms per loop
Параллелизация функция дала о 2.8x скорости вверх * на моем ноутбуке с 4 ядрами, но это лишь малая часть времени, необходимого выделить a
.
Урок здесь состоит в том, чтобы всегда прокомментировать ваш код, чтобы понять, где он проводит большую часть своего времени, прежде чем погрузиться в оптимизацию.
* Вы могли бы сделать немного лучше, пропуская большие куски a
для каждого рабочего процесса, например, путем увеличения chunksize
или с использованием schedule='guided'
Сначала отключите 'boundschecking', поэкспериментируйте с разными типами расписаний и chunksizes и посмотрите, что вы получаете. Посмотрите на [this] (http://stackoverflow.com/questions/33193851/cython-prange-slower-for-4-threads-then-with-range/33204449#33204449) вопрос. Вы выполняете 'sum-reduction', а компиляторы отлично справляются с оптимизацией таких циклов в последовательном режиме и с помощью« OpenMP »pragmas, приведенный код сборки может быть не таким оптимальным в этом случае. – romeric
@romeric, попробовал, результаты не лучше. – avocado