アイマスから離れて久しい・・・・外から参照するためのメモ程度のものです。 記載されたソースコードは、特に指定が無い限り改変再配布はご自由にどうぞ。 但し、無保証です。質問は受け付けません。バグ等には対応しません。

2013年3月14日木曜日

実数、虚数 別配列によるfftの性能測定

気が付いたら10%程度低下したと思っていたスレッド性能が戻っていた。
リソースモニターでは、負荷はかかっていなかったのだが?
まぁいい、どうせウイルスバスターだろう。

気になったので実数と虚数を個別の配列にしたパターンを試してみた。
sin,cosテーブル不使用。

ソース
void *fft(int nx ,double ar[],double ai[])
{
    int  i, k, m, n;
    double tmpr, tmpi, r, cosw, sinw;
    n = 1<<nx;
    r =  -M_PI / (double)n;
    for(m = n/2; m > 0  ; m /= 2 ) {
        r *= 2;
        for(k = 0 ; k < m; k++) {
            cosw = cos(r*(double)k);
            sinw = sin(r*(double)k);
            for(i= k ; i < n; i += m*2) {
                tmpr = ar[i] - ar[m+i];
                tmpi = ai[i] - ai[m+i];
                ar[i] = ar[i] + ar[m+i];
                ai[i] = ai[i] + ai[m+i];
                ar[m+i] = tmpr * cosw - tmpi * sinw;
                ai[m+i] = tmpi * cosw + tmpr * sinw;
            }
        }
    }
return;
}
/* =====  ifft ======== */
void *ifft(int nx,double ar[],double ai[])
{
    int  i, k, m,n;
    double tmpr, tmpi, r, cosw, sinw;
    n = 1<< nx ;
    r = M_PI ;
    for(m = 1; m < n ; m *= 2 ) {
        for(k = 0; k < m; k++) {
            cosw = cos(r*(double)k);
            sinw = sin(r*(double)k);
            for(i= k ; i < n; i += m*2) {
                tmpr = cosw * ar[m+i] - sinw * ai[m+i];
                tmpi = cosw * ai[m+i] + sinw * ar[m+i];
                ar[m+i] = ar[i] - tmpr ;
                ai[m+i] = ai[i] - tmpi;
                ar[i] = ar[i] + tmpr ;
                ai[i] = ai[i] + tmpi;
            }
        }
        r *= 0.5;
    }
    return;
}

結果
No.0 n=67108864=1<<26 radix=1000 fft  64.316458 , r_mul   -------- , ifft  65.452309 , error --------
No.1 n=67108864=1<<26 radix=1000 fft  39.964405 , r_mul   5.791993 , ifft  38.881574 , error 0.085938
No.2 n=67108864=1<<26 radix=1000 fft  38.348005 , r_mul   5.744716 , ifft  41.086747 , error 0.085938
No.3 n=67108864=1<<26 radix=1000 fft   3.771606 , r_mul   4.460116 , ifft   4.872133 , error 0.046875
No.4 n=67108864=1<<26 radix=1000 fft   3.780859 , r_mul   3.618985 , ifft   4.872954 , error 0.046875
No.5 n=67108864=1<<26 radix=1000 fft   3.802325 , r_mul   1.972648 , ifft   4.882619 , error 0.046875
No.6 n=67108864=1<<26 radix=1000 fft   3.779546 , r_mul   0.482001 , ifft   4.864007 , error 0.046875
No.7 n=67108864=1<<26 radix=1000 fft   2.680603 , r_mul   0.491500 , ifft   4.124678  ,error 0.046875
No.8 n=67108864=1<<26 radix=1000 fft   0.814415 , r_mul   0.132398 , ifft   1.112991  ,error 0.046875

fft,ifftの測定のみに特化しているため r_mul と error は、比較出来ない。
No.8 は、fft,ifft,r_mul の内部をスレッド化したもの。ソースが酷い事になっているので整理中。
No.1 の構造体と比べ倍遅い。

nを小さくした場合
No.0 n=65536=1<<16 radix=1000 fft   0.009721 , r_mul   -------- , ifft   0.010200 , error --------
No.1 n=65536=1<<16 radix=1000 fft   0.006810 , r_mul   0.001947 , ifft   0.007225 , error 0.000053
No.3 n=65536=1<<16 radix=1000 fft   0.001549 , r_mul   0.001122 , ifft   0.001413 , error 0.000023

No.1 の構造体と比べ n が小さければ、差は少なくなる。
No.3 sin,cos のテーブル化は有効だが、n=1<<26 のように10倍近く速くなる訳では無い。
No.3 の誤差(error)が小さいのは、sin,cos を求める範囲を0~π/4にしたため。

2013年3月9日土曜日

FFTのチューニングと測定4

No.7 fft,ifft キャシュヒット率向上
 やったことは、小分けにして集中的に処理させるようにした。
  MB_MAX : 集中的に処理するfftのデータサイズ 128KB か 256KB が最速となった。
  MB_WIN : 上記を分割する最小サイズ 4KB が最速。(ページサイズと同じ)。
 その他改修
  ・sin,cos テーブルを1/2にした。1/4にしたかったが、頭が回らない。
   (既存 sin,cos テーブルの1/4しか使わないロジックは組めたが、いざテーブルを縮小しようとすると破綻する。
    それほど難しくは無いはずなのだが、考えが纏まらない。まぁ後にしよう。)
  ・sin,cos テーブルを0~π/4 で計算。精度が若干上がる。
  ・sin,cos テーブル作成を1本化。
/* 処理単位  最小連続領域 fft内では、a[i]とa[m+i]の2領域必要 */
#define MB_WIN (4*1024)
/* 処理領域サイズ */
#define MB_MAX (128*1024)
ad_t *sc = NULL;
ad_t *sc2 = NULL;
double *scd = NULL;
inline unsigned int bitrv_v(int nx, unsigned int a );
void mk_sincos_table(int nx)
{
    int i,j,k,m,n,nr,nn;
    double r ;
    int mx ;
    if( sc != NULL ){
        free(sc);
        free(sc2);
        free(scd);
        sc = NULL ;
    }
    if( nx <= 0 ){
        return ;
    }
    n=1<<nx;
    mx = MB_MAX/(sizeof(ad_t)) ;

    /* fft,ifft (mx) 0 ~ pai */
    sc=(ad_t *)malloc(sizeof(ad_t)*mx);
    r =  2.0 * M_PI / (double)mx;
    for (m=0 ;m<=mx/8;m++){
        sc[mx/2+m].r = cos(r*(double)m);
        sc[mx/2+m].i = sin(r*(double)m);
        sc[mx/2+mx/4-m].r = sc[mx/2+m].i;
        sc[mx/2+mx/4-m].i = sc[mx/2+m].r;
    }
    for (m=1 ;m<mx/4;m++){
        sc[mx-m].r = -sc[mx/2+m].r;
        sc[mx-m].i =  sc[mx/2+m].i;
    }
    for( nn=mx/4 ; nn > 0 ; nn/=2 ){
        for (m=0 ;m<nn;m++){
            sc[nn+m] = sc[nn*2+m*2];
        }
    }
    /* fft,ifft (n) 0 ~ pai/2 */
    scd=(double *)malloc(sizeof(double)*n);
    r =  2.0 * M_PI / (double)n;
    scd[n/2+0] = 1.0;
    scd[n/2+1] = 0.0;
    for (m=1 ;m<=n/8;m++){
        scd[(n/4+m)*2+0] = cos(r*(double)m);
        scd[(n/4+m)*2+1] = sin(r*(double)m);
        scd[(n/2-m)*2+0] = scd[(n/4+m)*2+1];
        scd[(n/2-m)*2+1] = scd[(n/4+m)*2+0];
    }
    for( nn=n/8 ; nn > 0 ; nn/=2 ){
        for (m=0 ;m<nn;m++){
            scd[(nn+m)*2+0] = scd[(nn+m)*4+0];
            scd[(nn+m)*2+1] = scd[(nn+m)*4+1];
        }
    }
    /* r_mul  (n) 0 ~ pai/4 */
    sc2=(ad_t *)malloc(sizeof(ad_t)*n/4);
    r = M_PI /(double)n;
    for(j= 2 ; j < n; j *= 2) {
        i = j / 4;
        for(k = 0; k < j/2; k+=2) {
            nr = j + k ;
            m = bitrv_v(nx,nr);
            /* wi = cos(r*(double)m)*0.5; */
            /* wr = sin(r*(double)m)*0.5; */
            sc2[i].i=cos(r*(double)m)*0.5;
            sc2[i].r=sin(r*(double)m)*0.5;
            i++;
        }
    }
    printf("sin cos table size %lu MB\n",(sizeof(ad_t)*mx+sizeof(double)*n+sizeof(ad_t)*n/4)/1024/1024);
}
/* =====  fft ======== */
static inline void xfft_1(int ns,int ne,ad_t a[],int ms,int me,int mw,int md)
{
    int i,j,j1,j2,j3,k,m,cosm,sinm;
    double cosf,cosw,sinw,tmpr,tmpi;
    for( j1 = 0 ; j1 < ms/md ; j1 += mw){
        for( j2 = ns ; j2 < ne ; j2 += ms*2 ){
            for(m = ms; m > me  ; m /= 2 ) {
                for( j3 = j1 ; j3 < m ; j3 += ms/md ){
                    if( j3 < m/2 ){
                        cosm = j3*2+m;
                        sinm = j3*2+m+1;
                        cosf = 1.0 ;
                    }else{
                        cosm = j3*2 + 1;
                        sinm = j3*2;
                        cosf = -1.0 ;
                    }
                    for(j= j2 ; j < j2+ms*2; j += m*2) {
                        for(k = 0 ; k < mw; k++) {
                            i = j + k + j3 ;
                            cosw = scd[k*2+cosm] * cosf;
                            sinw =-scd[k*2+sinm];
                            tmpr = a[i].r - a[m+i].r;
                            tmpi = a[i].i - a[m+i].i;
                            a[i].r = a[i].r + a[m+i].r;
                            a[i].i = a[i].i + a[m+i].i;
                            a[m+i].r = tmpr * cosw - tmpi * sinw;
                            a[m+i].i = tmpi * cosw + tmpr * sinw;
#ifdef FFT_INDEX_CHECK
if(i < 0 || m+i >=n) { printf("xfft_1 index error m+i=%d n=%d k=%d j=%d\n",m+i,n,k,j); exit(2) ;};
#endif
                        }
                    }
                }
            }
        }
    }
}
static inline void xfft_2(int ns,int ne,ad_t a[],int ms,int me,int mw,int md)
{
    int i,j,j2,k,m;
    double cosw,sinw,tmpr,tmpi;
    for( j2 = ns ; j2 < ne ; j2 += ms*2 ){
        for(m = ms; m > 0  ; m /= 2 ) {
            for(j= j2 ; j < j2+ms*2; j += m*2) {
                for(k = 0 ; k < m; k++) {
                    i = j + k ;
                    cosw = sc[m+k].r;
                    sinw =-sc[m+k].i;
                    tmpr = a[i].r - a[m+i].r;
                    tmpi = a[i].i - a[m+i].i;
                    a[i].r = a[i].r + a[m+i].r;
                    a[i].i = a[i].i + a[m+i].i;
                    a[m+i].r = tmpr * cosw - tmpi * sinw;
                    a[m+i].i = tmpi * cosw + tmpr * sinw;
#ifdef FFT_INDEX_CHECK
if(i < 0 || m+i >=n) { printf("xfft_2 index error m+i=%d n=%d k=%d j=%d\n",m+i,n,k,j); exit(2) ;};
#endif
                }
            }
        }
    }
}
void fft(int nx ,ad_t a[])
{
    int n;
    int ms ,me ;
    int mx ,md ,mw ;
    n = 1<<nx;
    ms = n/2;
    mx = MB_MAX/(sizeof(ad_t)) ;
    mw = MB_WIN/(sizeof(ad_t)) ;
    md = mx / (mw*2) ;
    while ( ms*2 > mx ) {
        me = ms / md ;
        if( me*2 < mx ){
            me = mx/2 ;
        }
        xfft_1( 0, n, a, ms, me, mw, md);
        ms=me;
    }
    xfft_2( 0, n, a, ms, me, mw, md);
}
/* =====  ifft ======== */
static inline void xifft_1(int ns,int ne,ad_t a[],int ms,int me,int mw,int md)
{
    int i,j,j2,k,m;
    double cosw,sinw,tmpr,tmpi;
    for(j2= ns ; j2 < ne; j2 += me) {
        for(m = 1; m < me ; m *= 2 ) {
            for(j= j2 ; j < j2+me; j += m*2) {
                for(k = 0; k < m; k++) {
                    i = j + k ;
                    cosw = sc[m+k].r;
                    sinw = sc[m+k].i;
                    tmpr = cosw * a[m+i].r - sinw * a[m+i].i;
                    tmpi = cosw * a[m+i].i + sinw * a[m+i].r;
                    a[m+i].r = a[i].r - tmpr ;
                    a[m+i].i = a[i].i - tmpi;
                    a[i].r = a[i].r + tmpr ;
                    a[i].i = a[i].i + tmpi;
#ifdef FFT_INDEX_CHECK
if(i < 0 || m+i >=n) { printf("xifft_1 index error m+i=%d n=%d k=%d j=%d\n",m+i,n,k,j); exit(2) ;};
#endif
                }
            }
        }
    }
}
static inline void xifft_2(int ns,int ne,ad_t a[],int ms,int me,int mw,int md)
{
    int i,j,j1,j2,j3,k,m,cosm,sinm;
    double cosf,cosw,sinw,tmpr,tmpi;
    for( j1 = 0 ; j1 < me/(md*2) ; j1 += mw){
        for( j2 = ns ; j2 < ne ; j2 += me ){
            for(m = ms; m < me ; m *= 2 ) {
                for( j3 = j1 ; j3 < m ; j3 += me/(md*2) ){
                    if( j3 < m/2 ){
                        cosm = j3*2+m;
                        sinm = j3*2+m+1;
                        cosf = 1.0 ;
                    }else{
                        cosm = j3*2 + 1;
                        sinm = j3*2;
                        cosf = -1.0 ;
                    }
                    for(j= j2 ; j < j2+me; j += m*2) {
                        for(k = 0; k < mw; k++) {
                            i = j + k + j3;
                            cosw = scd[k*2+cosm] * cosf;
                            sinw = scd[k*2+sinm];
                            tmpr = cosw * a[m+i].r - sinw * a[m+i].i;
                            tmpi = cosw * a[m+i].i + sinw * a[m+i].r;
                            a[m+i].r = a[i].r - tmpr ;
                            a[m+i].i = a[i].i - tmpi;
                            a[i].r = a[i].r + tmpr ;
                            a[i].i = a[i].i + tmpi;
#ifdef FFT_INDEX_CHECK
if(i < 0 || m+i >=n) { printf("xifft_2 index error m+i=%d n=%d k=%d j=%d\n",m+i,n,k,j); exit(2) ;};
#endif
                        }
                    }
                }
            }
        }
    }
}
void ifft(int nx,ad_t a[])
{
    int n;
    int ms ,me ;
    int mx ,md ,mw ;
    long mel ;
    n = 1<< nx ;
    mx = MB_MAX/(sizeof(ad_t)) ;
    mw = MB_WIN/(sizeof(ad_t)) ;
    md = mx / (mw*2) ;
    me = mx;
    ms = 0;
    if ( n < me ){
        me = n ;
    }
    xifft_1( 0, n, a, ms, me, mw, md);
    ms = me ;
    while ( ms < n ){
        mel = (long)ms * md ;
        if( mel > n ){
            me = n ;
        }else{
            me = mel ;
        }
        xifft_2( 0, n, a, ms, me, mw, md);
        ms = me ;
    }
}

