-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathREADME.txt
330 lines (244 loc) · 14.5 KB
/
README.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
This software is released under the MIT License, see LICENSE.txt.
このソフトウェアは MIT ライセンスのもとにリリースされています。
詳細は LICENSE.txt を見てください。
【使用上の注意】
OTFFT は、コンパイルする環境に最適化されます。つまり、AVX が有効な環境で
コンパイルすれば、AVX を使うようになります。このバイナリを AVX をサポート
しない環境で動かせば、当然、例外を起こして落ちます。そのため、コンパイルした
バイナリを、広く一般に配布するような利用形態には、OTFFT は向きません。
OTFFT は、数値計算のプログラムをソースからコンパイルするような利用形態を
想定しています。
【必要なファイル】
otfft-6.5.tar.gz を展開すると、いくつかのファイルができますが、
OTFFT を使うのに必要なファイルはその中の otfft フォルダとその中身です。
このうち、
otfft_setup.h
otfft_fwd.h
otfft_fwd0.h
otfft_inv.h
otfft_invn.h
は自動生成されるファイルで、自分の環境で最大の効果を発揮したければ
ffttune を使って生成し直してやる必要があります。まず、otfft フォルダで
make ffttune
を実行し、ffttune コマンドを作ります。Makefile は自分の環境に合わせて
いじってください。C++ の template によるメタプログラミング技法を用いているので、
コンパイルには非常に長い時間がかかります。次に
./ffttune
と ffttune コマンドを実行します。すると先に示したファイルの自動生成が
始まります。しばらくお待ちください。このとき、Core i7(4 コア 8 スレッド)
の環境の場合、環境変数 OMP_NUM_THREADS を 7 に設定する必要があるかもしれません。
一部の Linux では、8 スレッドで動かすと謎の速度低下を起こします。
OTFFT は内部で幾つかのアルゴリズムを使い分けていますが、
どのアルゴリズムがどのサイズで一番速いか、実際に計測して決定します。
しかし、パソコンは FFT だけを黙々と実行しているわけではなく、
裏で色々なプロセスが動いています。計測には常に突発的なゆれが混入します。
そのため、1回の計測で最適な組み合わせが求まるとは限りません。
納得いかない場合は何度か ffttune を実行し直してください。
自動生成が終わったら、次は otfft.o ファイルを生成します。otfft フォルダで
次のコマンドを実行します。
make otfft.o
C++ の template によるメタプログラミング技法を用いているので、コンパイルには
非常に長い時間がかかります。辛抱強くお待ちください。
これで準備は終わりです。バージョン 3.2 までは OTFFT は全てヘッダファイルで
実装されていたため、ご自分のバイナリで利用する場合でも、いちいちコンパイル
し直されていました。そのため、上記のように非常に長いコンパイル時間が必要でした。
バージョン 4.0 から OTFFT 自身のコンパイルは事前に済ませておくようにしました。
ですから、ご自分のバイナリで利用する場合はリンクするだけです。
【シングルスレッドモード】
OTFFT はデフォルトでは OpenMP によるマルチスレッドで動作します。しかし、
中にはマルチスレッドの実装は、自前の仕組みを使いたい人もいるかもしれません。
そこで、シングルスレッドで動作するモードも用意しました。OTFFT は
DO_SINGLE_THREAD マクロを定義してコンパイルすると、シングルスレッドモードに
なります。シングルスレッドモードにしたい場合は、otfft/otfft_misc.h で定義して、
ffttune コマンドの生成から始めてください。
【ご自分のバイナリのコンパイル方法】
さて、otfft.o の生成が終われば、あとは otfft/otfft.h をインクルードし、
コンパイラのインクルードパスに otfft フォルダのあるフォルダを加えて、
otfft.o ファイルを目的のバイナリにリンクしてやれば OTFFT は使えます。
g++ でコンパイルするには、-lgomp や -liomp5 など OpenMP のランタイムも
リンクする必要があります。例えば、カレントフォルダに otfft フォルダがあるなら
以下のようにコンパイルします。
g++ -c -Ofast hello.cpp
g++ hello.o otfft/otfft.o -lgomp -o hello
【複素離散フーリエ変換】
サイズ N の複素離散フーリエ変換を実行するには、
#include "otfft/otfft.h"
using OTFFT::complex_t;
using OTFFT::simd_malloc;
using OTFFT::simd_free;
void f(int N)
{
complex_t* x = (complex_t*) simd_malloc(N*sizeof(complex_t));
// 何かする...
OTFFT::FFT fft(N); // FFT オブジェクトの作成
fft.fwd(x); // 複素離散フーリエ変換を実行。x が入力かつ出力
// 何かする...
simd_free(x);
}
のようにします。N は最大 2^24 (2の24乗)までで、2のべき乗である必要があります。
complex_t は
struct complex_t
{
double Re, Im;
complex_t() : Re(0), Im(0) {}
complex_t(const double& x) : Re(x), Im(0) {}
complex_t(const double& x, const double& y) : Re(x), Im(y) {}
complex_t(const std::complex<double>& z) : Re(z.real()), Im(z.imag()) {}
operator std::complex<double>() { return std::complex<double>(Re, Im); }
// その他のメンバ関数...
};
のように定義されています。
フーリエ変換(fwd)には係数 1/N が掛かっています。もしそれが不都合なら係数の
掛かっていない(fwd0)もあります。以下のようなメンバ関数が用意されています。
fwd0(x) 離散フーリエ変換(正規化無し)
fwd(x) 離散フーリエ変換(1/N による正規化付き)
fwdn(x) 離散フーリエ変換(1/N による正規化付き)
inv0(x) 逆離散フーリエ変換(正規化無し)
inv(x) 逆離散フーリエ変換(正規化無し)
invn(x) 逆離散フーリエ変換(1/N による正規化付き)
FFT のサイズを変更したい場合は、
fft.setup(2 * N);
のようにします。
OTFFT は Stockham のアルゴリズムを使っていますので、実行には入力系列と
同じサイズの作業領域が必要です。普通は内部でその領域を確保しますが、
マルチスレッドのプログラムを組む場合など、それだと都合が悪い場合があります。
そんな時は外部から作業領域を渡すバージョンもあります。次のように使います。
#include "otfft/otfft.h"
using OTFFT::complex_t;
using OTFFT::simd_malloc;
using OTFFT::simd_free;
void f(int N)
{
complex_t* x = (complex_t*) simd_malloc(N*sizeof(complex_t));
complex_t* y = (complex_t*) simd_malloc(N*sizeof(complex_t));
// 何かする...
OTFFT::FFT0 fft(N);
fft.fwd(x, y); // x が入力、y が作業領域
// 何かする...
fft.inv(x, y); // x が入力、y が作業領域
// 何かする...
simd_free(y);
simd_free(x);
}
OTFFT::FFT だったところが OTFFT::FFT0 になっていることに注意してください。
【実離散フーリエ変換】
バージョン 3.0 から実離散フーリエ変換、離散コサイン変換(DCT-II)、
Bluestein's FFT(任意サイズの FFT) もサポートするようになりました。
サイズ N の実離散フーリエ変換を実行するには以下のようにします。
#include "otfft/otfft.h"
using OTFFT::complex_t;
using OTFFT::simd_malloc;
using OTFFT::simd_free;
void f(int N)
{
double* x = (double*) simd_malloc(N*sizeof(double));
complex_t* y = (complex_t*) simd_malloc(N*sizeof(complex_t));
// 何かする...
OTFFT::RFFT rfft(N);
rfft.fwd(x, y); // 実離散フーリエ変換を実行。x が入力、y が出力
// 何かする...
simd_free(y);
simd_free(x);
}
実離散フーリエ変換には以下のようなメンバ関数が用意されています。
fwd0(x, y) 実離散フーリエ変換(正規化無し) x:入力、y:出力
fwd(x, y) 実離散フーリエ変換(1/N による正規化付き) x:入力、y:出力
fwdn(x, y) 実離散フーリエ変換(1/N による正規化付き) x:入力、y:出力
inv0(y, x) 逆実離散フーリエ変換(正規化無し) y:入力、x:出力
inv(y, x) 逆実離散フーリエ変換(正規化無し) y:入力、x:出力
invn(y, x) 逆実離散フーリエ変換(1/N による正規化付き) y:入力、x:出力
逆実離散フーリエ変換は複素系列 y を受け取って、実系列 x を返します。
y が斜対称、すなわち y[N-k] == conj(y[k]) でないと正しい結果を返しません。
また、逆実離散フーリエ変換は入力 y を破壊します。保存しておきたい場合は、
コピーを取っておく必要があります。指定できるサイズは2のべき乗で
2^25 以下です。内部的には N/2 のサイズの複素 FFT で実装されています。
実離散フーリエ変換は、作業領域として出力系列を使います。
そして逆実離散フーリエ変換は、作業領域として入力系列を使います。
マルチスレッドでプログラムする場合も、
それぞれのスレッド専用の入力と出力を与えてやれば OK です。
【離散コサイン変換(DCT-II)】
サイズ N の離散コサイン変換(DCT-II)を実行するには以下のようにします。
#include "otfft/otfft.h"
using OTFFT::complex_t;
using OTFFT::simd_malloc;
using OTFFT::simd_free;
void f(int N)
{
double* x = (double*) simd_malloc(N*sizeof(double));
// 何かする...
OTFFT::DCT dct(N);
dct.fwd(x); // DCT-II を実行する。x が入力かつ出力
// 何かする...
simd_free(x);
}
離散コサイン変換には以下のようなメンバ関数が用意されています。
fwd0(x) 離散コサイン変換(正規化無し)
fwd(x) 離散コサイン変換(1/N による正規化付き)
fwdn(x) 離散コサイン変換(1/N による正規化付き)
inv0(x) 逆離散コサイン変換(正規化無し)
inv(x) 逆離散コサイン変換(正規化無し)
invn(x) 逆離散コサイン変換(1/N による正規化付き)
離散コサイン変換は DCT-II を採用しています。ただし、直交化はしていません。
指定できるサイズは2のべき乗で 2^25 以下です。内部的には N/2 のサイズの複素
FFT で実装されています。マルチスレッドで使う場合の作業領域を外部から与える
バージョンもあります。以下のように使います。
#include "otfft/otfft.h"
using OTFFT::complex_t;
using OTFFT::simd_malloc;
using OTFFT::simd_free;
void f(int N)
{
double* x = (double*) simd_malloc(N*sizeof(double));
double* y = (double*) simd_malloc(N*sizeof(double));
complex_t* z = (complex_t*) simd_malloc(N*sizeof(complex_t));
// 何かする...
OTFFT::DCT0 dct(N);
dct.fwd(x, y, z); // DCT-II を実行する。x が入力かつ出力、y,z が作業領域
// 何かする...
simd_free(z);
simd_free(y);
simd_free(x);
}
OTFFT::DCT だったところが OTFFT::DCT0 になっていることに注意してください。
【Bluestein's FFT】
Bluestein's FFT(任意サイズ N の FFT) を実行するには以下のようにします。
#include "otfft/otfft.h"
using OTFFT::complex_t;
using OTFFT::simd_malloc;
using OTFFT::simd_free;
void f(int N)
{
complex_t* x = (complex_t*) simd_malloc(N*sizeof(complex_t));
// 何かする...
OTFFT::Bluestein bst(N); // N は任意の自然数。ただし、2^23 より小さい。
bst.fwd(x); // Bluestein's FFT を実行する。x が入力かつ出力
// 何かする...
simd_free(x);
}
Bluestein's FFT では離散フーリエ変換のサイズを2のべき乗に限る必要は
ありません。例えば大きな素数のサイズでも計算量は O(N log N) になります。
指定出来るサイズの上限は 2^23 です。だたし、マルチスレッド用のメンバ関数は
用意されていません。どうしてもマルチスレッドで使いたい場合は、
動作確認はしていませんが、スレッドの数だけ Bluestein オブジェクトを生成し、
各スレッドでそれらを使い分ければ大丈夫と思います。当然、メモリをゴージャスに
使ってしまいます。
Bluestein's FFT には以下のようなメンバ関数が用意されています。
fwd0(x) 離散フーリエ変換(正規化無し)
fwd(x) 離散フーリエ変換(1/N による正規化付き)
fwdn(x) 離散フーリエ変換(1/N による正規化付き)
inv0(x) 逆離散フーリエ変換(正規化無し)
inv(x) 逆離散フーリエ変換(正規化無し)
invn(x) 逆離散フーリエ変換(1/N による正規化付き)
【ベンチマーク】
最後に、OTFFT にはベンチマークプログラムが付属しています。しかし、この
ベンチマークには FFTW3 と大浦さんのプログラムを用いているため、単独では動作
しません。まずは FFTW3 をインストールし、大浦さんのページ
http://www.kurims.kyoto-u.ac.jp/~ooura/fft-j.html
から大浦さんの FFT ライブラリをダウンロードしてください。そしてその中の
fftsg.c というファイルを fftbench1.cpp と同じフォルダに入れてください。
その上で以下のように fftbench1 コマンドと fftbench2 コマンドを make します。
make fftbench1 fftbench2
fftbench1 が計測時間に FFT オブジェクトの初期化を含まないベンチマーク、
fftbench2 が初期化を含むベンチマークです。以下のように実行してください。
./fftbench1
./fftbench2