Почему FFTW в Windows быстрее, чем в Linux? - PullRequest
2 голосов
/ 31 декабря 2011

Я написал две одинаковые программы в Linux и Windows, используя библиотеки fftw (fftw3.a, fftw3.lib), и вычислил продолжительность оператора fftwf_execute(m_wfpFFTplan) (16-fft).

Для 10000 пробегов:

  • В Linux: среднее время составляет 0,9
  • В Windows: среднее время составляет 0,12

Меня смущает, почему в Windows это в девять раз быстрее, чем в Linux.

Процессор: Intel (R) Core (TM) i7 CPU 870 @ 2,93 ГГц

Каждая ОС (Windows XP 32-битная и Linux OpenSUSE 11.4 32-битная) установлена ​​на одних и тех же машинах.

Я скачал fftw.lib (для Windows) из интернета и не знаю, какие конфигурации. Однажды я собрал FFTW с этим конфигом:

/configure --enable-float  --enable-threads --with-combined-threads  --disable-fortran  --with-slow-timer  --enable-sse  --enable-sse2  --enable-avx   

в Linux, и это приводит к тому, что библиотека в четыре раза быстрее стандартных конфигураций (0,4 мс).

1 Ответ

7 голосов
/ 31 декабря 2011

16 БПФ очень мало. Вы обнаружите, что FFT меньше, чем, скажем, 64, будут жестко запрограммированными ассемблерами без циклов, чтобы получить максимально возможную производительность. Это означает, что они могут быть очень восприимчивы к изменениям в наборах команд, оптимизации компилятора, даже к 64 или 32-битным словам.

Что происходит, когда вы запускаете тест с размерами БПФ от 16 до 1048576 со степенью 2? Я говорю это как особая жестко запрограммированная процедура asm в Linux, возможно, не лучшая оптимизированная для вашей машины, тогда как вам, возможно, повезло с реализацией Windows для этого конкретного размера. Сравнение всех размеров в этом диапазоне даст вам лучшее представление о производительности Linux и Windows.

Вы калибровали FFTW? При первом запуске FFTW определяет самую быструю реализацию для каждой машины, однако, если у вас есть специальные наборы команд, или кэш определенного размера, или другие функции процессора, это может оказать существенное влияние на скорость выполнения. В результате выполнения калибровки будет проверяться скорость различных подпрограмм FFT и выбираться самый быстрый в зависимости от размера для вашего конкретного оборудования. Калибровка включает в себя многократное вычисление планов и сохранение сгенерированного файла FFTW «Мудрость». Сохраненные данные калибровки (это длительный процесс) могут быть использованы повторно. Я предлагаю сделать это один раз, когда ваше программное обеспечение запускается и повторно использовать файл каждый раз. Я заметил улучшение производительности в 4-10 раз для определенных размеров после калибровки!

Ниже приведен фрагмент кода, который я использовал для калибровки FFTW для определенных размеров. Обратите внимание, что этот код дословно вставлен из библиотеки DSP, над которой я работал, поэтому некоторые вызовы функций относятся к моей библиотеке. Я надеюсь, что конкретные вызовы FFTW будут полезны.