結果

No.1 n=67108864=1<<26 radix=1000 fft  39.964405 , r_mul   5.791993 , ifft  38.881574 , error 0.085938
No.2 n=67108864=1<<26 radix=1000 fft  38.348005 , r_mul   5.744716 , ifft  41.086747 , error 0.085938
No.3 n=67108864=1<<26 radix=1000 fft   3.771606 , r_mul   4.460116 , ifft   4.872133 , error 0.046875
No.4 n=67108864=1<<26 radix=1000 fft   3.780859 , r_mul   3.618985 , ifft   4.872954 , error 0.046875
No.5 n=67108864=1<<26 radix=1000 fft   3.802325 , r_mul   1.972648 , ifft   4.882619 , error 0.046875
No.6 n=67108864=1<<26 radix=1000 fft   3.779546 , r_mul   0.482001 , ifft   4.864007 , error 0.046875
No.7 n=67108864=1<<26 radix=1000 fft   2.680603 , r_mul   0.491500 , ifft   4.124678  ,error 0.046875
fft,ifftは、MB_MAX を境に2分した、速度の向上は、MB_MAX 以下の処理(xfft_2,xifft_1)による。
MB_MAX 以上の処理では、単一スレッドではあまり改善をしていない。
しかし並列に動作させた場合の劣化が少ない。
$ (./fft_sp_10 &);(./fft_sp_10 &);(./fft_sp_10 &);(./fft_sp_10 &)
$ sin cos table size 768 MB
mk_sincos_table*   1.414342
sin cos table size 768 MB
mk_sincos_table*   1.444200
sin cos table size 768 MB
mk_sincos_table*   1.446548
sin cos table size 768 MB
mk_sincos_table*   1.459807
n= 67108864=1<<26 radix=   1000 fft  3.55244350,r_mul  0.64616500,ifft  4.87817400,error 0.046875000
n= 67108864=1<<26 radix=   1000 fft  3.69948500,r_mul  0.82990200,ifft  4.91807500,error 0.046875000
n= 67108864=1<<26 radix=   1000 fft  3.77741450,r_mul  0.81672000,ifft  4.94080800,error 0.046875000
n= 67108864=1<<26 radix=   1000 fft  3.81712500,r_mul  0.81457200,ifft  5.02338200,error 0.046875000
あら?Linuxをアップデートしたせいかマルチスレッド性能が、10%程度劣化してる。(他でも確認)
仕事だったら大事だけど、ほぼ改修前の性能が出ているので良しとしよう。


n=1<<30 で処理したかったが、あきらめたほうが良さそう。
メモリーが厳しいのもあるが、n=1<<29とn=1<<30の計算出来る桁数が,1.5倍であるため
n=1<<30を超える桁数の乗算を行う場合、n=1<<29で行う方が、効率が良いかもしれない。

n=1<<30 の倍の桁を計算する場合。但し便宜上 nが倍異なるfftの速度比は倍、fftとifftの速度は同じ。

(1)n=1<<30 による場合
  fftのデータ領域が、16GB 2つ。
  ・fft 5回とifft4回 計9回
  ・カラツバ方の場合 fft 6回とifft3回 計9回
   
(2)n=1<<29 による場合
  fftのデータ領域が、8GB 5つ。
  ・fft 6回とifft9回 計15回(n=1<<30相当で7.5回)
  fftのデータ領域が、8GB 4つ。
  ・fft 8回とifft9回 計16回(n=1<<30相当で8回)
このようになるとは限らないが、n=1<<29 で計算しても大差は無さそう。

基数(radix) 1xxx と 5xxx の処理速度と精度
 全桁 radix-1 と radix-2 の乗算結果
 radix=5?? は、radix=1000 で 500 以上のとき 1000 引いて-500とし次の桁に1繰り越した値を使った場合の精度です。
 2進表現で1bit少なくなるので精度が上がる。
 radixの値は、radix=5?? で誤差 0.125 未満となるようにした。
sin cos table size 12288 MB
mk_sincos_table*  19.288233
n=       32=1<< 5 radix=1000000 fft  0.00000144,r_mul  0.00000258,ifft  0.00000260,error 0.007812500
n=       64=1<< 6 radix=1000000 fft  0.00000175,r_mul  0.00000282,ifft  0.00000287,error 0.015625000
n=      128=1<< 7 radix=1000000 fft  0.00000241,r_mul  0.00000315,ifft  0.00000347,error 0.015625000
n=      256=1<< 8 radix=1000000 fft  0.00000399,r_mul  0.00000398,ifft  0.00000498,error 0.062500000
n=      512=1<< 9 radix=1000000 fft  0.00000743,r_mul  0.00000559,ifft  0.00000818,error 0.125000000
n=     1024=1<<10 radix=1000000 fft  0.00001481,r_mul  0.00000870,ifft  0.00001531,error 0.281250000
n=     2048=1<<11 radix= 100000 fft  0.00003047,r_mul  0.00001484,ifft  0.00003201,error 0.007812500
n=     4096=1<<12 radix= 100000 fft  0.00006592,r_mul  0.00002706,ifft  0.00006947,error 0.015625000
n=     8192=1<<13 radix= 100000 fft  0.00014421,r_mul  0.00005723,ifft  0.00013765,error 0.031250000
n=    16384=1<<14 radix= 100000 fft  0.00030691,r_mul  0.00011254,ifft  0.00030151,error 0.062500000
n=    32768=1<<15 radix= 100000 fft  0.00066610,r_mul  0.00022469,ifft  0.00064341,error 0.125000000
n=    65536=1<<16 radix= 100000 fft  0.00146280,r_mul  0.00040211,ifft  0.00136111,error 0.250000000
n=   131072=1<<17 radix=  10000 fft  0.00306610,r_mul  0.00079527,ifft  0.00330002,error 0.007812500
n=   262144=1<<18 radix=  10000 fft  0.00664170,r_mul  0.00161984,ifft  0.00769197,error 0.011718750
n=   524288=1<<19 radix=  10000 fft  0.01429983,r_mul  0.00341949,ifft  0.01750854,error 0.025390625
n=  1048576=1<<20 radix=  10000 fft  0.03075497,r_mul  0.00726500,ifft  0.03936812,error 0.062500000
n=  2097152=1<<21 radix=  10000 fft  0.06450894,r_mul  0.01460850,ifft  0.08666512,error 0.101562500
n=  4194304=1<<22 radix=  10000 fft  0.13735087,r_mul  0.02956750,ifft  0.18965150,error 0.250000000
n=  8388608=1<<23 radix=   1000 fft  0.28949825,r_mul  0.06009750,ifft  0.41275250,error 0.004882812
n= 16777216=1<<24 radix=   1000 fft  0.60363550,r_mul  0.12108900,ifft  0.88746600,error 0.011718750
n= 33554432=1<<25 radix=   1000 fft  1.25817400,r_mul  0.24190800,ifft  1.90104000,error 0.023437500
n= 67108864=1<<26 radix=   1000 fft  2.65220050,r_mul  0.48588900,ifft  4.07407800,error 0.046875000
n=134217728=1<<27 radix=   1000 fft  5.53910800,r_mul  0.97108300,ifft  8.65552200,error 0.109375000
n=268435456=1<<28 radix=   1000 fft 11.47869800,r_mul  1.96056300,ifft 18.41602400,error 0.187500000
n=536870912=1<<29 radix=   1000 fft 23.80241650,r_mul  3.93131700,ifft 38.82842100,error 0.406250000
n=1073741824=1<<30 radix=    100 fft 50.63409150,r_mul  7.84821800,ifft 82.09815700,error 0.007812500

sin cos table size 12288 MB
mk_sincos_table*  19.289345
n=       32=1<< 5 radix= 500000 fft  0.00000146,r_mul  0.00000264,ifft  0.00000263,error 0.001953125
n=       64=1<< 6 radix= 500000 fft  0.00000176,r_mul  0.00000284,ifft  0.00000293,error 0.003906250
n=      128=1<< 7 radix= 500000 fft  0.00000245,r_mul  0.00000324,ifft  0.00000353,error 0.007812500
n=      256=1<< 8 radix= 500000 fft  0.00000398,r_mul  0.00000397,ifft  0.00000498,error 0.015625000
n=      512=1<< 9 radix= 500000 fft  0.00000736,r_mul  0.00000561,ifft  0.00000823,error 0.039062500
n=     1024=1<<10 radix= 500000 fft  0.00001491,r_mul  0.00000874,ifft  0.00001519,error 0.062500000
n=     2048=1<<11 radix=  50000 fft  0.00003078,r_mul  0.00001506,ifft  0.00003131,error 0.001953125
n=     4096=1<<12 radix=  50000 fft  0.00006631,r_mul  0.00002742,ifft  0.00006889,error 0.003906250
n=     8192=1<<13 radix=  50000 fft  0.00014308,r_mul  0.00005813,ifft  0.00014106,error 0.007812500
n=    16384=1<<14 radix=  50000 fft  0.00030473,r_mul  0.00010875,ifft  0.00030888,error 0.015625000
n=    32768=1<<15 radix=  50000 fft  0.00065135,r_mul  0.00023616,ifft  0.00065838,error 0.031250000
n=    65536=1<<16 radix=  50000 fft  0.00145519,r_mul  0.00039919,ifft  0.00136547,error 0.062500000
n=   131072=1<<17 radix=   5000 fft  0.00304454,r_mul  0.00079201,ifft  0.00327229,error 0.001586914
n=   262144=1<<18 radix=   5000 fft  0.00663593,r_mul  0.00160174,ifft  0.00765709,error 0.002929688
n=   524288=1<<19 radix=   5000 fft  0.01433247,r_mul  0.00351286,ifft  0.01745560,error 0.006347656
n=  1048576=1<<20 radix=   5000 fft  0.03078732,r_mul  0.00737153,ifft  0.03932924,error 0.011718750
n=  2097152=1<<21 radix=   5000 fft  0.06439238,r_mul  0.01480862,ifft  0.08659762,error 0.023437500
n=  4194304=1<<22 radix=   5000 fft  0.13750100,r_mul  0.02979375,ifft  0.18965050,error 0.062500000
n=  8388608=1<<23 radix=    500 fft  0.29046100,r_mul  0.06008050,ifft  0.41201500,error 0.001220703
n= 16777216=1<<24 radix=    500 fft  0.60163050,r_mul  0.12070600,ifft  0.88503500,error 0.002441406
n= 33554432=1<<25 radix=    500 fft  1.25629450,r_mul  0.24342700,ifft  1.90311300,error 0.005859375
n= 67108864=1<<26 radix=    500 fft  2.65134900,r_mul  0.48445000,ifft  4.07278700,error 0.011718750
n=134217728=1<<27 radix=    500 fft  5.53345550,r_mul  0.97872600,ifft  8.67555700,error 0.031250000
n=268435456=1<<28 radix=    500 fft 11.48626400,r_mul  1.95629100,ifft 18.36818100,error 0.050781250
n=536870912=1<<29 radix=    500 fft 23.78614350,r_mul  3.92355900,ifft 38.80194300,error 0.109375000
n=1073741824=1<<30 radix=     50 fft 50.65816300,r_mul  7.86358500,ifft 82.21162600,error 0.001953125

