Mempercepat komputasi FFT yang jarang

Saya berharap seseorang dapat meninjau kode saya di bawah ini dan memberikan petunjuk bagaimana mempercepat bagian antara tic dan toc. Fungsi di bawah ini mencoba menjalankan IFFT lebih cepat daripada fungsi bawaan Matlab karena (1) hampir semua bin koefisien fft adalah nol (yaitu bin 10 hingga 1000 dari bin 10M hingga 300M bukan nol), dan (2 ) hanya sepertiga bagian tengah dari hasil IFFT yang dipertahankan (sepertiga pertama dan terakhir dibuang -- jadi tidak perlu menghitungnya terlebih dahulu).

Variabel masukannya adalah:

fftcoef = complex fft-coef 1D array (10 to 1000 pts long)
bins = index of fft coefficients corresponding to fftcoef (10 to 1000 pts long)
DATAn = # of pts in data before zero padding and fft (in range of 10M to 260M)
FFTn = DATAn + # of pts used to zero pad before taking fft (in range of 16M to 268M) (e.g. FFTn = 2^nextpow2(DATAn))

Saat ini, kode ini membutuhkan waktu beberapa kali lipat lebih lama daripada pendekatan fungsi ifft Matlab yang menghitung seluruh spektrum kemudian membuang 2/3-nya. Misalnya, jika data input untuk fftcoef dan bins adalah 9x1 array (yaitu hanya 9 koefisien fft kompleks per sideband; 18 pts ketika mempertimbangkan kedua sideband), dan DATAn=32781534, FFTn=33554432 (yaitu 2^25), maka pendekatan ifft memerlukan waktu 1.6 detik sedangkan loop di bawah membutuhkan waktu lebih dari 700 detik.

Saya telah menghindari penggunaan matriks untuk membuat vektorisasi loop nn karena terkadang ukuran array untuk fftcoef dan bins bisa mencapai panjang 1000 pts, dan matriks 260Mx1K akan terlalu besar untuk memori kecuali jika matriks tersebut dapat dipecah.

Setiap saran sangat dihargai! Terima kasih sebelumnya.

function fn_fft_v1p0(fftcoef, bins, DATAn, FFTn)

fftcoef = [fftcoef; (conj(flipud(fftcoef)))];     % fft coefficients
bins = [bins; (FFTn - flipud(bins) +2)];          % corresponding fft indices for fftcoef array

ttrend = zeros( (round(2*DATAn/3) - round(DATAn/3) + 1), 1); % preallocate

start = round(DATAn/3)-1;

