FFTW使用小結

簡介
================
FFTW—Fastest Fourier Transform in the West,是由 MIT 的 Matteo Frigo 博士和 Steven G. Johnson 博士開發的一個完全免費的軟件包。FFTW 最初的 release 版本於 1997 年發佈,最新的 release 版本 fftw-3.3.4。git路徑:
https://github.com/FFTW/fftw3.git
它是一個 C 語言開發的庫,支持任意大小的、任意維數的數據的離散傅里葉變換(DFT),並且還支持離散餘弦變換(DCT)、離散正弦變換(DST)和離散哈特萊變換(DHT)

數據類型
================
FFTW 有三個版本的數據類型:double、float 和 long double,使用方法如下:
1.鏈接對應的庫(比如 libfftw3-3、libfftw3f-3、或 ibfftw3l-3)
2.包含同樣的頭文件 fftw3.h
將所有以小寫"fftw_"開頭的名字替換爲"fftwf_"(float 版本)或"fftwl_"(long double 版本)。比如將 fftw_complex 替換爲 fftwf_complex,將 fftw_execute
替換爲 fftwf_execute 等。
3.所有以大寫"FFTW_"開頭的名字不變
4.將函數參數中的 double 替換爲 float 或 long double
5.最後,雖然 long double 是 C99 的標準,但你的編譯器可能根本不支持該類型,或它並不能提供比 double 更高的精度。
6.fftw_malloc 考慮了數據對齊,以便使用 SIMD 指令加速,所以最好不要用 C 函數malloc 替代,而且不要將 fftw_malloc、fftw_free 和 malloc、free、 delete 等混用。
儘量使用 fftw_malloc 分配空間,而不是使用的靜態數組,因爲靜態數組是在棧上分配的,而棧空間較小;還因爲這種方式沒有考慮數據對齊,不便應用SIMD 指令。


編譯構造
=================
1.默認FFTW編譯生成double類型,加入參數“--enable-single”或“--enable-float”編譯單精度(float),加入參數“--enable-long-double”支持長雙進度類型
2.另外,在ARM平臺上可增加“--enable-neon”,使能ARM NEON流媒體加速核心

多線程
=================
FFTW 可以多線程執行,但是多線程存在線程同步問題,這可能會降低性能。所以除非問題規模非常大,一般並不能從多線程中獲益。

plan複用
=================
用同一個 fftw_plan 執行多個數據的變換
同一個 fftw_plan 通過對輸入數據賦不同值來實現不同的變換,實際上還可以利用同一個 fftw_plan 實現對不同輸入輸出數據的變換,也就是說可以有多個
輸入輸出數據數組,各自進行變換,互不影響。當然這要滿足一定的條件:
1.輸入/輸出數據大小相等。
2.變換類型、是否原位運算不變。
3.對 split 數組(指實虛部分開),實部和虛部的分割方式與方案創建時相同。
4.數組的對齊方式相同。如果都是用 fftw_malloc 分配的則此項條件滿足,除非使用
FFTW_UNALIGNED 標誌

總結:是用現有PLAN必須滿足所有參數條件和申請PLAN時一致。

一維數據
=================
目前使用最廣泛,項目也只使用1D,並且輸入數據爲實數,因此變化有兩種方法
1.將輸入部分擴展成虛數,即所有虛數爲0,然後使用一維複數變化接口

fftw_plan fftw_plan_dft_1d(int n, fftw_complex *in, fftw_complex *out,
int sign, unsigned flags);
參數說明:
n -- 複數數據點數
in/out -- 輸入數據和輸出數據,可以相同(原位相同)
sign -- FFTW_FORWARD(-1)正變換 FFTW_BACKWORD(+1) 逆變換
flags -- 主要有兩個參數 FFTW_MEASURE/FFTW_ESTIMATE
    FFTW_MEASURE 先進行預處理,對大數據(數據長度不變)連續FFT變化效果明顯
    FFTW_ESTIMATE 直接構造一個次最優的方案,非連續 實時選擇這種方案

2.使用1D實數DFT變化,由於實數DFT變化具有Hmitian對稱性,所以只需計算n/2 + 1個輸出數據即可
需要注意:
a.如果採用原位運算則out空間必須>= 2*(n/2 + 1)
b.1D實數DFT逆變換任何情況都會破壞IN數據
c.當n爲偶數,由Nyquust採樣定理,第n/2個變化結果也是實數,所以可以把第0個和n/2個數據合併(n/2實部做0虛部),但實際上FFTW沒有這樣實現,因爲在多維數據時不兼容