予定
 ・fft,ifft内部マルチスレッド化
   fft,ifftをCPU分マルチスレッドで動かせれば良いが、数が不足する場合もある。
   単体でマルチスレッド化出来れば、扱い易い。
 ・fft,ifftの動作検証
   1/3とかだと検算が楽だが今一。
   sqrt(2)をニュートン方で計算して検証は、GMPか?
   比較的組みやすいガウス=ルジャンドルのアルゴリズムでπを計算してもよいか。
残件
 ・整数化&ディスクI/O
  計算とディスクへの保存の都合上 int に10進8桁で保存予定。fftのradixとサイズが異なる。
  ディスクI/Oは、mmapを使う予定。I/Oのスケジューリングの方が大変そう。
 ・整数の乗算
  fftを使うのは多分1000桁以上。それ以下は、カラツバ法などを使って乗算することになる。
 ・n>1<<30 の乗算
 ・多倍長割り算
 ・他色々
  

2013年3月3日日曜日

FFTのチューニングと測定3

No.6 r_mulのsin,cosテーブル化
 ソース
  0~π/4のsin,cosをテーブル化。π/4~π/2はsin,cosを入れ替えて使用。
ad_t *sc2 = NULL;
inline unsigned int bitrv_v(int nx, unsigned int a );
void *mk_sincos_table2(int nx)
{
    int i,j,k,m,n,nr;
    double r;
    n=1<<nx;
    sc2=(ad_t *)malloc(sizeof(ad_t)*n/4);
    r = M_PI /(double)n;
    for(j= 2 ; j < n; j *= 2) {
        i = j / 4;
        for(k = 0; k < j/2; k+=2) {
            nr = j + k ;
            m = bitrv_v(nx,nr);
            /* wi = cos(r*(double)m)*0.5; */
            /* wr = sin(r*(double)m)*0.5; */
            sc2[i].i=cos(r*(double)m)*0.5;
            sc2[i].r=sin(r*(double)m)*0.5;
            i++;
        }
    }
}
/* 実数fft後処理+乗算+実数ifft前処理 */
/*  c = a * b ; a=b=c OK */
void *r_mul( int nx,ad_t c[],ad_t a[],ad_t b[] )
{
    double wr,wi,tmpr,tmpi;
    ad_t anr,amr,bnr,bmr;
    int m,n,mr,nr,j,k ;
/*    double r ; */

    n = 1<< nx;

    anr.r = a[0].r + a[0].i ;
    anr.i = a[0].r - a[0].i ;
    anr.r *=  b[0].r + b[0].i ;
    anr.i *=  b[0].r - b[0].i ;
    c[0].r = (anr.r + anr.i)*0.5 ;
    c[0].i = (anr.r - anr.i)*0.5 ;
    anr = a[1];
    bnr = b[1];
    c[1].r = anr.r * bnr.r - anr.i * bnr.i ;
    c[1].i = anr.r * bnr.i + anr.i * bnr.r ;
/*    r = M_PI /(double)n; */
/*    m = 0;               */
    for(j= 2 ; j < n; j *= 2) {
        m = j/4 -1 ;
        for(k = 0; k < j; k+=2) {
            nr = j + k ;
            mr = j*2-1-k ;
           /*  m = inc_rvbit(nx-1,m);        */
           /*  wi = cos(r*(double)m)*0.5;    */
           /*  wr = sin(r*(double)m)*0.5+0.5;*/
            if ( k < j/2 ){
                m ++ ;
                wi = sc2[m].i;
                wr = sc2[m].r+0.5;
            }else{
                wi = sc2[m].r;
                wr = sc2[m].i+0.5;
                m --;
            }
#ifdef FFT_INDEX_CHECK
if(nr < 0 ) { printf("r_mul index under nr\n"); exit ;};
if(mr< 0 )  { printf("r_mul index under mr\n"); exit ;};
if(m< 0 )   { printf("r_mul index under m\n"); exit ;};
if(nr >=n ) { printf("r_mul index over nr\n"); exit ;};
if(mr >=n ) { printf("r_mul index over mr\n"); exit ;};
if(m >=n/4 ){ printf("r_mul index over m\n"); exit ;};
#endif

            anr = a[nr];
            amr = a[mr];
            bnr = b[nr];
            bmr = b[mr];

            /* fft 後処理 a */
            tmpr = wr * (anr.r - amr.r) - wi * (anr.i + amr.i);
            tmpi = wr * (anr.i + amr.i) + wi * (anr.r - amr.r);
            anr.r = anr.r - tmpr ;
            anr.i = anr.i - tmpi;
            amr.r = amr.r + tmpr ;
            amr.i = amr.i - tmpi;
            /* fft 後処理 b */
            tmpr = wr * (bnr.r - bmr.r) - wi * (bnr.i + bmr.i);
            tmpi = wr * (bnr.i + bmr.i) + wi * (bnr.r - bmr.r);
            bnr.r = bnr.r - tmpr ;
            bnr.i = bnr.i - tmpi;
            bmr.r = bmr.r + tmpr ;
            bmr.i = bmr.i - tmpi;
            /* 乗算 */
            tmpr  = anr.r ;
            anr.r = tmpr * bnr.r - anr.i * bnr.i ;
            anr.i = tmpr * bnr.i + anr.i * bnr.r ;
            tmpr  = amr.r ;
            amr.r = tmpr * bmr.r - amr.i * bmr.i ;
            amr.i = tmpr * bmr.i + amr.i * bmr.r ;
            /* ifft 前処理 */
            tmpr = wr * (anr.r - amr.r) + wi * (anr.i + amr.i);
            tmpi = wr * (anr.i + amr.i) - wi * (anr.r - amr.r);
            c[nr].r = anr.r - tmpr ;
            c[nr].i = anr.i - tmpi;
            c[mr].r = amr.r + tmpr ;
            c[mr].i = amr.i - tmpi;
        }
    }
}

結果

No.1 n=67108864=1<<26 radix=1000 fft  39.964405 , r_mul   5.791993 , ifft  38.881574 , error 0.085938
No.2 n=67108864=1<<26 radix=1000 fft  38.348005 , r_mul   5.744716 , ifft  41.086747 , error 0.085938
No.3 n=67108864=1<<26 radix=1000 fft   3.771606 , r_mul   4.460116 , ifft   4.872133 , error 0.046875
No.4 n=67108864=1<<26 radix=1000 fft   3.780859 , r_mul   3.618985 , ifft   4.872954 , error 0.046875
No.5 n=67108864=1<<26 radix=1000 fft   3.802325 , r_mul   1.972648 , ifft   4.882619 , error 0.046875
No.6 n=67108864=1<<26 radix=1000 fft   3.779546 , r_mul   0.482001 , ifft   4.864007 , error 0.046875
約4倍。もう少し速くなると思ったが、fftの最外LOOPの約4周分の処理を行っているので妥当な速さなのでしょうね。

sin,cosテーブルを、n=67108864=1<<26 で作成
mk_sincos_table*   1.746971
n=67108864=1<<26 radix=1000 fft   3.779546 , r_mul   0.482001 , ifft   4.864007 , error 0.046875
mk_sincos_table*   1.739643
n=33554432=1<<25 radix=1000 fft   1.801143 , r_mul   0.239757 , ifft   2.291959 , error 0.023438
mk_sincos_table*   1.743299
n=16777216=1<<24 radix=1000 fft   0.867854 , r_mul   0.119740 , ifft   1.079338 , error 0.009766

FFTのチューニングと測定2

No.4 r_mulのチューニングメモリーのリニアアクセス化
 ソース
/* 実数fft後処理+乗算+実数ifft前処理 */
/*  c = a * b ; a=b=c OK */
void *r_mul( int nx,ad_t c[],ad_t a[],ad_t b[] )
{
    double wr,wi,tmpr,tmpi,r;
    ad_t anr,amr,bnr,bmr;
    int m,n,mr,nr ;
    n = 1<< nx ;

    anr.r = a[0].r + a[0].i ;
    anr.i = a[0].r - a[0].i ;
    anr.r *=  b[0].r + b[0].i ;
    anr.i *=  b[0].r - b[0].i ;
    c[0].r = (anr.r + anr.i)*0.5 ;
    c[0].i = (anr.r - anr.i)*0.5 ;
    anr = a[1];
    bnr = b[1];
    c[1].r = anr.r * bnr.r - anr.i * bnr.i ;
    c[1].i = anr.r * bnr.i + anr.i * bnr.r ;
    r = M_PI /(double)n;
    for(  nr = 2 ; nr < n ; nr+=2 ) {
        m = bitrv_v(nx,nr);
        mr = bitrv_v(nx,n-m);
        anr = a[nr];
        amr = a[mr];
        bnr = b[nr];
        bmr = b[mr];
        wi = sc[n+m].r*0.5;
        wr = sc[n+m].i*0.5+0.5;
        /* fft 後処理 a */
        tmpr = wr * (anr.r - amr.r) - wi * (anr.i + amr.i);
        tmpi = wr * (anr.i + amr.i) + wi * (anr.r - amr.r);
        anr.r = anr.r - tmpr ;
        anr.i = anr.i - tmpi;
        amr.r = amr.r + tmpr ;
        amr.i = amr.i - tmpi;
        /* fft 後処理 b */
        tmpr = wr * (bnr.r - bmr.r) - wi * (bnr.i + bmr.i);
        tmpi = wr * (bnr.i + bmr.i) + wi * (bnr.r - bmr.r);
        bnr.r = bnr.r - tmpr ;
        bnr.i = bnr.i - tmpi;
        bmr.r = bmr.r + tmpr ;
        bmr.i = bmr.i - tmpi;
        /* 乗算 */
        tmpr  = anr.r ;
        anr.r = tmpr * bnr.r - anr.i * bnr.i ;
        anr.i = tmpr * bnr.i + anr.i * bnr.r ;
        tmpr  = amr.r ;
        amr.r = tmpr * bmr.r - amr.i * bmr.i ;
        amr.i = tmpr * bmr.i + amr.i * bmr.r ;
        /* ifft 前処理 */
        tmpr = wr * (anr.r - amr.r) + wi * (anr.i + amr.i);
        tmpi = wr * (anr.i + amr.i) - wi * (anr.r - amr.r);
        c[nr].r = anr.r - tmpr ;
        c[nr].i = anr.i - tmpi;
        c[mr].r = amr.r + tmpr ;
        c[mr].i = amr.i - tmpi;
    }
}
この処理でmrもほぼリニアになる。
後で解ったが、ifftとほぼ同じLOOPで処理可能。
ただ m は駄目だ。

結果
No.1 n=67108864=1<<26 radix=1000 fft  39.964405 , r_mul   5.791993 , ifft  38.881574 , error 0.085938
No.2 n=67108864=1<<26 radix=1000 fft  38.348005 , r_mul   5.744716 , ifft  41.086747 , error 0.085938
No.3 n=67108864=1<<26 radix=1000 fft   3.771606 , r_mul   4.460116 , ifft   4.872133 , error 0.046875
No.4 n=67108864=1<<26 radix=1000 fft   3.780859 , r_mul   3.618985 , ifft   4.872954 , error 0.046875
fftとifftの劇的改善に比べランダムアクセスが残るためか、余り改善しない。
sin,cosテーブルをリニアにアクセス出来るように並べ替えれば高速化可能だが、fftと共用不可となる。


No.5 r_mul でsin,cos関数を使用。
 ソース
  r_mul用sin,cosテーブル作成部を削除。
  r_mulをifftのLOOPの記述に合わせる。
