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 fornblocks
blocks using thefft
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
, andfftw3f_threads
libraries when compiling.Can define the compiler flag
USE_VOLK
to use thevolk
library (seefft.hpp
).g++ test_fft.cpp -o test_fft -luhd -lboost_program_options -lfftw3f -lfftw3f_threads -DUSE_VOLK -lvolk
3.6.2.3. Tests#
Experiment
Use the default values of
fftsize
andnblocks
. Run the program with increasing numbers of threads, starting from one thread, to see how much speed advantage you can obtain.Redo 1. with different values of
fftsize
andnblocks
to see the effects of the parameters on the speed.Recompile without defining the
USE_VOLK
flag. Run the program again to see how much reduction in speed results.