幾種方案測試

=============

使用C2C MEASURE
===============================
FFT-DFT :309us
FFT-DFT :345us
FFT-DFT :320us
FFT-DFT :349us
FFT-DFT :319us
FFT-DFT :320us
FFT-DFT :321us
FFT-DFT :324us
FFT-DFT :320us
FFT-DFT :319us
FFT-DFT :757919us
幅值:9.465116    相位:-169.040115

real    0m2.064s
user    0m2.044s
sys    0m0.020s
===============================

===============================
使用R2C
FFT-DFT :208us
FFT-DFT :195us
FFT-DFT :198us
FFT-DFT :214us
FFT-DFT :212us
FFT-DFT :212us
FFT-DFT :221us
FFT-DFT :215us
FFT-DFT :214us
FFT-DFT :216us
FFT-DFT :564592us
幅值:9.465116    相位:-169.040115

real    0m0.584s
user    0m0.552s
sys    0m0.032
================================
================================
使用C2C 次最優方案
FFT-DFT :213us
FFT-DFT :204us
FFT-DFT :195us
FFT-DFT :195us
FFT-DFT :195us
FFT-DFT :196us
FFT-DFT :197us
FFT-DFT :196us
FFT-DFT :198us
FFT-DFT :197us
FFT-DFT :554788us
幅值:9.465116    相位:-169.040115

real    0m0.571s
user    0m0.544s
sys    0m0.028s
================================

採樣點數爲24000,進行1維複數/實數變化,參數選擇有FFTW_MEASURE/FFTW_ESTIMATE

1.經測試,發現使用MEASURE方案時,在第一構造plan時花費約1.5秒,而一次DFT變化在300us-,構造的時間能做普通(FFTW_ESTIMATE)5000次左右,故在一般場合FFTW_ESTIMATE完全滿足需求。

2.實數FFTW_ESTIMATE,時間上並沒有優於複數DFT變化,只是內存開闢只有複數DFT的一半。實數DFT在構造plan時間上比複數慢,但變換比複數快,所以整體持平

3.最終選擇複數和實數 ESTIMATE方案


FFTW_WISDOM
============
關於WISDOM,翻譯自FFTW官網
FFTW -wisdom是一種實用工具來生成wisdom文件,其中包含有關如何以最佳方式計算(傅立葉)變換不同大小的保存的信息。
wisdom 的大體思路就是把生成好的策略相關的配置信息存儲在磁盤裏,然後在下次重新運行程序的時候,把策略相關的配置信息重新載入到內存中,這樣在重新生成 plan 的時候就可以節約大量的時間。

FFTW 提供了多種方式來生成wisdom文件,使用時不必關心其中具體格式是怎麼樣,可以導入/出到文件/字符串等

322 FFTW_EXTERN int X(export_wisdom_to_filename)(const char *filename);        \
323 FFTW_EXTERN void X(export_wisdom_to_file)(FILE *output_file);              \
324 FFTW_EXTERN char *X(export_wisdom_to_string)(void);                        \
325 FFTW_EXTERN void X(export_wisdom)(X(write_char_func) write_char,           \
326                                   void *data);                             \
327 FFTW_EXTERN int X(import_system_wisdom)(void);                             \
328 FFTW_EXTERN int X(import_wisdom_from_filename)(const char *filename);      \
329 FFTW_EXTERN int X(import_wisdom_from_file)(FILE *input_file);              \
330 FFTW_EXTERN int X(import_wisdom_from_string)(const char *input_string);    \
331 FFTW_EXTERN int X(import_wisdom)(X(read_char_func) read_char, void *data);

wisdom 存儲起來的不是 plan 本身,而是和 plan 相關的配置信息,例如內存、寄存器等。故在導入wisdom後還是需要執行plan初始化。
example:
    fftw_export_wisdom_to_filename("wisdom.conf");
    p_str = fftw_export_wisdom_to_string();
    
    fftw_export_wisdom_from_filename("wisdom.conf");
    fftw_export_wisdom_from_string(p_str);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章