ad_t *sc = NULL;
void *mk_sincos_table(int nx)
{
    int n ,m,nn;
    double r ;
    if( sc != NULL ){
        free(sc);
        sc = NULL ;
    }
    if( nx <= 0 ){
        return ;
    }
    n=1<<nx;
    sc=(ad_t *)malloc(sizeof(ad_t)*n);
    r =  2.0 * M_PI / (double)n;
    for ( m=0;m<n;m++){
        sc[m].r = 0.0;
        sc[m].i = 0.0;
    }
    /* fft,ifft (nx) 0 ~ pai */
    for (m=0 ;m<n/4;m++){
        sc[n/2+m].r = cos(r*(double)m);
        sc[n/2+m].i = sin(r*(double)m);
    }
    sc[n/2+n/4].r = 0.0;
    sc[n/2+n/4].i = 1.0;
    for (m=1 ;m<n/4;m++){
        sc[n-m].r = -sc[n/2+m].r;
        sc[n-m].i =  sc[n/2+m].i;
    }
    for( nn=n/4 ; nn > 0 ; nn/=2 ){
        for (m=0 ;m<nn;m++){
            sc[nn+m] = sc[nn*2+m*2];
        }
    }

}
/* 実数fft後処理+乗算+実数ifft前処理 */
/*  c = a * b ; a=b=c OK */
void *r_mul( int nx,ad_t c[],ad_t a[],ad_t b[] )
{
    double wr,wi,tmpr,tmpi,r;
    ad_t anr,amr,bnr,bmr;
    int m,n,mr,nr,j,k ;
    n = 1<< nx ;

    anr.r = a[0].r + a[0].i ;
    anr.i = a[0].r - a[0].i ;
    anr.r *=  b[0].r + b[0].i ;
    anr.i *=  b[0].r - b[0].i ;
    c[0].r = (anr.r + anr.i)*0.5 ;
    c[0].i = (anr.r - anr.i)*0.5 ;
    anr = a[1];
    bnr = b[1];
    c[1].r = anr.r * bnr.r - anr.i * bnr.i ;
    c[1].i = anr.r * bnr.i + anr.i * bnr.r ;
    r = M_PI /(double)n;
    m = 0;
    for(j= 2 ; j < n; j *= 2) {
        for(k = 0; k < j; k+=2) {
            nr = j + k ;
            mr = j*2-1-k ;
            m = inc_rvbit(nx-1,m);
#ifdef FFT_INDEX_CHECK
if(nr < 0 ) { printf("r_mul index under nr\n"); exit ;};
if(mr< 0 )  { printf("r_mul index under mr\n"); exit ;};
if(m< 0 )   { printf("r_mul index under m\n"); exit ;};
if(nr >=n ) { printf("r_mul index over nr\n"); exit ;};
if(mr >=n ) { printf("r_mul index over mr\n"); exit ;};
if(m >=n )  { printf("r_mul index over m\n"); exit ;};
#endif

            anr = a[nr];
            amr = a[mr];
            bnr = b[nr];
            bmr = b[mr];

            wi = cos(r*(double)m)*0.5;
            wr = sin(r*(double)m)*0.5+0.5;

            /* fft 後処理 a */
            tmpr = wr * (anr.r - amr.r) - wi * (anr.i + amr.i);
            tmpi = wr * (anr.i + amr.i) + wi * (anr.r - amr.r);
            anr.r = anr.r - tmpr ;
            anr.i = anr.i - tmpi;
            amr.r = amr.r + tmpr ;
            amr.i = amr.i - tmpi;
            /* fft 後処理 b */
            tmpr = wr * (bnr.r - bmr.r) - wi * (bnr.i + bmr.i);
            tmpi = wr * (bnr.i + bmr.i) + wi * (bnr.r - bmr.r);
            bnr.r = bnr.r - tmpr ;
            bnr.i = bnr.i - tmpi;
            bmr.r = bmr.r + tmpr ;
            bmr.i = bmr.i - tmpi;
            /* 乗算 */
            tmpr  = anr.r ;
            anr.r = tmpr * bnr.r - anr.i * bnr.i ;
            anr.i = tmpr * bnr.i + anr.i * bnr.r ;
            tmpr  = amr.r ;
            amr.r = tmpr * bmr.r - amr.i * bmr.i ;
            amr.i = tmpr * bmr.i + amr.i * bmr.r ;
            /* ifft 前処理 */
            tmpr = wr * (anr.r - amr.r) + wi * (anr.i + amr.i);
            tmpi = wr * (anr.i + amr.i) - wi * (anr.r - amr.r);
            c[nr].r = anr.r - tmpr ;
            c[nr].i = anr.i - tmpi;
            c[mr].r = amr.r + tmpr ;
            c[mr].i = amr.i - tmpi;
        }
    }
}

結果

No.1 n=67108864=1<<26 radix=1000 fft  39.964405 , r_mul   5.791993 , ifft  38.881574 , error 0.085938
No.2 n=67108864=1<<26 radix=1000 fft  38.348005 , r_mul   5.744716 , ifft  41.086747 , error 0.085938
No.3 n=67108864=1<<26 radix=1000 fft   3.771606 , r_mul   4.460116 , ifft   4.872133 , error 0.046875
No.4 n=67108864=1<<26 radix=1000 fft   3.780859 , r_mul   3.618985 , ifft   4.872954 , error 0.046875
No.5 n=67108864=1<<26 radix=1000 fft   3.802325 , r_mul   1.972648 , ifft   4.882619 , error 0.046875
一言『CPUキャッシュヒットミスによるペナルティは、三角関数より重い』
r_mul専用 sin,cosテーブルは、fftのデータサイズの1/2程度で作成が可能だが、データの配置がかなり面倒
未確認だが、nx 別に作成する必要がなさそう。この場合fftのデータサイズの1/4程度になる。
少し考えよう。


●その他の確認した項目

最大サイズのFFT結果
n=536870912=1<<29 radix=1000 fft  33.229422 , r_mul  15.924549 , ifft  39.121055 , error 0.437500
n=536870912=1<<29 radix=100 fft  33.393006 , r_mul  15.857790 , ifft  45.415891 , error 0.003906
n=1073741824=1<<30 radix=100 fft  69.092204 , r_mul  31.908605 , ifft  69.060423 , error 0.008301

n=1<<29 で radix=1000は、やはり無理か。
n=1<<30は、VMのメモリー52GBでLinuxが無応答になり原因の調査すら出来ない。メモリーリークは無いようだが何故だ?
VMのメモリー56GBでは正常。
※ Linuxを最新の状態に更新したらVMのメモリー52GBで問題無く動作。バグを踏んだらしい。
n=1<<30でのメモリー使用量は、fft領域で16GB*2 sin,cosテーブルで現状16GB。計48GB
sin,cosテーブルは、半分以下に切り詰めれるが、加減算やディスク保存に使う領域等で合計50GBは必要。
最大 n=1<<30で処理したいが、VM環境では無理そう。


多重実行性能
 fft_sp_04 は、No.5のモジュール
$ (./fft_sp_04 &) ;(./fft_sp_04 &) ;(./fft_sp_04 &) ;(./fft_sp_04 &) ;
$ mk_sincos_table   1.296122
mk_sincos_table   1.310993
mk_sincos_table   1.338312
mk_sincos_table   1.342632
n=67108864=1<<26 radix=1000 fft   8.507075 , r_mul   2.069274 , ifft   8.423515 , error 0.046875
n=67108864=1<<26 radix=1000 fft   8.480474 , r_mul   2.064933 , ifft   8.439081 , error 0.046875
n=67108864=1<<26 radix=1000 fft   8.513428 , r_mul   2.074727 , ifft   8.491021 , error 0.046875
n=67108864=1<<26 radix=1000 fft   8.557486 , r_mul   2.067748 , ifft   8.503273 , error 0.046875
r_mulは、4多重で実行時間はほぼ同じ。これは並列実行数に比例して性能向上が期待できる。
処理時間の殆どがsin,cos関数によるものなので、sin,cosテーブルをリニアにアクセス出来るようにした場合、4倍以上の性能向上が期待出来る。
fft,ifftは、4多重で実行時間は約2倍。たぶんメモリー帯域を食潰して半分程度の性能が出ていないと思われる。
CPUキャッシュのヒット率を向上させれば良い。
つまりCPUキャッシュに乗る程度の小さな領域に小分けにし集中的に処理させるようにする。

2013年3月2日土曜日

FFTのチューニングと測定

性能測定方法概要。
・チューニング対象 fft,ifft,r_mul
・計算は、全桁 radix-1 と radix-2 の実数乗算。(単純計算で解が求まる)

 実行回数
  fft2回、r_mul1回、ifft1回

 解
  0<=k<n/2  a[k]=(k+1)*(radix-1)*(radix-2)
  n/2<=k<n a[k]=(n-1- k)*(radix-1)*(radix-2)

・誤差(error)は、スケーリング後解との差の絶対値
・実行時間は、fftの合計実行時間が、1秒以上になるまで繰り返し平均値。(fft1回分)
・データは、10進。radix=1000~100000 誤差 0.25 未満


No.1 チューニング前
n=67108864=1<<26 radix=1000 fft  39.964405 , r_mul   5.791993 , ifft  38.881574 , error 0.085938

radix=10000の場合
n=67108864=1<<26 radix=10000 fft  39.905315 , r_mul   5.775796 , ifft  38.209183 , error 8.500000
誤差が大きすぎ。

No.2 メモリーのリニアアクセス化
 ソース
/* =====  fft ======== */
void *fft(int nx ,ad_t a[])
{
    int  i, j,k, m, n;
    double tmpr, tmpi, r, cosw, sinw;
    n = 1<<nx;
    r =  -M_PI / (double)n;
    for(m = n/2; m > 0  ; m /= 2 ) {
        r *= 2;
        for(j= 0 ; j < n; j += m*2) {
            for(k = 0 ; k < m; k++) {
                i = j + k ;
                cosw = cos(r*(double)k);
                sinw = sin(r*(double)k);
                tmpr = a[i].r - a[m+i].r;
                tmpi = a[i].i - a[m+i].i;
                a[i].r = a[i].r + a[m+i].r;
                a[i].i = a[i].i + a[m+i].i;
                a[m+i].r = tmpr * cosw - tmpi * sinw;
                a[m+i].i = tmpi * cosw + tmpr * sinw;
            }
        }
    }
return;
}
/* =====  ifft ======== */
void *ifft(int nx,ad_t a[])
{
    int  i, j, k, m,n;
    double tmpr, tmpi, r, cosw, sinw;
    n = 1<< nx ;
    r = M_PI ;
    for(m = 1; m < n ; m *= 2 ) {
        for(j= 0 ; j < n; j += m*2) {
            for(k = 0; k < m; k++) {
                i = j + k ;
                cosw = cos(r*(double)k);
                sinw = sin(r*(double)k);
                tmpr = cosw * a[m+i].r - sinw * a[m+i].i;
                tmpi = cosw * a[m+i].i + sinw * a[m+i].r;
                a[m+i].r = a[i].r - tmpr ;
                a[m+i].i = a[i].i - tmpi;
                a[i].r = a[i].r + tmpr ;
                a[i].i = a[i].i + tmpi;
            }
        }
        r *= 0.5;
    }
    return;
}
結果
No.1 n=67108864=1<<26 radix=1000 fft  39.964405 , r_mul   5.791993 , ifft  38.881574 , error 0.085938
No.2 n=67108864=1<<26 radix=1000 fft  38.348005 , r_mul   5.744716 , ifft  41.086747 , error 0.085938
比較のため No.1 も列挙
毎回 sin,cos が呼ばれるので遅くなると思ったが、あまり変わらない。
sin,cos が思ったより速くメモリーのリニアアクセス化では改善しないと思ったが、そうでは無かった。


No.3 sin,cos テーブル化
 ソース