// Calibration FFTW
void DSP::forceCalibration(void)
{
// Try to import FFTw Wisdom for fast plan creation
FILE *fftw_wisdom = fopen("DSPDLL.ftw", "r");

// If wisdom does not exist, ask user to calibrate
if (fftw_wisdom == 0)
{
    int iStatus2 = AfxMessageBox("FFTw not calibrated on this machine."\
        "Would you like to perform a one-time calibration?\n\n"\
        "Note:\tMay take 40 minutes (on P4 3GHz), but speeds all subsequent FFT-based filtering & convolution by up to 100%.\n"\
        "\tResults are saved to disk (DSPDLL.ftw) and need only be performed once per machine.\n\n"\
        "\tMAKE SURE YOU REALLY WANT TO DO THIS, THERE IS NO WAY TO CANCEL CALIBRATION PART-WAY!", 
        MB_YESNO | MB_ICONSTOP, 0);

    if (iStatus2 == IDYES)
    {
        // Perform calibration for all powers of 2 from 8 to 4194304
        // (most heavily used FFTs - for signal processing)
        AfxMessageBox("About to perform calibration.\n"\
            "Close all programs, turn off your screensaver and do not move the mouse in this time!\n"\
            "Note:\tThis program will appear to be unresponsive until the calibration ends.\n\n"
            "\tA MESSAGEBOX WILL BE SHOWN ONCE THE CALIBRATION IS COMPLETE.\n");
        startTimer();

        // Create a whole load of FFTw Plans (wisdom accumulates automatically)
        for (int i = 8; i <= 4194304; i *= 2)
        {
            // Create new buffers and fill
            DSP::cFFTin = new fftw_complex[i];
            DSP::cFFTout = new fftw_complex[i];
            DSP::fconv_FULL_Real_FFT_rdat = new double[i];
            DSP::fconv_FULL_Real_FFT_cdat = new fftw_complex[(i/2)+1];
            for(int j = 0; j < i; j++)
            {
                DSP::fconv_FULL_Real_FFT_rdat[j] = j;
                DSP::cFFTin[j][0] = j;
                DSP::cFFTin[j][1] = j;
                DSP::cFFTout[j][0] = 0.0;
                DSP::cFFTout[j][1] = 0.0;
            }

            // Create a plan for complex FFT.
            // Use the measure flag to get the best possible FFT for this size
            // FFTw "remembers" which FFTs were the fastest during this test. 
            // at the end of the test, the results are saved to disk and re-used
            // upon every initialisation of the DSP Library
            DSP::pCF = fftw_plan_dft_1d
                (i, DSP::cFFTin, DSP::cFFTout, FFTW_FORWARD, FFTW_MEASURE);

            // Destroy the plan
            fftw_destroy_plan(DSP::pCF);

            // Create a plan for real forward FFT
            DSP::pCF = fftw_plan_dft_r2c_1d
                (i, fconv_FULL_Real_FFT_rdat, fconv_FULL_Real_FFT_cdat, FFTW_MEASURE);

            // Destroy the plan
            fftw_destroy_plan(DSP::pCF);

            // Create a plan for real inverse FFT
            DSP::pCF = fftw_plan_dft_c2r_1d
                (i, fconv_FULL_Real_FFT_cdat, fconv_FULL_Real_FFT_rdat, FFTW_MEASURE);

            // Destroy the plan
            fftw_destroy_plan(DSP::pCF);

            // Destroy the buffers. Repeat for each size
            delete [] DSP::cFFTin;
            delete [] DSP::cFFTout;
            delete [] DSP::fconv_FULL_Real_FFT_rdat;
            delete [] DSP::fconv_FULL_Real_FFT_cdat;
        }

        double time = stopTimer();

        char * strOutput;
        strOutput = (char*) malloc (100);
        sprintf(strOutput, "DSP.DLL Calibration complete in %d minutes, %d seconds\n"\
            "Please keep a copy of the DSPDLL.ftw file in the root directory of your application\n"\
            "to avoid re-calibration in the future\n", (int)time/(int)60, (int)time%(int)60);
        AfxMessageBox(strOutput);

        isCalibrated = 1;

        // Save accumulated wisdom
        char * strWisdom = fftw_export_wisdom_to_string();  
        FILE *fftw_wisdomsave = fopen("DSPDLL.ftw", "w");
        fprintf(fftw_wisdomsave, "%s", strWisdom);

        fclose(fftw_wisdomsave);
        DSP::pCF = NULL;
        DSP::cFFTin = NULL;
        DSP::cFFTout = NULL;
        fconv_FULL_Real_FFT_cdat = NULL;
        fconv_FULL_Real_FFT_rdat = NULL;
        free(strOutput);
    }
}
else 
{
    // obtain file size.
    fseek (fftw_wisdom , 0 , SEEK_END);
    long lSize = ftell (fftw_wisdom);
    rewind (fftw_wisdom);

    // allocate memory to contain the whole file.
    char * strWisdom = (char*) malloc (lSize);

    // copy the file into the buffer.
    fread (strWisdom,1,lSize,fftw_wisdom);

    // import the buffer to fftw wisdom
    fftw_import_wisdom_from_string(strWisdom);

    fclose(fftw_wisdom);
    free(strWisdom);

    isCalibrated = 1;

    return;
}
}

Секретный соус заключается в создании плана с использованием флага FFTW_MEASURE, который конкретно измеряет сотни подпрограмм, чтобы найти самый быстрый для вашего конкретного типа FFT (реальный, сложный, 1D, 2D) и размера:

DSP::pCF = fftw_plan_dft_1d (i, DSP::cFFTin, DSP::cFFTout, 
   FFTW_FORWARD, FFTW_MEASURE);

Наконец, все тесты производительности также должны выполняться с одним этапом плана FFT вне выполнения, вызываемого из кода, который компилируется в режиме выпуска с оптимизацией и отсоединяется от отладчика. Тесты должны выполняться в цикле со многими тысячами (или даже миллионами) итераций, а затем принимать среднее время выполнения для вычисления результата. Как вы, вероятно, знаете, этап планирования занимает значительное количество времени, и выполнение рассчитано на многократное выполнение с одним планом.

...