Эффективное 2D FFT реальных входных данных фиксированной длины в C/C++

Я разрабатываю алгоритм, который несколько раз вызывает функцию БПФ. У меня есть несколько ограничений по времени (желательно в режиме реального времени), поэтому мне нужно минимизировать время, затрачиваемое на каждый вызов БПФ.

Я работаю с библиотекой OpenCV и уже реализовал свой код двумя разными способами:

  • Использование библиотеки FFTW. Управление данными/памятью + БПФ (8 мс) = 14 мс (в среднем, флаг FFT_MEASURE).
  • Использование функции OpenCV fft. Управление данными/памятью + БПФ (21 мс) = 23 мс (в среднем).

Поскольку мои входные данные всегда фиксируются как реальное изображение размером 512x512 пикселей, как вы думаете, если я реализую алгоритм БПФ, основанный на математическом определении ДПФ, сохраняя таблицы синуса/косинуса, смогу ли я добиться лучшей производительности, или библиотека FFTW действительно сильно оптимизирован? Есть идеи получше?

Все идеи и предложения будут действительно оценены. На данный момент я не рассматриваю параллелизацию или реализацию GPU.

Спасибо

Обновление:

Система: процессор Intel Xeon 5130 2,0 ГГц в Windows 7, Visual Studio 10.0 и FFTW 3.3.3 (составлен по инструкциям на сайте), OpenCV 2.4.3.