ad_t *sc = NULL;
void *mk_sincos_table(int nx)
{
    int n ,m,nn;
    double r ;
    if( sc != NULL ){
        free(sc);
        sc = NULL ;
    }
    if( nx <= 0 ){
        return ;
    }
    n=1<<nx;
    sc=(ad_t *)malloc(sizeof(ad_t)*(n+n/2));
    r =  M_PI / (double)n;
    for ( m=0;m<n+n/2;m++){
        sc[m].r = 0.0;
        sc[m].i = 0.0;
    }
    /* r_mul 用 (nx+1) 0 ~ pai/2 */
    for ( m=0;m<=n/4;m++){
        sc[n+m].r = cos(r*(double)m);
        sc[n+m].i = sin(r*(double)m);
    }
    for ( m=1;m<n/4;m++){
        sc[n+n/2-m].r = sc[n+m].i;
        sc[n+n/2-m].i = sc[n+m].r;
    }
    /* fft,ifft,r_mul (nx) 0 ~ pai */
    for (m=0 ;m<n/4;m++){
        sc[n/2+m].r = sc[n+m*2].r;
        sc[n/2+m].i = sc[n+m*2].i;
    }
    sc[n/2+n/4].r = 0.0;
    sc[n/2+n/4].i = 1.0;
    for (m=1 ;m<n/4;m++){
        sc[n-m].r = -sc[n/2+m].r;
        sc[n-m].i =  sc[n/2+m].i;
    }
    for( nn=n/4 ; nn > 0 ; nn/=2 ){
        for (m=0 ;m<nn;m++){
            sc[nn+m] = sc[nn*2+m*2];
        }
    }

}
/* =====  fft ======== */
void *fft(int nx ,ad_t a[])
{
    int  i, j,k, m, n;
    double tmpr, tmpi, cosw, sinw;
    n = 1<<nx;
    for(m = n/2; m > 0  ; m /= 2 ) {
        for(j= 0 ; j < n; j += m*2) {
            for(k = 0 ; k < m; k++) {
                i = j + k ;
                cosw = sc[m+k].r;
                sinw =-sc[m+k].i;
                tmpr = a[i].r - a[m+i].r;
                tmpi = a[i].i - a[m+i].i;
                a[i].r = a[i].r + a[m+i].r;
                a[i].i = a[i].i + a[m+i].i;
                a[m+i].r = tmpr * cosw - tmpi * sinw;
                a[m+i].i = tmpi * cosw + tmpr * sinw;
#ifdef FFT_INDEX_CHECK
if(i < 0 )   { printf("fft index under i\n"); exit ;};
if(m+i >=n ) { printf("fft index over m+i\n"); exit ;};
#endif
            }
        }
    }
return;
}
/* =====  ifft ======== */
void *ifft(int nx,ad_t a[])
{
    int  i, j, k, m,n;
    double tmpr, tmpi, cosw, sinw;
    n = 1<< nx ;
    for(m = 1; m < n ; m *= 2 ) {
        for(j= 0 ; j < n; j += m*2) {
            for(k = 0; k < m; k++) {
                i = j + k ;
                cosw = sc[m+k].r;
                sinw = sc[m+k].i;
                tmpr = cosw * a[m+i].r - sinw * a[m+i].i;
                tmpi = cosw * a[m+i].i + sinw * a[m+i].r;
                a[m+i].r = a[i].r - tmpr ;
                a[m+i].i = a[i].i - tmpi;
                a[i].r = a[i].r + tmpr ;
                a[i].i = a[i].i + tmpi;
#ifdef FFT_INDEX_CHECK
if(i < 0 )   { printf("ifft index under i\n"); exit ;};
if(m+i >=n ) { printf("ifft index over m+i\n"); exit ;};
#endif
            }
        }
    }
    return;
}
/* 実数fft後処理+乗算+実数ifft前処理 */
/*  c = a * b ; a=b=c OK */
void *r_mul( int nx,ad_t c[],ad_t a[],ad_t b[] )
{
    double wr,wi,tmpr,tmpi,r;
    ad_t anr,amr,bnr,bmr;
    int m,n,mr,nr ;
    n = 1<< nx ;

    anr.r = a[0].r + a[0].i ;
    anr.i = a[0].r - a[0].i ;
    anr.r *=  b[0].r + b[0].i ;
    anr.i *=  b[0].r - b[0].i ;
    c[0].r = (anr.r + anr.i)*0.5 ;
    c[0].i = (anr.r - anr.i)*0.5 ;
    anr = a[1];
    bnr = b[1];
    c[1].r = anr.r * bnr.r - anr.i * bnr.i ;
    c[1].i = anr.r * bnr.i + anr.i * bnr.r ;

    r = M_PI /(double)n;
    mr = n - 1 ;
    nr = 0 ;
    for(  m = 1 ; m < n/2 ; m++ ) {
        nr = inc_rvbit(nx,nr);
        anr = a[nr];
        amr = a[mr];
        bnr = b[nr];
        bmr = b[mr];
/*
 *       wi = cos(r*(double)m)*0.5;
 *       wr = sin(r*(double)m)*0.5+0.5;
 */
        wi = sc[n+m].r*0.5;
        wr = sc[n+m].i*0.5+0.5;
        /* fft 後処理 a */
        tmpr = wr * (anr.r - amr.r) - wi * (anr.i + amr.i);
        tmpi = wr * (anr.i + amr.i) + wi * (anr.r - amr.r);
        anr.r = anr.r - tmpr ;
        anr.i = anr.i - tmpi;
        amr.r = amr.r + tmpr ;
        amr.i = amr.i - tmpi;
        /* fft 後処理 b */
        tmpr = wr * (bnr.r - bmr.r) - wi * (bnr.i + bmr.i);
        tmpi = wr * (bnr.i + bmr.i) + wi * (bnr.r - bmr.r);
        bnr.r = bnr.r - tmpr ;
        bnr.i = bnr.i - tmpi;
        bmr.r = bmr.r + tmpr ;
        bmr.i = bmr.i - tmpi;
        /* 乗算 */
        tmpr  = anr.r ;
        anr.r = tmpr * bnr.r - anr.i * bnr.i ;
        anr.i = tmpr * bnr.i + anr.i * bnr.r ;
        tmpr  = amr.r ;
        amr.r = tmpr * bmr.r - amr.i * bmr.i ;
        amr.i = tmpr * bmr.i + amr.i * bmr.r ;
        /* ifft 前処理 */
        tmpr = wr * (anr.r - amr.r) + wi * (anr.i + amr.i);
        tmpi = wr * (anr.i + amr.i) - wi * (anr.r - amr.r);
        c[nr].r = anr.r - tmpr ;
        c[nr].i = anr.i - tmpi;
        c[mr].r = amr.r + tmpr ;
        c[mr].i = amr.i - tmpi;
        mr = n -1 - nr ;
    }
}
mk_sincos_table は、fft の使用前に 最大 nx を指定し呼び出す。
fft 用として nx までの全パターン r_mul の nx (fftのnx+1に相当)を作成。
sin,cosは、0~π/4 で計算し π/4~π はそのコピー。若干精度が上がる。
(πが2のべき乗で割り切れ無いことによる誤差らしい。)
現状fft,ifftにで手を入れたく無いので0~πまで求めている。最終的には、0~π/2か0~π/4にする予定。

結果
No.1 n=67108864=1<<26 radix=1000 fft  39.964405 , r_mul   5.791993 , ifft  38.881574 , error 0.085938
No.2 n=67108864=1<<26 radix=1000 fft  38.348005 , r_mul   5.744716 , ifft  41.086747 , error 0.085938
No.3 n=67108864=1<<26 radix=1000 fft   3.771606 , r_mul   4.460116 , ifft   4.872133 , error 0.046875
fft,ifftは、10倍近く改善。r_mul は、メモリーのランダムアクセスを改善していないので若干改善。
fftとifftの差は、バグでは無く設定データ(全桁同一)によるものらしい。
r_mulが、1LOOPなのにfft,ifftとほぼ同じなのは頂けない。

2013年3月1日金曜日

長さ半分の複素FFTで実数のFFT

以下を参考に作成しました。

FFT (高速フーリエ・コサイン・サイン変換) の概略と設計法
http://www.kurims.kyoto-u.ac.jp/~ooura/fftman/index.html
2.1.1 複素 FFT を間接的に用いる方法
http://www.kurims.kyoto-u.ac.jp/~ooura/fftman/ftmn2_1.html#sec2_1_1

第二の方法
長さ半分の複素FFTで実数のFFTを行う。

ソース
 n=1<<30 でも使えるようにするため、参照元とnのスケーリングが異なる。
 r_fft で a[n].r の値が存在するが、格納場所が無いので a[0].i に入れる。
 r_carry で配列を超える繰越値は、配列の最後尾に入れる。
 r_ifft 本来なら周波数間引きかな?でも作れないので wi の符号逆転で対応する。スケーリングが気になったが問題無いようだ。
 これもメモリーのランダムアクセスになるのでかなり重いと思われる。使うなら相応のチューニングが必要かな?
/* 実数fft後処理 */
void *r_fft( int nx,ad_t a[] )
{
    double wr,wi,tmpr,tmpi,r;
    int m,n,mr,nr ;
    n = 1<< nx ;

    tmpr = a[0].r ;
    a[0].r = tmpr + a[0].i ;
    a[0].i = tmpr - a[0].i ;
    r = M_PI /(double)n;
    mr = n - 1 ;
    nr = 0 ;
    for(  m = 1 ; m < n/2 ; m++ ) {
        nr = inc_rvbit(nx,nr);
        wi = cos(r*(double)m)*0.5;
        wr = sin(r*(double)m)*0.5+0.5;
        tmpr = wr * (a[nr].r - a[mr].r) - wi * (a[nr].i + a[mr].i);
        tmpi = wr * (a[nr].i + a[mr].i) + wi * (a[nr].r - a[mr].r);
        a[nr].r = a[nr].r - tmpr ;
        a[nr].i = a[nr].i - tmpi;
        a[mr].r = a[mr].r + tmpr ;
        a[mr].i = a[mr].i - tmpi;
        mr = n -1 - nr ;
    }
}
/* 実数ifft前処理 */
void *r_ifft( int nx,ad_t a[] )
{
    double wr,wi,tmpr,tmpi,r;
    int m,n,mr,nr ;
    n = 1<< nx ;

    tmpr = a[0].r ;
    a[0].r = (tmpr + a[0].i)*0.5 ;
    a[0].i = (tmpr - a[0].i)*0.5 ;
    r = M_PI /(double)n;
    mr = n - 1 ;
    nr = 0 ;
    for(  m = 1 ; m < n/2 ; m++ ) {
        nr = inc_rvbit(nx,nr);
        wi = cos(r*(double)m)*0.5;
        wr = sin(r*(double)m)*0.5+0.5;
        tmpr = wr * (a[nr].r - a[mr].r) + wi * (a[nr].i + a[mr].i);
        tmpi = wr * (a[nr].i + a[mr].i) - wi * (a[nr].r - a[mr].r);
        a[nr].r = a[nr].r - tmpr ;
        a[nr].i = a[nr].i - tmpi;
        a[mr].r = a[mr].r + tmpr ;
        a[mr].i = a[mr].i - tmpi;
        mr = n -1 - nr ;
    }

}
/* 実数fft後処理+乗算+実数ifft前処理 */
/*  c = a * b ; a=b=c OK */
void *r_mul( int nx,ad_t c[],ad_t a[],ad_t b[] )
{
    double wr,wi,tmpr,tmpi,r;
    ad_t anr,amr,bnr,bmr;
    int m,n,mr,nr ;
    n = 1<< nx ;

    anr.r = a[0].r + a[0].i ;
    anr.i = a[0].r - a[0].i ;
    anr.r *=  b[0].r + b[0].i ;
    anr.i *=  b[0].r - b[0].i ;
    c[0].r = (anr.r + anr.i)*0.5 ;
    c[0].i = (anr.r - anr.i)*0.5 ;
    anr = a[1];
    bnr = b[1];
    c[1].r = anr.r * bnr.r - anr.i * bnr.i ;
    c[1].i = anr.r * bnr.i + anr.i * bnr.r ;

    r = M_PI /(double)n;
    mr = n - 1 ;
    nr = 0 ;
    for(  m = 1 ; m < n/2 ; m++ ) {
        nr = inc_rvbit(nx,nr);
        anr = a[nr];
        amr = a[mr];
        bnr = b[nr];
        bmr = b[mr];
        wi = cos(r*(double)m)*0.5;
        wr = sin(r*(double)m)*0.5+0.5;
        /* fft 後処理 a */
        tmpr = wr * (anr.r - amr.r) - wi * (anr.i + amr.i);
        tmpi = wr * (anr.i + amr.i) + wi * (anr.r - amr.r);
        anr.r = anr.r - tmpr ;
        anr.i = anr.i - tmpi;
        amr.r = amr.r + tmpr ;
        amr.i = amr.i - tmpi;
        /* fft 後処理 b */
        tmpr = wr * (bnr.r - bmr.r) - wi * (bnr.i + bmr.i);
        tmpi = wr * (bnr.i + bmr.i) + wi * (bnr.r - bmr.r);
        bnr.r = bnr.r - tmpr ;
        bnr.i = bnr.i - tmpi;
        bmr.r = bmr.r + tmpr ;
        bmr.i = bmr.i - tmpi;
        /* 乗算 */
        tmpr  = anr.r ;
        anr.r = tmpr * bnr.r - anr.i * bnr.i ;
        anr.i = tmpr * bnr.i + anr.i * bnr.r ;
        tmpr  = amr.r ;
        amr.r = tmpr * bmr.r - amr.i * bmr.i ;
        amr.i = tmpr * bmr.i + amr.i * bmr.r ;
        /* ifft 前処理 */
        tmpr = wr * (anr.r - amr.r) + wi * (anr.i + amr.i);
        tmpi = wr * (anr.i + amr.i) - wi * (anr.r - amr.r);
        c[nr].r = anr.r - tmpr ;
        c[nr].i = anr.i - tmpi;
        c[mr].r = amr.r + tmpr ;
        c[mr].i = amr.i - tmpi;
        mr = n -1 - nr ;
    }
}
/* スケーリングと繰越 n-2 -> 0 方向 */
void *r_carry(int nx,ad_t a[],int redex)
{
    double *aa;
    double pn,tmp;
    int n , m;
    long car , t;
    aa = (double*)a;
    n = 1<<nx ;
    pn = 1.0/(double)n;
    car = 0 ;
    for(m=n*2-2;m>=0;m--){
        tmp = aa[m]*pn + (double)car  ;
        if( tmp >= 0.0 ){
            t = (long)(tmp+0.5) ;
            car = t / redex ;
            aa[m] =(double)( t % redex );
        }else{
            t = (long)(tmp-0.5) ;
            car = t / redex - 1;
            aa[m] =(double)( t % redex + redex );
        }
    }
    aa[n*2-1]=(double)car;
}
/* スケーリングと繰越 0 -> n-2 方向 */
void *r_carry_r(int nx,ad_t a[],int redex)
{
    double *aa;
    double pn,tmp;
    int n , m;
    long car , t;
    aa = (double*)a;
    n = 1<<nx ;
    pn = 1.0/(double)n;
    car = 0 ;
    for(m=0;m<n*2-1;m++){
        tmp = aa[m]*pn + (double)car  ;
        if( tmp >= 0.0 ){
            t = (long)(tmp+0.5) ;
            car = t / redex ;
            aa[m] =(double)( t % redex );
        }else{
            t = (long)(tmp-0.5) ;
            car = t / redex - 1;
            aa[m] =(double)( t % redex + redex );
        }
    }
    aa[n*2-1]=(double)car;
}

