3.6.2. test_fft.cpp#

3.6.2.1. Source code#

  1// University of Florida EEL6528
  2// Tan F. Wong
  3// Jan 18, 2021
  4
  5#include <uhd/utils/safe_main.hpp>
  6#include <boost/program_options.hpp>
  7#include <uhd/utils/thread.hpp>
  8#include <csignal>
  9#include <chrono>
 10#include "fft.hpp"
 11
 12namespace po = boost::program_options;
 13namespace clk = std::chrono;
 14
 15static bool stop_signal_called = false;
 16void sig_int_handler(int){
 17    stop_signal_called = true;
 18}
 19
 20int UHD_SAFE_MAIN(int argc, char *argv[]){
 21
 22    // Set main thread priority to highest
 23    uhd::set_thread_priority_safe(1.0, false);
 24 
 25    //variables to be set by po
 26    int fftsize, nblocks, nthreads;
 27 
 28    //setup the program options
 29    po::options_description desc("Allowed options");
 30    desc.add_options()
 31        ("help", "help message")
 32        ("fftsize,n", po::value<int>(&fftsize)->default_value(1024), "FFT size")
 33        ("nblocks,b", po::value<int>(&nblocks)->default_value(1000), "Number of FFT blocks to calculate")
 34        ("nthreads,t", po::value<int>(&nthreads)->default_value(1), "Number of threads to use")
 35    ;
 36 
 37    po::variables_map vm;
 38    po::store(po::parse_command_line(argc, argv, desc), vm);
 39    po::notify(vm);
 40 
 41    //print the help message
 42    if (vm.count("help")){
 43        std::cout << desc << std::endl;
 44        return EXIT_SUCCESS;
 45    }
 46
 47    std::signal(SIGINT, &sig_int_handler);
 48
 49    // Instantiate forward FFT (1st false) and allocate memory for input array
 50    fft fwd(nthreads, fftsize, nblocks, fftsize, fftsize, false);
 51    // Instantiate inverse FFT (1st true)
 52    // DON'T allocate memory for input array but set it to fwd's output 
 53    fft inv(nthreads, fftsize, nblocks, fftsize, fftsize, true, fwd.get_out());
 54
 55    // Set random number generator seed
 56    srand(time(NULL));
 57    std::complex<float>* in = fwd.get_in();
 58    std::complex<float>* out = inv.get_out();
 59
 60    // Initial timing statistics
 61    double avg_fft_time = 0.0;
 62    double avg_ifft_time = 0.0;
 63    unsigned count = 0;
 64    while (not stop_signal_called) {
 65        // Generate samples with random complex-values
 66        for (int i=0; i<fftsize*nblocks; i++) {
 67            in[i] = std::complex<float>(float(rand())/float(RAND_MAX), float(rand())/float(RAND_MAX));
 68        }
 69        // Execute forward FFT
 70        clk::steady_clock::time_point start_fft = clk::steady_clock::now();
 71        fwd.calculate();
 72        clk::steady_clock::time_point done_fft = clk::steady_clock::now();
 73        // Execute inverse FFT
 74        clk::steady_clock::time_point start_ifft = clk::steady_clock::now();
 75        inv.calculate();
 76        clk::steady_clock::time_point done_ifft = clk::steady_clock::now();
 77        // Get the time required to calculate FFT and IFFT
 78        clk::microseconds fft_time = clk::duration_cast<clk::microseconds>(done_fft-start_fft);
 79        clk::microseconds ifft_time = clk::duration_cast<clk::microseconds>(done_ifft-start_ifft);
 80        
 81        // Check whether doing forward and then inverse FFT gives back the samples
 82        float diff = 0.0;
 83        for (int i=0; i<fftsize*nblocks; i++) {
 84            diff += std::norm(in[i]-out[i]);
 85        }
 86        diff /= nblocks*fftsize;
 87        std::cout << "Squared difference per sample = " << diff << std::endl;
 88        if (diff <= 1e-10)
 89            std::cout << "FFT has worked correctly!" << std::endl;
 90        else
 91            std::cout << "FFT has failed!\n\n"<< std::endl;
 92        std::cout << "It took " << fft_time.count() << " us to compute " << nblocks << " blocks of " << fftsize <<"-point FFT\n";
 93        std::cout << "It took " << ifft_time.count() << " us to compute " << nblocks << " blocks of " << fftsize <<"-point IFFT\n\n";
 94        // Accummulate timing stats
 95        count++;
 96        avg_fft_time += fft_time.count();
 97        avg_ifft_time += ifft_time.count();
 98        
 99        std::this_thread::sleep_for(std::chrono::milliseconds(10)); 
100    }
101
102    std::cout << std::endl;
103    std::cout << "Average time to compute " << nblocks << " blocks of " << fftsize <<"-point FFT is " << avg_fft_time/count << " us\n" ;
104    std::cout << "Average time to compute " << nblocks << " blocks of " << fftsize <<"-point IFFT is " << avg_ifft_time/count << " us\n\n" ;
105
106    fft::cleanup();
107    return EXIT_SUCCESS;
108}
  • This test program do fftsize-point FFT and then IFFT on each block of samples for nblocks blocks using the fft wrapper class.

  • It also estimates the average run times for the FFT and IFFT calculations.

3.6.2.2. Build#

  • Need to link the uhd, boost_program_options, fftw3f, and fftw3f_threads libraries when compiling.

  • Can define the compiler flag USE_VOLK to use the volk library (see fft.hpp).

    g++ test_fft.cpp -o test_fft -luhd -lboost_program_options -lfftw3f -lfftw3f_threads -DUSE_VOLK -lvolk 
    

3.6.2.3. Tests#

Experiment

  1. Use the default values of fftsize and nblocks. Run the program with increasing numbers of threads, starting from one thread, to see how much speed advantage you can obtain.

  2. Redo 1. with different values of fftsize and nblocks to see the effects of the parameters on the speed.

  3. Recompile without defining the USE_VOLK flag. Run the program again to see how much reduction in speed results.