Я разместил это на центральном центральном процессоре Matlab, но не получил никаких ответов, поэтому решил, что переставлю здесь.
Недавно я написал простую процедуру в Matlab, которая использует FFT в for-loop; БПФ доминирует над расчетами. Я написал ту же процедуру в mex только для экспериментов, и она называет библиотеку FFTW 3.3. Оказывается, что процедура matlab работает быстрее, чем стандартная mex для очень больших массивов (примерно в два раза быстрее). Мексовая процедура использует мудрость и выполняет те же вычисления FFT. Я также знаю, что Matlab использует FFTW, но возможно ли, что их версия немного оптимизирована? Я даже использовал флаг FFTW_EXHAUSTIVE и его все еще примерно вдвое медленнее для больших массивов, чем аналог MATLAB. Кроме того, я гарантировал, что используемый мною Matlab был однопоточным с флагом "-singleCompThread", а файл mex, который я использовал, не был в режиме отладки. Любопытно, если это было так - или если есть некоторые оптимизации, которые использует Matlab под капотом, о котором я не знаю. Спасибо.
Здесь mex часть:
void class_cg_toeplitz::analysis() {
// This method computes CG iterations using FFTs
// Check for wisdom
if(fftw_import_wisdom_from_filename("cd.wis") == 0) {
mexPrintf("wisdom not loaded.\n");
} else {
mexPrintf("wisdom loaded.\n");
}
// Set FFTW Plan - use interleaved FFTW
fftw_plan plan_forward_d_buffer;
fftw_plan plan_forward_A_vec;
fftw_plan plan_backward_Ad_buffer;
fftw_complex *A_vec_fft;
fftw_complex *d_buffer_fft;
A_vec_fft = fftw_alloc_complex(n);
d_buffer_fft = fftw_alloc_complex(n);
// CREATE MASTER PLAN - Do this on an empty vector as creating a plane
// with FFTW_MEASURE will erase the contents;
// Use d_buffer
// This is somewhat dangerous because Ad_buffer is a vector; but it does not
// get resized so &Ad_buffer[0] should work
plan_forward_d_buffer = fftw_plan_dft_r2c_1d(d_buffer.size(),&d_buffer[0],d_buffer_fft,FFTW_EXHAUSTIVE);
plan_forward_A_vec = fftw_plan_dft_r2c_1d(A_vec.height,A_vec.value,A_vec_fft,FFTW_WISDOM_ONLY);
// A_vec_fft.*d_buffer_fft will overwrite d_buffer_fft
plan_backward_Ad_buffer = fftw_plan_dft_c2r_1d(Ad_buffer.size(),d_buffer_fft,&Ad_buffer[0],FFTW_EXHAUSTIVE);
// Get A_vec_fft
fftw_execute(plan_forward_A_vec);
// Find initial direction - this is the initial residual
for (int i=0;i<n;i++) {
d_buffer[i] = b.value[i];
r_buffer[i] = b.value[i];
}
// Start CG iterations
norm_ro = norm(r_buffer);
double fft_reduction = (double)Ad_buffer.size(); // Must divide by size of vector because inverse FFT does not do this
while (norm(r_buffer)/norm_ro > relativeresidual_cutoff) {
// Find Ad - use fft
fftw_execute(plan_forward_d_buffer);
// Get A_vec_fft.*fft(d) - A_vec_fft is only real, but d_buffer_fft
// has complex elements; Overwrite d_buffer_fft
for (int i=0;i<n;i++) {
d_buffer_fft[i][0] = d_buffer_fft[i][0]*A_vec_fft[i][0]/fft_reduction;
d_buffer_fft[i][1] = d_buffer_fft[i][1]*A_vec_fft[i][0]/fft_reduction;
}
fftw_execute(plan_backward_Ad_buffer);
// Calculate r'*r
rtr_buffer = 0;
for (int i=0;i<n;i++) {
rtr_buffer = rtr_buffer + r_buffer[i]*r_buffer[i];
}
// Calculate alpha
alpha = 0;
for (int i=0;i<n;i++) {
alpha = alpha + d_buffer[i]*Ad_buffer[i];
}
alpha = rtr_buffer/alpha;
// Calculate new x
for (int i=0;i<n;i++) {
x[i] = x[i] + alpha*d_buffer[i];
}
// Calculate new residual
for (int i=0;i<n;i++) {
r_buffer[i] = r_buffer[i] - alpha*Ad_buffer[i];
}
// Calculate beta
beta = 0;
for (int i=0;i<n;i++) {
beta = beta + r_buffer[i]*r_buffer[i];
}
beta = beta/rtr_buffer;
// Calculate new direction vector
for (int i=0;i<n;i++) {
d_buffer[i] = r_buffer[i] + beta*d_buffer[i];
}
*total_counter = *total_counter+1;
if(*total_counter >= iteration_cutoff) {
// Set total_counter to -1, this indicates failure
*total_counter = -1;
break;
}
}
// Store Wisdom
fftw_export_wisdom_to_filename("cd.wis");
// Free fft alloc'd memory and plans
fftw_destroy_plan(plan_forward_d_buffer);
fftw_destroy_plan(plan_forward_A_vec);
fftw_destroy_plan(plan_backward_Ad_buffer);
fftw_free(A_vec_fft);
fftw_free(d_buffer_fft);
};
Здесь часть matlab:
% Take FFT of A_vec.
A_vec_fft = fft(A_vec); % Take fft once
% Find initial direction - this is the initial residual
x = zeros(n,1); % search direction
r = zeros(n,1); % residual
d = zeros(n+(n-2),1); % search direction; pad to allow FFT
for i = 1:n
d(i) = b(i);
r(i) = b(i);
end
% Enter CG iterations
total_counter = 0;
rtr_buffer = 0;
alpha = 0;
beta = 0;
Ad_buffer = zeros(n+(n-2),1); % This holds the product of A*d - calculate this once per iteration and using FFT; only 1:n is used
norm_ro = norm(r);
while(norm(r)/norm_ro > 10^-6)
% Find Ad - use fft
Ad_buffer = ifft(A_vec_fft.*fft(d));
% Calculate rtr_buffer
rtr_buffer = r'*r;
% Calculate alpha
alpha = rtr_buffer/(d(1:n)'*Ad_buffer(1:n));
% Calculate new x
x = x + alpha*d(1:n);
% Calculate new residual
r = r - alpha*Ad_buffer(1:n);
% Calculate beta
beta = r'*r/(rtr_buffer);
% Calculate new direction vector
d(1:n) = r + beta*d(1:n);
% Update counter
total_counter = total_counter+1;
end
В терминах времени для N = 50000 и b = 1: n требуется около 10,5 секунд с mex и 4,4 секунды с помощью matlab. Я использую R2011b. Благодаря
Несколько замечаний, а не определенный ответ, поскольку я не знаю какой-либо специфики реализации MATLAB FFT:
Предполагаю, что вы уже рассмотрели вторую проблему и количество итераций сопоставимо. (Если это не так, это, скорее всего, некоторые вопросы точности и заслуживающие дальнейшего изучения.)
Теперь, если сравнивать скорость FFT:
Это классическое увеличение производительности благодаря оптимизации на уровне низкого уровня и архитектуры.
Matlab использует FFT из библиотеки Intel MKL (Math Kernel Library) (mkl.dll). Эти подпрограммы оптимизированы (на уровне сборки) процессорами Intel для Intel. Даже на AMD это, похоже, дает хорошие повышения производительности.
FFTW выглядит как обычная библиотека c, которая не оптимизирована. Следовательно, усиление производительности для использования MKL.
version('-fftw')
показывает FFTW-3.3.3-sse2-avx
. В папке bin MATLAB есть две общие библиотеки, которые экспортируют интерфейс API FFTW: libmwfftw3.dll
и libmwfftw3f.dll
(в дополнение к третьей lib libmwmfl_fft.dll
построенной поверх двух предыдущих, предназначенной для абстрагирования использования планов FFTW) , Поэтому, хотя MATLAB использует Intel MKL в качестве оптимизированной реализации BLAS / LAPACK, насколько я могу судить, он не вызывает интерфейс FFTW из MKL.
EDIT: @wakjah ответ на этот ответ верен: FFTW поддерживает разделение реального и мнимого хранилища памяти через интерфейс Guru. Таким образом, мои претензии в отношении взлома неточны, но могут очень хорошо применяться, если интерфейс FFTW Guru не используется - по умолчанию, так что остерегайтесь!
Во-первых, извините за то, что вы опаздываете на год. Я не уверен, что увеличение скорости, которое вы видите, происходит от MKL или других оптимизаций. Между FFTW и Matlab существует нечто принципиально иное, и именно так хранятся сложные данные в памяти.
В Matlab действительная и мнимая части комплексного вектора X представляют собой отдельные массивы Xre [i] и Xim [i] (линейные по памяти, эффективные при работе на любом из них отдельно).
В FFTW действительная и мнимая части чередуются как двойные [2] по умолчанию, т.е. X [i] [0] - действительная часть, а X [i] [1] - мнимая часть.
Таким образом, чтобы использовать библиотеку FFTW в mex файлах, нельзя использовать массив Matlab напрямую, но сначала необходимо выделить новую память, а затем упаковать входные данные из Matlab в формат FFTW, а затем распаковать вывод из FFTW в формат Matlab. то есть.
X = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
Y = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
затем
for (size_t i=0; i<N; ++i) {
X[i][0] = Xre[i];
X[i][1] = Xim[i];
}
затем
for (size_t i=0; i<N; ++i) {
Yre[i] = Y[i][0];
Yim[i] = Y[i][1];
}
Следовательно, для этого требуются 2x выделения памяти + 4x чтение + 4x записи - весь размер N. Это требует больших сбоев при больших проблемах.
У меня есть подозрение, что Mathworks, возможно, взломал код FFTW3, чтобы он мог читать входные векторы непосредственно в формате Matlab, что позволяет избежать всего вышеперечисленного.
В этом случае можно выделить X и использовать X для Y для запуска FFTW на месте (как fftw_plan_*(N, X, X, ...)
вместо fftw_plan_*(N, X, Y, ...)
), так как он будет скопирован в вектор Yre и Yim Matlab, если только приложение требует/выгоды от хранения X и Y отдельно.
EDIT. Рассматривая потребление памяти в режиме реального времени при запуске Matlab fft2() и моего кода на основе библиотеки fftw3, он показывает, что Matlab выделяет только один дополнительный сложный массив (вывод), тогда как моему коду нужны два таких массива (буфер *fftw_complex
плюс вывод Matlab). Преобразование на месте между форматами Matlab и fftw невозможно, поскольку реальные и мнимые массивы Matlab не являются последовательными в памяти. Это говорит о том, что Mathworks взломал библиотеку fftw3 для чтения/записи данных с использованием формата Matlab.
Еще одна оптимизация для нескольких вызовов заключается в постоянном распределении (используя mexMakeMemoryPersistent()
). Я не уверен, что реализация Matlab также делает это.
Приветствия.
p.s. В качестве побочного примечания, формат хранения сложных данных Matlab более эффективен для работы на реальных или мнимых векторах отдельно. В формате FFTW вам нужно будет читать + 2 чтения.
Я нашел следующий комментарий на веб-сайте MathWorks [1]:
Замечание о больших степенях 2: для измерений FFT, которые являются степенями 2, между 2 ^ 14 и 2 ^ 22, программное обеспечение MATLAB использует специальные предустановленные информацию во внутренней базе данных для оптимизации вычисления БПФ. Никакая настройка не выполняется, когда размер FTT составляет 2, если вы не очистите базу данных с помощью команды fftw ( "мудрость", []).
Хотя это относится к степеням 2, он может напомнить, что MATLAB использует свою собственную "особую мудрость" при использовании FFTW для определенных (больших) размеров массива. Рассмотрим: 2 ^ 16 = 65536.
[1] R2013b Документация доступна из http://www.mathworks.de/de/help/matlab/ref/fftw.html (доступ к ней 29 октября 2013 года)