実行例
input data  n = 8 = 1<<3
 0 a[ 0].r =   2.000000  a[ 0].i =   1.000000  b[ 0].r =   9.000000  b[ 0].i =   8.000000
 1 a[ 1].r =   2.000000  a[ 1].i =   3.000000  b[ 1].r =   7.000000  b[ 1].i =   6.000000
 2 a[ 2].r =   4.000000  a[ 2].i =   5.000000  b[ 2].r =   5.000000  b[ 2].i =   4.000000
 3 a[ 3].r =   6.000000  a[ 3].i =   7.000000  b[ 3].r =   2.000000  b[ 3].i =   3.000000
 4 a[ 4].r =   0.000000  a[ 4].i =   0.000000  b[ 4].r =   0.000000  b[ 4].i =   0.000000
 5 a[ 5].r =   0.000000  a[ 5].i =   0.000000  b[ 5].r =   0.000000  b[ 5].i =   0.000000
 6 a[ 6].r =   0.000000  a[ 6].i =   0.000000  b[ 6].r =   0.000000  b[ 6].i =   0.000000
 7 a[ 7].r =   0.000000  a[ 7].i =   0.000000  b[ 7].r =   0.000000  b[ 7].i =   0.000000
fft(nx,a) fft(nx,b)
 0 a[ 0].r =  14.000000  a[ 0].i =  16.000000  b[ 0].r =  23.000000  b[ 0].i =  21.000000
 1 a[ 4].r =  11.242641  a[ 4].i = -11.485281  b[ 4].r =  22.899495  b[ 4].i =  -1.242641
 2 a[ 2].r =  -6.000000  a[ 2].i =  -0.000000  b[ 2].r =   7.000000  b[ 2].i =  -1.000000
 3 a[ 6].r =   6.899495  a[ 6].i =   2.171573  b[ 6].r =   7.828427  b[ 6].i =   4.514719
 4 a[ 1].r =  -2.000000  a[ 1].i =  -4.000000  b[ 1].r =   5.000000  b[ 1].i =   3.000000
 5 a[ 5].r =   2.757359  a[ 5].i =   5.485281  b[ 5].r =   3.100505  b[ 5].i =   7.242641
 6 a[ 3].r =   2.000000  a[ 3].i =  -8.000000  b[ 3].r =   1.000000  b[ 3].i =   9.000000
 7 a[ 7].r = -12.899495  a[ 7].i =   7.828427  b[ 7].r =   2.171573  b[ 7].i =  21.485281
r_fft(nx,a) r_fft(nx,b)
 0 a[ 0].r =  30.000000  a[ 0].i =  -2.000000  b[ 0].r =  44.000000  b[ 0].i =   2.000000
 1 a[ 4].r =  -7.137071  a[ 4].i = -20.109358  b[ 4].r =  17.920298  b[ 4].i = -24.812274
 2 a[ 2].r =  -2.000000  a[ 2].i =   9.656854  b[ 2].r =   4.707107  b[ 2].i =  -9.949747
 3 a[ 6].r =   4.380086  a[ 6].i =  -5.986423  b[ 6].r =   5.530124  b[ 6].i =  -7.699802
 4 a[ 1].r =  -2.000000  a[ 1].i =  -4.000000  b[ 1].r =   5.000000  b[ 1].i =   3.000000
 5 a[ 5].r =   5.276769  a[ 5].i =  -2.672715  b[ 5].r =   5.398808  b[ 5].i =  -4.971880
 6 a[ 3].r =  -2.000000  a[ 3].i =   1.656854  b[ 3].r =   3.292893  b[ 3].i =   0.050253
 7 a[ 7].r =   5.480217  a[ 7].i =  -0.795649  b[ 7].r =   7.150769  b[ 7].i =  -2.084352
ri_mul(nx,a,b)
 0 a[ 0].r = 1320.000000  a[ 0].i =  -4.000000
 1 a[ 4].r = -626.857348  a[ 4].i = -183.278730
 2 a[ 2].r =  86.669048  a[ 2].i =  65.355339
 3 a[ 6].r = -21.871852  a[ 6].i = -66.831453
 4 a[ 1].r =   2.000000  a[ 1].i = -26.000000
 5 a[ 5].r =  15.199846  a[ 5].i = -40.664931
 6 a[ 3].r =  -6.669048  a[ 3].i =   5.355339
 7 a[ 7].r =  37.529354  a[ 7].i = -17.112207
r_ifft(nx,a)
 0 a[ 0].r = 658.000000  a[ 0].i = 662.000000
 1 a[ 4].r = -74.970563  a[ 4].i = -351.646753
 2 a[ 2].r = -18.000000  a[ 2].i =  38.000000
 3 a[ 6].r =  34.357431  a[ 6].i =  29.480231
 4 a[ 1].r =   2.000000  a[ 1].i = -26.000000
 5 a[ 5].r = -41.029437  a[ 5].i =  55.646753
 6 a[ 3].r =  98.000000  a[ 3].i = -22.000000
 7 a[ 7].r = -514.357431  a[ 7].i = -185.480231
ifft(nx,a)
 0 a[ 0].r = 144.000000  a[ 0].i = 200.000000
 1 a[ 1].r = 320.000000  a[ 1].i = 496.000000
 2 a[ 2].r = 720.000000  a[ 2].i = 984.000000
 3 a[ 3].r = 1264.000000  a[ 3].i = 1608.000000
 4 a[ 4].r = 1336.000000  a[ 4].i = 1104.000000
 5 a[ 5].r = 872.000000  a[ 5].i = 648.000000
 6 a[ 6].r = 440.000000  a[ 6].i = 256.000000
 7 a[ 7].r = 168.000000  a[ 7].i =   0.000000
copy a -> b
r_carry(nx,a,10)
   a[ 7].i =   2.000000
   a[ 0].r =   0.000000
   a[ 0].i =   9.000000
   a[ 1].r =   7.000000
   a[ 1].i =   2.000000
   a[ 2].r =   4.000000
   a[ 2].i =   0.000000
   a[ 3].r =   9.000000
   a[ 3].i =   9.000000
   a[ 4].r =   1.000000
   a[ 4].i =   9.000000
   a[ 5].r =   7.000000
   a[ 5].i =   6.000000
   a[ 6].r =   8.000000
   a[ 6].i =   4.000000
   a[ 7].r =   1.000000
r_carry_r(nx,b,10)
   b[ 7].i =   2.000000
   b[ 7].r =   4.000000
   b[ 6].i =   8.000000
   b[ 6].r =   4.000000
   b[ 5].i =   3.000000
   b[ 5].r =   4.000000
   b[ 4].i =   6.000000
   b[ 4].r =   8.000000
   b[ 3].i =   8.000000
   b[ 3].r =   1.000000
   b[ 2].i =   2.000000
   b[ 2].r =   6.000000
   b[ 1].i =   6.000000
   b[ 1].r =   2.000000
   b[ 0].i =   6.000000
   b[ 0].r =   8.000000
21234567*98765423=2097240991976841
76543212*32456789=2484346881266268

実行例2
fft(nx,a)
fft(nx,b)
r_mul(nx,a,b,a)
 0 a[ 0].r = 658.000000  a[ 0].i = 662.000000
 1 a[ 4].r = -74.970563  a[ 4].i = -351.646753
 2 a[ 2].r = -18.000000  a[ 2].i =  38.000000
 3 a[ 6].r =  34.357431  a[ 6].i =  29.480231
 4 a[ 1].r =   2.000000  a[ 1].i = -26.000000
 5 a[ 5].r = -41.029437  a[ 5].i =  55.646753
 6 a[ 3].r =  98.000000  a[ 3].i = -22.000000
 7 a[ 7].r = -514.357431  a[ 7].i = -185.480231
ifft(nx,a)
r_carry(nx,a,10)
   a[ 7].i =   2.000000
   a[ 0].r =   0.000000
   a[ 0].i =   9.000000
   a[ 1].r =   7.000000
   a[ 1].i =   2.000000
   a[ 2].r =   4.000000
   a[ 2].i =   0.000000
   a[ 3].r =   9.000000
   a[ 3].i =   9.000000
   a[ 4].r =   1.000000
   a[ 4].i =   9.000000
   a[ 5].r =   7.000000
   a[ 5].i =   6.000000
   a[ 6].r =   8.000000
   a[ 6].i =   4.000000
   a[ 7].r =   1.000000
21234567*98765423=2097240991976841


fft関連パーツが一応そろったので次からはチューニング。

実数2値FFT

以下を参考に作成しました。(10%も理解していないが、何とかなるものだ。)

FFT (高速フーリエ・コサイン・サイン変換) の概略と設計法
http://www.kurims.kyoto-u.ac.jp/~ooura/fftman/index.html
2.1.1 複素 FFT を間接的に用いる方法
http://www.kurims.kyoto-u.ac.jp/~ooura/fftman/ftmn2_1.html#sec2_1_1

第一の方法
実数部と虚数部に別々の実数データを設定し1回のfftで2つ同時に実施する。

ソース
 メモリーのランダムアクセスになるのでかなり重いと思われる。使うなら相応のチューニングが必要かな?
/* 2値fft結果の分離 a -> a,b  */
void *d_a2ab( int nx ,ad_t a[],ad_t b[] )
{
    int n,m,nr,mr;
    n = 1<<nx ;
    b[0].r = a[0].i ;
    a[0].i = 0.0 ;
    b[0].i = 0.0 ;
    b[1].r = a[1].i ;
    a[1].i = 0.0 ;
    b[1].i = 0.0 ;

    mr = n - 1 ;
    nr = 0 ;
    for(  m = 1 ; m < n/2 ; m++ ) {
        nr = inc_rvbit(nx , nr );
        b[nr].i =(a[mr].r - a[nr].r)*0.5 ;
        b[mr].i =-b[nr].i;
        a[nr].r = a[nr].r + b[nr].i;
        a[mr].r = a[nr].r;
        b[nr].r =(a[mr].i + a[nr].i)*0.5 ;
        b[mr].r = b[nr].r;
        a[nr].i = a[nr].i - b[nr].r;
        a[mr].i =-a[nr].i;
        mr = n - 1 - nr ;
    }
}
/* fft結果の合成 a,b -> a  */
void *d_ab2a(int nx, ad_t a[],ad_t b[] )
{
    int n,m;
    a[0].i = b[0].r ;
    a[1].i = b[1].r ;
    n = 1<<nx ;
    for(  m = 2 ; m < n ; m++ ) {
        a[m].r = a[m].r -  b[m].i;
        a[m].i = a[m].i +  b[m].r;
    }
}


参考 実数のみ設定したfftの結果
input data  n = 16 = 1<<4
 0 a[ 0].r =   2.000000  a[ 0].i =   0.000000  b[ 0].r =   9.000000  b[ 0].i =   0.000000
 1 a[ 1].r =   1.000000  a[ 1].i =   0.000000  b[ 1].r =   8.000000  b[ 1].i =   0.000000
 2 a[ 2].r =   2.000000  a[ 2].i =   0.000000  b[ 2].r =   7.000000  b[ 2].i =   0.000000
 3 a[ 3].r =   3.000000  a[ 3].i =   0.000000  b[ 3].r =   6.000000  b[ 3].i =   0.000000
 4 a[ 4].r =   4.000000  a[ 4].i =   0.000000  b[ 4].r =   5.000000  b[ 4].i =   0.000000
 5 a[ 5].r =   5.000000  a[ 5].i =   0.000000  b[ 5].r =   4.000000  b[ 5].i =   0.000000
 6 a[ 6].r =   6.000000  a[ 6].i =   0.000000  b[ 6].r =   2.000000  b[ 6].i =   0.000000
 7 a[ 7].r =   7.000000  a[ 7].i =   0.000000  b[ 7].r =   3.000000  b[ 7].i =   0.000000
 8 a[ 8].r =   0.000000  a[ 8].i =   0.000000  b[ 8].r =   0.000000  b[ 8].i =   0.000000
 9 a[ 9].r =   0.000000  a[ 9].i =   0.000000  b[ 9].r =   0.000000  b[ 9].i =   0.000000