Пример кода для вызова FFT с FFTW (вход: OpenCV Mat CV_32F (1 канал, тип с плавающей запятой), вывод OpenCV Mat CV_32FC2 (2 канала, тип с плавающей запятой):

float           *im_data;

fftwf_complex    *data_in;
fftwf_complex    *fft;      

fftwf_plan       plan_f;

int             i, j, k;

int height=I.rows;
int width=I.cols;
int N=height*width;


float* outdata = new float[2*N];
im_data = ( float* ) I.data;

data_in = ( fftwf_complex* )fftwf_malloc( sizeof( fftwf_complex ) * N );
fft     = ( fftwf_complex* )fftwf_malloc( sizeof( fftwf_complex ) * N );

plan_f = fftwf_plan_dft_2d( height , width , data_in , fft ,  FFTW_FORWARD ,  FFTW_MEASURE );

for(int i = 0,k=0; i < height; ++i) {
    float* row = I.ptr<float>(i);
    for(int j = 0; j < width; j++) {
        data_in[k][0]=(float)row[j];
        data_in[k][1] =(float)0.0;
        k++;
    }
} 

fftwf_execute( plan_f );

int width2=2*width;
// writing output matrix: RealFFT[0],ImaginaryFFT[0],RealFFT[1],ImaginaryFFT[1],...
for( i = 0, k = 0 ; i < height ; i++ ) {
    for( j = 0 ; j < width2 ; j++ ) {

        outdata[i * width2 + j] = ( float )fft[k][0];
        outdata[i * width2 + j+1] = ( float )fft[k][1];
        j++;
        k++;
    }
}

Mat fft_I(height,width,CV_32FC2,outdata);

fftwf_destroy_plan( plan_f );
fftwf_free( data_in );
fftwf_free( fft );


return fft_I;

person gui    schedule 04.12.2012    source источник
comment
Я пытался реализовать fft самостоятельно, используя таблицы sin\cos и другие оптимизации. Я действительно думаю, что единственный способ улучшить скорость fft самостоятельно и сделать ее быстрее, чем в таких библиотеках, как fftw, - это выполнить ее на аппаратном уровне. Они действительно знают, что делают.   -  person Arsenii Fomin    schedule 04.12.2012
comment
вы можете работать над управлением памятью, если размер фиксирован, вы можете повторно использовать один и тот же фрагмент памяти, не выполняя выделение на каждой итерации (при условии, что вам не нужно хранить старые изображения)   -  person Alessandro Teruzzi    schedule 04.12.2012
comment
Не рассчитывайте, что сможете так легко победить FFTW. Хотя это, безусловно, возможно (и я делал это раньше, потому что это то, чем я занимаюсь), вам не следует пытаться делать это, если у вас нет глубоких знаний о современном оборудовании, а также опыта работы с высокопроизводительными вычислениями.   -  person Mysticial    schedule 04.12.2012
comment
Что вы делаете в течение оставшихся 6 мс на этапе управления данными/памятью? Можно ли это улучшить (меньше копий данных, операций векторизации и т. д.)?   -  person Jason B    schedule 04.12.2012
comment
Я сделал все возможное, чтобы оптимизировать эти 6 мс в управлении данными/памятью, но я не эксперт в этой области, так что это наверняка можно улучшить. Я приведу пример своего кода в вопросе.   -  person gui    schedule 05.12.2012


Ответы (3)


Ваше время FFT с FFTW кажется очень высоким. Чтобы получить максимальную отдачу от FFTW с БПФ фиксированного размера, вы должны создать план с использованием флага FFTW_PATIENT, а затем в идеале сохранить сгенерированную «мудрость» для последующего повторного использования. Вы можете генерировать мудрость либо из собственного кода, либо с помощью инструмента fftw-wisdom.

person Paul R    schedule 04.12.2012
comment
с FFTW_PATIENT я получаю в среднем 7 мс для процессора Intel Xeon 5130 2,0 ГГц в Windows 7, Visual Studio 10.0 и FFTW 3.3.3 (составлено в соответствии с инструкциями на сайте). Как вы думаете, это все еще высоко? - person gui; 05.12.2012
comment
Да, это кажется немного высоким, но вы делаете сложное-сложное не на месте, что, вероятно, объясняет это. - person Paul R; 05.12.2012
comment
Если вам нужна более высокая производительность, попробуйте использовать реальное преобразование в сложное (и, если возможно, сделайте его на месте). - person Paul R; 05.12.2012
comment
Вы хотите не использовать тип данных float в качестве входных данных? - person gui; 05.12.2012
comment
Нет - используйте число с плавающей запятой, но используйте БПФ от реального к комплексному (r2c), то есть чисто реальный ввод, а не сложный - в настоящее время все ваши воображаемые входы равны 0, поэтому вы тратите около 50% вычислений БПФ. На месте означает, что вы используете один и тот же буфер для ввода и вывода, что также может повысить производительность. - person Paul R; 05.12.2012
comment
Я закодировал версию своего кода r2c (вне места), как вы предложили. Я доволен результатами (БПФ за 4 мс в среднем и 9 мс с памятью и управлением данными). Я приму этот ответ, спасибо. - person gui; 07.12.2012

БПФ из библиотеки Intel Math Kernel (отдельно от компилятора Intel) работает быстрее чем FFTW большую часть времени. Я не знаю, будет ли в вашем случае достаточно улучшения, чтобы оправдать цену.

Я соглашусь с другими, что создание вашего собственного БПФ, вероятно, не является хорошим использованием вашего времени (если только вы не хотите научиться это делать). Доступные реализации БПФ (FFTW, MKL) были так точно настроены в течение многих лет. Я не говорю, что вы не можете сделать лучше, но это, вероятно, потребует много работы и времени для незначительной выгоды.

person Jason B    schedule 04.12.2012
comment
Я обнаружил прямо противоположное при тестировании, по крайней мере, для двухмерных БПФ реального-сложного с размерами изображения в диапазоне от 512x512 до 2048x2048 на современных процессорах Intel (Core i7 et al) — FFTW превосходит Библиотеки Intel вполне удобны, особенно если вы потратите время на создание наилучших возможных планов. - person Paul R; 04.12.2012
comment
Хорошо, большая часть моего опыта связана с относительно длинными (> 32K) 1D FFT, где кажется, что MKL FFT быстрее. Я не пробовал 2D-БПФ, поэтому, думаю, я ошибочно предположил, что результаты будут справедливы для 2D-случая. - person Jason B; 05.12.2012

Поверьте мне, fftw действительно очень оптимизирован, очень мало шансов, что вы сможете сделать это лучше.

Какой компилятор вы использовали для компиляции fftw? Иногда компилятор от Intel дает лучшую производительность, чем gcc

person kobra    schedule 04.12.2012
comment
Я согласен с вами по поводу производительности FFTW, и это правда, что в целом ICC дает лучшие результаты, чем gcc для нормального кода, но для FFTW бабочки уже сильно оптимизированы и по моему опыту выбор компилятора мало на что влияет. - person Paul R; 04.12.2012