Для решения PDE (уравнения Шредингера) мне нужно вычислить оператор Лапласа в трех измерениях. Мое текущее решение таково (часть кода, которая требует больше всего времени):
for n in range(Ntstep): # loop
for i in range(self.Nixyz[0]): # internal levels of wavefunction
wf.psi[i,:,:,:]=self.expu * wf.psi[i,:,:,:] # potential
if n < Ntstep - 1: # compute laplacian in 3d
wf.psi[i,:,:,:]=\
sf.ifft(self.expkx*sf.fft(wf.psi[i,:,:,:],
axis=0,**fft_args),axis=0,**fft_args)
wf.psi[i,:,:,:]=\
sf.ifft(self.expky*sf.fft(wf.psi[i,:,:,:],
axis=1,**fft_args),axis=1,**fft_args)
wf.psi[i,:,:,:]=\
sf.ifft(self.expkz*sf.fft(wf.psi[i,:,:,:],
axis=2,**fft_args),axis=2,**fft_args)
Чтобы добиться большей производительности, я пробовал / делал / думал о следующем:
Не выполняйте 3D БПФ напрямую. Лапласиан разделим и, следовательно, может быть разделен на три одномерных БПФ, что должно снизить сложность с
n^3
до3n
. (Выполнено в коде выше.)Я скомпилировал numpy и scipy для MKL в надежде получить некоторую производительность, особенно надеясь включить многопоточные вычисления. Для некоторых операций используется несколько потоков (умножение матрицы на вектор), но ни numpy.fft, ни scipy.fftpack не используют несколько ядер.
Я скомпилировал libfftw и pyfftw и использовал его как замену np / sp. У меня Intel Core i7-3770K, то есть четыре ядра и восемь потоков. Я получаю примерно вдвое большую производительность по сравнению с np / sp при использовании двух или четырех потоков с fftw. Один или более четырех потоков по какой-то причине работают медленнее.
Итак, мои основные вопросы в основном сейчас:
Можно ли распараллелить БПФ (W) таким образом, чтобы производительность масштабировалась с количеством доступных ядер / потоков? Если да, что мне нужно учесть? В настоящее время мне кажется, что от двух до четырех потоков лучше всего. Более (или менее) медленнее, хотя на моем процессоре доступно восемь потоков.
Стоит ли мне попытаться распараллелить свой код Python? Например. поместите три одномерных БПФ на три разных ядра. Конечно, я должен убедиться, что я не читаю и не записываю одну и ту же переменную в разных потоках одновременно, поэтому мне нужны дополнительные временные переменные в приведенном выше коде, например:
- Thread 1: TempA = FFT(psi..., axis=0)
- Поток 2: TempB = FFT (psi ..., ось = 1)
- Резьба 3: TempC = FFT (psi ..., ось = 1)
- Последний шаг: psi = TempA + TempB + TempC
БПФ для
axis=0
занимает вдвое (!) Больше времени, чем для других осей. Можно ли избавиться от этой разницы и сделать все БПФ одинаково быстрыми?(Новое) Является ли подход БПФ лучшим выбором, или подход пользователя Рори, основанный на конечных различиях, всегда лучше, по крайней мере, с точки зрения производительности?
Я думаю, что эффективное вычисление лапласиана - это тема, которая широко исследована, поэтому даже некоторые ссылки или подсказки к статьям, книгам и т. Д. Могут быть полезны.