10 a[10].r =   0.000000  a[10].i =   0.000000  b[10].r =   0.000000  b[10].i =   0.000000
11 a[11].r =   0.000000  a[11].i =   0.000000  b[11].r =   0.000000  b[11].i =   0.000000
12 a[12].r =   0.000000  a[12].i =   0.000000  b[12].r =   0.000000  b[12].i =   0.000000
13 a[13].r =   0.000000  a[13].i =   0.000000  b[13].r =   0.000000  b[13].i =   0.000000
14 a[14].r =   0.000000  a[14].i =   0.000000  b[14].r =   0.000000  b[14].i =   0.000000
15 a[15].r =   0.000000  a[15].i =   0.000000  b[15].r =   0.000000  b[15].i =   0.000000
fft(nx,a) fft(nx,b)
 0 a[ 0].r =  30.000000  a[ 0].i =   0.000000  b[ 0].r =  44.000000  b[ 0].i =   0.000000
 1 a[ 8].r =  -7.137071  a[ 8].i = -20.109358  b[ 8].r =  17.920298  b[ 8].i = -24.812274
 2 a[ 4].r =  -2.000000  a[ 4].i =   9.656854  b[ 4].r =   4.707107  b[ 4].i =  -9.949747
 3 a[12].r =   4.380086  a[12].i =  -5.986423  b[12].r =   5.530124  b[12].i =  -7.699802
 4 a[ 2].r =  -2.000000  a[ 2].i =   4.000000  b[ 2].r =   5.000000  b[ 2].i =  -3.000000
 5 a[10].r =   5.276769  a[10].i =  -2.672715  b[10].r =   5.398808  b[10].i =  -4.971880
 6 a[ 6].r =  -2.000000  a[ 6].i =   1.656854  b[ 6].r =   3.292893  b[ 6].i =   0.050253
 7 a[14].r =   5.480217  a[14].i =  -0.795649  b[14].r =   7.150769  b[14].i =  -2.084352
 8 a[ 1].r =  -2.000000  a[ 1].i =   0.000000  b[ 1].r =   2.000000  b[ 1].i =   0.000000
 9 a[ 9].r =   5.480217  a[ 9].i =   0.795649  b[ 9].r =   7.150769  b[ 9].i =   2.084352
10 a[ 5].r =  -2.000000  a[ 5].i =  -1.656854  b[ 5].r =   3.292893  b[ 5].i =  -0.050253
11 a[13].r =   5.276769  a[13].i =   2.672715  b[13].r =   5.398808  b[13].i =   4.971880
12 a[ 3].r =  -2.000000  a[ 3].i =  -4.000000  b[ 3].r =   5.000000  b[ 3].i =   3.000000
13 a[11].r =   4.380086  a[11].i =   5.986423  b[11].r =   5.530124  b[11].i =   7.699802
14 a[ 7].r =  -2.000000  a[ 7].i =  -9.656854  b[ 7].r =   4.707107  b[ 7].i =   9.949747
15 a[15].r =  -7.137071  a[15].i =  20.109358  b[15].r =  17.920298  b[15].i =  24.812274

2値fft結果の分離と合成
input data  n = 16 = 1<<4
input data a.i <- b.r
 0 a[ 0].r =   2.000000  a[ 0].i =   9.000000
 1 a[ 1].r =   1.000000  a[ 1].i =   8.000000
 2 a[ 2].r =   2.000000  a[ 2].i =   7.000000
 3 a[ 3].r =   3.000000  a[ 3].i =   6.000000
 4 a[ 4].r =   4.000000  a[ 4].i =   5.000000
 5 a[ 5].r =   5.000000  a[ 5].i =   4.000000
 6 a[ 6].r =   6.000000  a[ 6].i =   2.000000
 7 a[ 7].r =   7.000000  a[ 7].i =   3.000000
 8 a[ 8].r =   0.000000  a[ 8].i =   0.000000
 9 a[ 9].r =   0.000000  a[ 9].i =   0.000000
10 a[10].r =   0.000000  a[10].i =   0.000000
11 a[11].r =   0.000000  a[11].i =   0.000000
12 a[12].r =   0.000000  a[12].i =   0.000000
13 a[13].r =   0.000000  a[13].i =   0.000000
14 a[14].r =   0.000000  a[14].i =   0.000000
15 a[15].r =   0.000000  a[15].i =   0.000000
fft(nx,a)
 0 a[ 0].r =  30.000000  a[ 0].i =  44.000000
 1 a[ 8].r =  17.675203  a[ 8].i =  -2.189060
 2 a[ 4].r =   7.949747  a[ 4].i =  14.363961
 3 a[12].r =  12.079887  a[12].i =  -0.456299
 4 a[ 2].r =   1.000000  a[ 2].i =   9.000000
 5 a[10].r =  10.248648  a[10].i =   2.726093
 6 a[ 6].r =  -2.050253  a[ 6].i =   4.949747
 7 a[14].r =   7.564569  a[14].i =   6.355120
 8 a[ 1].r =  -2.000000  a[ 1].i =   2.000000
 9 a[ 9].r =   3.395865  a[ 9].i =   7.946419
10 a[ 5].r =  -1.949747  a[ 5].i =   1.636039
11 a[13].r =   0.304889  a[13].i =   8.071523
12 a[ 3].r =  -5.000000  a[ 3].i =   1.000000
13 a[11].r =  -3.319716  a[11].i =  11.516547
14 a[ 7].r = -11.949747  a[ 7].i =  -4.949747
15 a[15].r = -31.949345  a[15].i =  38.029656
d_a2ab(nx,a,b)
 0 a[ 0].r =  30.000000  a[ 0].i =   0.000000  b[ 0].r =  44.000000  b[ 0].i =   0.000000
 1 a[ 8].r =  -7.137071  a[ 8].i = -20.109358  b[ 8].r =  17.920298  b[ 8].i = -24.812274
 2 a[ 4].r =  -2.000000  a[ 4].i =   9.656854  b[ 4].r =   4.707107  b[ 4].i =  -9.949747
 3 a[12].r =   4.380086  a[12].i =  -5.986423  b[12].r =   5.530124  b[12].i =  -7.699802
 4 a[ 2].r =  -2.000000  a[ 2].i =   4.000000  b[ 2].r =   5.000000  b[ 2].i =  -3.000000
 5 a[10].r =   5.276769  a[10].i =  -2.672715  b[10].r =   5.398808  b[10].i =  -4.971880
 6 a[ 6].r =  -2.000000  a[ 6].i =   1.656854  b[ 6].r =   3.292893  b[ 6].i =   0.050253
 7 a[14].r =   5.480217  a[14].i =  -0.795649  b[14].r =   7.150769  b[14].i =  -2.084352
 8 a[ 1].r =  -2.000000  a[ 1].i =   0.000000  b[ 1].r =   2.000000  b[ 1].i =   0.000000
 9 a[ 9].r =   5.480217  a[ 9].i =   0.795649  b[ 9].r =   7.150769  b[ 9].i =   2.084352
10 a[ 5].r =  -2.000000  a[ 5].i =  -1.656854  b[ 5].r =   3.292893  b[ 5].i =  -0.050253
11 a[13].r =   5.276769  a[13].i =   2.672715  b[13].r =   5.398808  b[13].i =   4.971880
12 a[ 3].r =  -2.000000  a[ 3].i =  -4.000000  b[ 3].r =   5.000000  b[ 3].i =   3.000000
13 a[11].r =   4.380086  a[11].i =   5.986423  b[11].r =   5.530124  b[11].i =   7.699802
14 a[ 7].r =  -2.000000  a[ 7].i =  -9.656854  b[ 7].r =   4.707107  b[ 7].i =   9.949747
15 a[15].r =  -7.137071  a[15].i =  20.109358  b[15].r =  17.920298  b[15].i =  24.812274
d_ab2a(nx,a,b)
 0 a[ 0].r =  30.000000  a[ 0].i =  44.000000
 1 a[ 8].r =  17.675203  a[ 8].i =  -2.189060
 2 a[ 4].r =   7.949747  a[ 4].i =  14.363961
 3 a[12].r =  12.079887  a[12].i =  -0.456299
 4 a[ 2].r =   1.000000  a[ 2].i =   9.000000
 5 a[10].r =  10.248648  a[10].i =   2.726093
 6 a[ 6].r =  -2.050253  a[ 6].i =   4.949747
 7 a[14].r =   7.564569  a[14].i =   6.355120
 8 a[ 1].r =  -2.000000  a[ 1].i =   2.000000
 9 a[ 9].r =   3.395865  a[ 9].i =   7.946419
10 a[ 5].r =  -1.949747  a[ 5].i =   1.636039
11 a[13].r =   0.304889  a[13].i =   8.071523
12 a[ 3].r =  -5.000000  a[ 3].i =   1.000000
13 a[11].r =  -3.319716  a[11].i =  11.516547
14 a[ 7].r = -11.949747  a[ 7].i =  -4.949747
15 a[15].r = -31.949345  a[15].i =  38.029656

使うとしても分離・乗算・合成を同時に行う事になる。組み合わせは複数あるので状況により作成。

2013年2月24日日曜日

fftのソース

現状のfftソースを晒してみる。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
typedef struct {
    double r ;
    double i ;
} ad_t ;
/* =====  fft ======== */
void *fft(int nx ,ad_t a[])
{
    int  i, k, m, n;
    double tmpr, tmpi, r, cosw, sinw;
    n = 1<<nx;
    r =  -M_PI / (double)n;
    for(m = n/2; m > 0  ; m /= 2 ) {
        r *= 2;
        for(k = 0 ; k < m; k++) {
            cosw = cos(r*(double)k);
            sinw = sin(r*(double)k);
            for(i= k ; i < n; i += m*2) {
                tmpr = a[i].r - a[m+i].r;
                tmpi = a[i].i - a[m+i].i;
                a[i].r = a[i].r + a[m+i].r;
                a[i].i = a[i].i + a[m+i].i;
                a[m+i].r = tmpr * cosw - tmpi * sinw;
                a[m+i].i = tmpi * cosw + tmpr * sinw;
            }
        }
    }
return;
}
/* =====  ifft ======== */
void *ifft(int nx,ad_t a[])
{
    int  i, k, m,n;
    double tmpr, tmpi, r, cosw, sinw;
    n = 1<< nx ;
    r = M_PI ;
    for(m = 1; m < n ; m *= 2 ) {
        for(k = 0; k < m; k++) {
            cosw = cos(r*(double)k);
            sinw = sin(r*(double)k);
            for(i= k ; i < n; i += m*2) {
                tmpr = cosw * a[m+i].r - sinw * a[m+i].i;
                tmpi = cosw * a[m+i].i + sinw * a[m+i].r;
                a[m+i].r = a[i].r - tmpr ;
                a[m+i].i = a[i].i - tmpi;
                a[i].r = a[i].r + tmpr ;
                a[i].i = a[i].i + tmpi;
            }
        }
        r *= 0.5;
    }
    return;
}
/* a * b -> a */
void *ri_mul(int nx,ad_t a[],ad_t b[])
{
    int m ;
    double tmpa , tmpb ;
    a[0].r = a[0].r*b[0].r;
    a[0].i = a[0].i*b[0].i;
    for(m=1;m<(1<<nx);m++){
        tmpa = a[m].r;
        tmpb = b[m].r;
        a[m].r = tmpa*tmpb   - a[m].i*b[m].i;
        a[m].i = tmpa*b[m].i + a[m].i*tmpb;
    }
}
/* スケーリングと繰越 n-2 -> 0 方向 */
void *ri_carry(int nx,ad_t a[],int redex)
{
    double pn,tmp;
    int n , m;
    long car , t;
    n = 1<<nx ;
    pn = 1.0/(double)n;
    car = 0 ;
    for(m=n-2;m>=0;m--){
        tmp = a[m].r*pn + (double)car  ;
        if( tmp >= 0.0 ){
            t = (long)(tmp+0.5) ;
            car = t / redex ;
            a[m].r =(double)( t % redex );
        }else{
            t = (long)(tmp-0.5) ;
            car = t / redex - 1;
            a[m].r =(double)( t % redex + redex );
        }
    }
    a[n-1].r=(double)car;
}
/* スケーリングと繰越 0 -> n-2 方向 */
void *ri_carry_r(int nx,ad_t a[],int redex)
{
    double pn,tmp;
    int n , m;
    long car , t;
    n = 1<<nx ;
    pn = 1.0/(double)n;
    car = 0 ;
    for(m=0;m<n-1;m++){
        tmp = a[m].r*pn + (double)car  ;
        if( tmp >= 0.0 ){
            t = (long)(tmp+0.5) ;
            car = t / redex ;
            a[m].r =(double)( t % redex );
        }else{
            t = (long)(tmp-0.5) ;
            car = t / redex - 1;
            a[m].r =(double)( t % redex + redex );
        }
    }
    a[n-1].r=(double)car;
}