tic;
for nn = start+1 : round(2*DATAn/3)  % loop over desired time indices
  % sum over all fft indices having non-zero coefficients
  arg = 2*pi*(bins-1)*(nn-1)/FFTn;
  ttrend(nn-start) = sum( fftcoef.*( cos(arg) + 1j*sin(arg)); 
end
toc;

end

person ggkmath    schedule 25.01.2011    source sumber
comment
Lihat fftw.org/pruned.html untuk analisis potensi penghematan. Ini mungkin tidak sepadan.   -  person mtrw    schedule 25.01.2011
comment
Anda sedang melihat operasi length(bins)*(2*DATAn/3), lebih baik daripada DATAn*lg(DATAn) untuk pendekatan FFT jika 2*length(bins)/3 > lg(DAtan) (karena FFTW menangani ukuran transformasi non-kekuatan-2, saya mengabaikan bantalan nol). Untuk kasus 10 bin dan 2^25 titik keluaran, itu adalah '20/3 › 25', faktor potensial dari 3 peningkatan. Segera setelah Anda mencapai 75 koefisien FFT, Anda kehilangan keuntungan. Dan Anda harus mengkodekan algoritma dalam C dan memeliharanya.   -  person mtrw    schedule 25.01.2011
comment
Terima kasih mtrw, saya meninjau tautan di atas beberapa hari yang lalu. Ini awalnya memberi saya harapan karena menyatakan: Karena itu, saya tidak akan merekomendasikan repot-repot mempertimbangkan FFT 1 hari yang dipangkas kecuali Anda menginginkan 1% dari output atau kurang (dan/atau jika 1% atau kurang dari input Anda bukan nol). Dalam kasus saya, kurang dari 0,00001% masukan saya (koefisien terhadap IDFT) bukan nol. Saya pikir ini harus menjadi alasan dominan peningkatan kecepatan, bukan faktor peningkatan 3 yang Anda kutip di atas.   -  person ggkmath    schedule 25.01.2011


Jawaban (2)


Anda harus ingat bahwa Matlab menggunakan perpustakaan fft yang dikompilasi (http://www.fftw.org/ ) untuk fungsi fftnya, yang selain beroperasi lebih cepat dibandingkan skrip Matlab, juga dioptimalkan untuk banyak kasus penggunaan. Jadi langkah pertama mungkin menulis kode Anda dalam c/c++ dan mengkompilasinya sebagai file mex yang dapat Anda gunakan dalam Matlab. Itu pasti akan mempercepat kode Anda setidaknya satu urutan besarnya (mungkin lebih).

Selain itu, salah satu optimasi sederhana yang bisa Anda lakukan adalah dengan mempertimbangkan 2 hal:

  1. Anda berasumsi deret waktu Anda bernilai nyata, sehingga Anda dapat menggunakan simetri koefisien fft.
  2. Deret waktu Anda biasanya lebih panjang daripada vektor koefisien fft Anda, jadi lebih baik melakukan iterasi melalui bin daripada titik waktu (sehingga membuat vektor menjadi vektor yang lebih panjang).

Kedua poin ini diterjemahkan ke loop berikut:

nn=(start+1 : round(2*DATAn/3))';
ttrend2 = zeros( (round(2*DATAn/3) - round(DATAn/3) + 1), 1);
tic;
for bn = 1:length(bins)
     arg = 2*pi*(bins(bn)-1)*(nn-1)/FFTn; 
     ttrend2 = ttrend2 +  2*real(fftcoef(bn) * exp(i*arg)); 
end
toc;

Perhatikan bahwa Anda harus menggunakan loop ini sebelum memperluas bins dan fftcoef, karena simetri sudah diperhitungkan. Loop ini membutuhkan waktu 8,3 detik untuk dijalankan dengan parameter dari pertanyaan Anda, sementara di komputer saya dibutuhkan 141,3 detik untuk dijalankan dengan kode Anda.

person Itamar Katz    schedule 25.01.2011
comment
Hai Itamar Katz, saran bagus. Saya mendapatkan nomor yang sama seperti yang Anda tunjukkan di atas untuk perbaikan menggunakan saran #2 di atas dan kode yang diberikan. Saya tidak yakin bagaimana cara mengkodekan saran Anda #1 di atas (untuk memanfaatkan simetri). Bisakah Anda menjelaskan lebih lanjut? - person ggkmath; 25.01.2011
comment
Oh begitu, Anda sudah memasukkan saran 1 dengan memasukkan 2*kode asli di atas, lalu saya cukup mengomentari dua baris asli di atas untuk fftcoef=[...] dan bins=[...]. Trik yang bagus, ini memotong waktu menjadi dua. Maka, tidak ada alasan untuk memperluas bins dan fftcoef. - person ggkmath; 25.01.2011
comment
Iya benar sekali. Simetrinya berasal dari fakta bahwa untuk data bernilai riil, koefisien ke-k adalah konjugasi kompleks dari koefisien ke-(N-k), sehingga Anda dapat menjumlahkan setiap suku dan konjugasinya menjadi 2*real(...) - person Itamar Katz; 25.01.2011
comment
Besar! Terima kasih banyak! Saya belum pernah bekerja dengan MEX sebelumnya, apakah mudah untuk membuat kode contoh tampilannya? - person ggkmath; 25.01.2011
comment
Saya tidak akan mengatakan ini mudah, tetapi juga tidak terlalu sulit - tentu saja tergantung pada pengalaman Anda. Matlab memiliki dokumentasi yang bagus, jadi ini adalah titik awal yang baik. - person Itamar Katz; 25.01.2011

Saya telah memposting pertanyaan/jawaban di Mempercepat pemangkasan FFTW untuk menghindari zero padding besar-besaran yang memecahkan masalah kasus C++ menggunakan FFTW. Anda dapat menggunakan solusi ini dengan mengeksploitasi mex-files.

person Vitality    schedule 21.11.2016