/* 任意の値のビット反転 */
inline unsigned int bitrv_v(int nx, unsigned int a )
{
    a = ((a & 0x55555555)<< 1 ) | ((a >> 1) & 0x55555555  );
    a = ((a & 0x33333333)<< 2 ) | ((a >> 2) & 0x33333333  );
    a = ((a & 0x0f0f0f0f)<< 4 ) | ((a >> 4) & 0x0f0f0f0f  );
    a = ((a & 0x00ff00ff)<< 8 ) | ((a >> 8) & 0x00ff00ff  );
    a = ((a & 0x0000ffff)<<16 ) | ((a >>16) & 0x0000ffff  );
    return (a >> (32-nx)) ;
}
/* ビット反転 インクリメンタル */
inline unsigned int inc_rvbit(int nx , unsigned int a )
{
    unsigned int rb ;
    rb = 1<<nx;
    do {
         rb >>= 1;
         a ^= rb;       /* xor は、繰越なしで1bitの足し算したものと同じ */
    } while ( rb > a ); /* xor の結果が、1の時終わり */
    return a;
}
/* ビット反転位置のデータ入れ替え */
void *bitrv(int nx , ad_t a[])
{
    int i ,j;
    int nm1 , nh ;
    ad_t t;
    nh = 1<<(nx-1);
    nm1 = (1<<nx) -1 ;
    j = 0 ;
    for (i = 1; i < nh ; i++) {
        j = inc_rvbit(nx , j );
        if (j > i) {
            t = a[j];
            a[j] = a[i];
            a[i] = t;
            if( j < nh ){
                t = a[nm1-j];
                a[nm1-j]=a[nm1-i];
                a[nm1-i]=t;
            }
        }
    }
}


以下動作確認
input data  n = 16 = 1<<4
 0 a[ 0].r =   2.000000  a[ 0].i =   0.000000  b[ 0].r =   9.000000  b[ 0].i =   0.000000
 1 a[ 1].r =   1.000000  a[ 1].i =   0.000000  b[ 1].r =   8.000000  b[ 1].i =   0.000000
 2 a[ 2].r =   2.000000  a[ 2].i =   0.000000  b[ 2].r =   7.000000  b[ 2].i =   0.000000
 3 a[ 3].r =   3.000000  a[ 3].i =   0.000000  b[ 3].r =   6.000000  b[ 3].i =   0.000000
 4 a[ 4].r =   4.000000  a[ 4].i =   0.000000  b[ 4].r =   5.000000  b[ 4].i =   0.000000
 5 a[ 5].r =   5.000000  a[ 5].i =   0.000000  b[ 5].r =   4.000000  b[ 5].i =   0.000000
 6 a[ 6].r =   6.000000  a[ 6].i =   0.000000  b[ 6].r =   2.000000  b[ 6].i =   0.000000
 7 a[ 7].r =   7.000000  a[ 7].i =   0.000000  b[ 7].r =   3.000000  b[ 7].i =   0.000000
 8 a[ 8].r =   0.000000  a[ 8].i =   0.000000  b[ 8].r =   0.000000  b[ 8].i =   0.000000
 9 a[ 9].r =   0.000000  a[ 9].i =   0.000000  b[ 9].r =   0.000000  b[ 9].i =   0.000000
10 a[10].r =   0.000000  a[10].i =   0.000000  b[10].r =   0.000000  b[10].i =   0.000000
11 a[11].r =   0.000000  a[11].i =   0.000000  b[11].r =   0.000000  b[11].i =   0.000000
12 a[12].r =   0.000000  a[12].i =   0.000000  b[12].r =   0.000000  b[12].i =   0.000000
13 a[13].r =   0.000000  a[13].i =   0.000000  b[13].r =   0.000000  b[13].i =   0.000000
14 a[14].r =   0.000000  a[14].i =   0.000000  b[14].r =   0.000000  b[14].i =   0.000000
15 a[15].r =   0.000000  a[15].i =   0.000000  b[15].r =   0.000000  b[15].i =   0.000000
fft(nx,a) fft(nx,b)
 0 a[ 0].r =  30.000000  a[ 0].i =   0.000000  b[ 0].r =  44.000000  b[ 0].i =   0.000000
 1 a[ 8].r =  -7.137071  a[ 8].i = -20.109358  b[ 8].r =  17.920298  b[ 8].i = -24.812274
 2 a[ 4].r =  -2.000000  a[ 4].i =   9.656854  b[ 4].r =   4.707107  b[ 4].i =  -9.949747
 3 a[12].r =   4.380086  a[12].i =  -5.986423  b[12].r =   5.530124  b[12].i =  -7.699802
 4 a[ 2].r =  -2.000000  a[ 2].i =   4.000000  b[ 2].r =   5.000000  b[ 2].i =  -3.000000
 5 a[10].r =   5.276769  a[10].i =  -2.672715  b[10].r =   5.398808  b[10].i =  -4.971880
 6 a[ 6].r =  -2.000000  a[ 6].i =   1.656854  b[ 6].r =   3.292893  b[ 6].i =   0.050253
 7 a[14].r =   5.480217  a[14].i =  -0.795649  b[14].r =   7.150769  b[14].i =  -2.084352
 8 a[ 1].r =  -2.000000  a[ 1].i =   0.000000  b[ 1].r =   2.000000  b[ 1].i =   0.000000
 9 a[ 9].r =   5.480217  a[ 9].i =   0.795649  b[ 9].r =   7.150769  b[ 9].i =   2.084352
10 a[ 5].r =  -2.000000  a[ 5].i =  -1.656854  b[ 5].r =   3.292893  b[ 5].i =  -0.050253
11 a[13].r =   5.276769  a[13].i =   2.672715  b[13].r =   5.398808  b[13].i =   4.971880
12 a[ 3].r =  -2.000000  a[ 3].i =  -4.000000  b[ 3].r =   5.000000  b[ 3].i =   3.000000
13 a[11].r =   4.380086  a[11].i =   5.986423  b[11].r =   5.530124  b[11].i =   7.699802
14 a[ 7].r =  -2.000000  a[ 7].i =  -9.656854  b[ 7].r =   4.707107  b[ 7].i =   9.949747
15 a[15].r =  -7.137071  a[15].i =  20.109358  b[15].r =  17.920298  b[15].i =  24.812274
ri_mul(nx,a,b)
 0 a[ 0].r = 1320.000000  a[ 0].i =   0.000000
 1 a[ 8].r = -626.857348  a[ 8].i = -183.278730
 2 a[ 4].r =  86.669048  a[ 4].i =  65.355339
 3 a[12].r = -21.871852  a[12].i = -66.831453
 4 a[ 2].r =   2.000000  a[ 2].i =  26.000000
 5 a[10].r =  15.199846  a[10].i = -40.664931
 6 a[ 6].r =  -6.669048  a[ 6].i =   5.355339
 7 a[14].r =  37.529354  a[14].i = -17.112207
 8 a[ 1].r =  -4.000000  a[ 1].i =   0.000000
 9 a[ 9].r =  37.529354  a[ 9].i =  17.112207
10 a[ 5].r =  -6.669048  a[ 5].i =  -5.355339
11 a[13].r =  15.199846  a[13].i =  40.664931
12 a[ 3].r =   2.000000  a[ 3].i = -26.000000
13 a[11].r = -21.871852  a[11].i =  66.831453
14 a[ 7].r =  86.669048  a[ 7].i = -65.355339
15 a[15].r = -626.857348  a[15].i = 183.278730
ifft(nx,a)
 0 a[ 0].r = 288.000000  a[ 0].i =  -0.000000
 1 a[ 1].r = 400.000000  a[ 1].i =  -0.000000
 2 a[ 2].r = 640.000000  a[ 2].i =  -0.000000
 3 a[ 3].r = 992.000000  a[ 3].i =   0.000000
 4 a[ 4].r = 1440.000000  a[ 4].i =  -0.000000
 5 a[ 5].r = 1968.000000  a[ 5].i =   0.000000
 6 a[ 6].r = 2528.000000  a[ 6].i =  -0.000000
 7 a[ 7].r = 3216.000000  a[ 7].i =  -0.000000
 8 a[ 8].r = 2672.000000  a[ 8].i =   0.000000
 9 a[ 9].r = 2208.000000  a[ 9].i =   0.000000
10 a[10].r = 1744.000000  a[10].i =   0.000000
11 a[11].r = 1296.000000  a[11].i =  -0.000000
12 a[12].r = 880.000000  a[12].i =  -0.000000
13 a[13].r = 512.000000  a[13].i =  -0.000000
14 a[14].r = 336.000000  a[14].i =  -0.000000
15 a[15].r =  -0.000000  a[15].i =   0.000000
copy a -> b
ri_carry(nx,a,10) ri_carry_r(nx,b,10)
 0 a[15].r =   2.000000  a[15].i =   0.000000  b[15].r =   2.000000  b[15].i =   0.000000
 1 a[ 0].r =   0.000000  a[ 0].i =  -0.000000  b[14].r =   4.000000  b[14].i =  -0.000000
 2 a[ 1].r =   9.000000  a[ 1].i =  -0.000000  b[13].r =   8.000000  b[13].i =  -0.000000
 3 a[ 2].r =   7.000000  a[ 2].i =  -0.000000  b[12].r =   4.000000  b[12].i =  -0.000000
 4 a[ 3].r =   2.000000  a[ 3].i =   0.000000  b[11].r =   3.000000  b[11].i =  -0.000000
 5 a[ 4].r =   4.000000  a[ 4].i =  -0.000000  b[10].r =   4.000000  b[10].i =   0.000000
 6 a[ 5].r =   0.000000  a[ 5].i =   0.000000  b[ 9].r =   6.000000  b[ 9].i =   0.000000
 7 a[ 6].r =   9.000000  a[ 6].i =  -0.000000  b[ 8].r =   8.000000  b[ 8].i =   0.000000
 8 a[ 7].r =   9.000000  a[ 7].i =  -0.000000  b[ 7].r =   8.000000  b[ 7].i =  -0.000000
 9 a[ 8].r =   1.000000  a[ 8].i =   0.000000  b[ 6].r =   1.000000  b[ 6].i =  -0.000000
10 a[ 9].r =   9.000000  a[ 9].i =   0.000000  b[ 5].r =   2.000000  b[ 5].i =   0.000000
11 a[10].r =   7.000000  a[10].i =   0.000000  b[ 4].r =   6.000000  b[ 4].i =  -0.000000
12 a[11].r =   6.000000  a[11].i =  -0.000000  b[ 3].r =   6.000000  b[ 3].i =   0.000000
13 a[12].r =   8.000000  a[12].i =  -0.000000  b[ 2].r =   2.000000  b[ 2].i =  -0.000000
14 a[13].r =   4.000000  a[13].i =  -0.000000  b[ 1].r =   6.000000  b[ 1].i =  -0.000000
15 a[14].r =   1.000000  a[14].i =  -0.000000  b[ 0].r =   8.000000  b[ 0].i =  -0.000000
a   21234567 * 98765423 = 2097240991976841
b   76543212 * 32456789 = 2484346881266268

fft一本で逆変換(ifft)も可能だが、ビット反転位置のデータ入れ替えを省略するため、fft,ifftを使っている。
bitrv は、調査以外では使わないのでまともに動くかちと不安。
ri_carry と ri_carry_r は、繰越の方向が異なるだけ。どちらを使うかは状況による。
現在、実数fftについて検証中。メモリー使用量が半分ですむならそれに越したことは無い。

円周率計算のプログラムを作っているのだが・・

まだこのアカウント生きてたwww 掲題の件、世界記録を目指すとかでは無いです。
チューニングとスレッドの勉強のため負荷のかかるものを探したらそうなった。
目標     100億桁以上
アルゴリズム アークタンジェントのテーラ展開ガウスの式。
環境
 CPU i7-3820 4core
 メモリ 64GB
 ホストOS Win7
 VM VirtualBox
 VOS Fedora 17
 VCPU 4~6
 VMEM 54GB
コンパイラ gcc
言語   C
環境依存の最適化を行うけど、アセンブラは使わない予定
今のところ GPU も使う予定なし。
主にfftのチューニングになるのかな?