Skip to content

Commit 5f2c0d2

Browse files
committed
Hide IFFT to factory with caching
1 parent 0d0128e commit 5f2c0d2

File tree

11 files changed

+256
-130
lines changed

11 files changed

+256
-130
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.10)
2-
project(dsplib LANGUAGES CXX VERSION 0.54.8)
2+
project(dsplib LANGUAGES CXX VERSION 0.54.9)
33

44
set(CMAKE_CXX_STANDARD 17)
55
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include/dsplib/ifft.h

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,48 @@
11
#pragma once
22

3-
#include <dsplib/types.h>
43
#include <dsplib/array.h>
5-
#include <dsplib/fft.h>
64
#include <memory>
75

86
namespace dsplib {
97

10-
class IfftPlan
8+
class BaseIfftPlanC
119
{
1210
public:
13-
IfftPlan(int n);
11+
virtual ~BaseIfftPlanC() = default;
12+
[[nodiscard]] virtual arr_cmplx solve(const arr_cmplx& x) const = 0;
13+
[[nodiscard]] virtual int size() const noexcept = 0;
14+
};
15+
16+
class BaseIfftPlanR
17+
{
18+
public:
19+
virtual ~BaseIfftPlanR() = default;
20+
[[nodiscard]] virtual arr_real solve(const arr_cmplx& x) const = 0;
21+
[[nodiscard]] virtual int size() const noexcept = 0;
22+
};
23+
24+
class IfftPlan : public BaseIfftPlanC
25+
{
26+
public:
27+
explicit IfftPlan(int n);
1428
arr_cmplx operator()(const arr_cmplx& x) const;
15-
[[nodiscard]] arr_cmplx solve(const arr_cmplx& x) const;
16-
[[nodiscard]] int size() const noexcept;
29+
[[nodiscard]] arr_cmplx solve(const arr_cmplx& x) const final;
30+
[[nodiscard]] int size() const noexcept final;
1731

1832
private:
19-
std::shared_ptr<FftPlan> _d;
33+
std::shared_ptr<BaseIfftPlanC> ifft_;
2034
};
2135

22-
class IfftPlanR
36+
class IfftPlanR : public BaseIfftPlanR
2337
{
2438
public:
25-
IfftPlanR(int n);
39+
explicit IfftPlanR(int n);
2640
arr_real operator()(const arr_cmplx& x) const;
27-
[[nodiscard]] arr_real solve(const arr_cmplx& x) const;
28-
[[nodiscard]] int size() const noexcept;
41+
[[nodiscard]] arr_real solve(const arr_cmplx& x) const final;
42+
[[nodiscard]] int size() const noexcept final;
2943

3044
private:
31-
const int _n;
32-
std::shared_ptr<FftPlan> _d;
33-
const std::vector<cmplx_t> _w; //exp(1i * 2 * pi / n)
45+
std::shared_ptr<BaseIfftPlanR> ifft_;
3446
};
3547

3648
/*!

lib/fft/cmplx-ifft.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
3+
#include <dsplib/ifft.h>
4+
5+
#include "fft/factory.h"
6+
7+
namespace dsplib {
8+
9+
class CmplxIfftPlan : public BaseIfftPlanC
10+
{
11+
public:
12+
explicit CmplxIfftPlan(int n)
13+
: fft_{create_fft_plan(n)} {
14+
}
15+
16+
arr_cmplx solve(const arr_cmplx& x) const final {
17+
const real_t m = real_t(1) / x.size();
18+
arr_cmplx y = x * m;
19+
_inplace_conj(y);
20+
y = fft_->solve(y);
21+
_inplace_conj(y);
22+
return y;
23+
}
24+
25+
int size() const noexcept final {
26+
return fft_->size();
27+
}
28+
29+
private:
30+
static void _inplace_conj(arr_cmplx& x) {
31+
for (auto& v : x) {
32+
v.im = -v.im;
33+
}
34+
}
35+
36+
std::shared_ptr<BaseFftPlanC> fft_;
37+
};
38+
39+
} // namespace dsplib

lib/fft/factory.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
#include <dsplib/fft.h>
2+
#include <dsplib/ifft.h>
23
#include <dsplib/math.h>
34
#include <dsplib/utils.h>
45

6+
#include "fft/cmplx-ifft.h"
57
#include "fft/fact-fft.h"
68
#include "fft/primes-fft.h"
79
#include "fft/factory.h"
810
#include "fft/pow2-fft.h"
911
#include "fft/real-fft.h"
12+
#include "fft/real-ifft.h"
1013
#include "fft/small-fft.h"
1114
#include "lru-cache.h"
1215

@@ -41,6 +44,14 @@ std::shared_ptr<BaseFftPlanR> _get_rfft_plan(int n) {
4144
return std::make_shared<FactorFFTPlanR>(n);
4245
}
4346

47+
std::shared_ptr<BaseIfftPlanC> _get_ifft_plan(int n) {
48+
return std::make_shared<CmplxIfftPlan>(n);
49+
}
50+
51+
std::shared_ptr<BaseIfftPlanR> _get_irfft_plan(int n) {
52+
return std::make_shared<RealIfftPlan>(n);
53+
}
54+
4455
} // namespace
4556

4657
//-------------------------------------------------------------------------------------------------
@@ -74,4 +85,24 @@ std::shared_ptr<BaseFftPlanR> create_rfft_plan(int n) {
7485
return cache.get(n);
7586
}
7687

88+
std::shared_ptr<BaseIfftPlanC> create_ifft_plan(int n) {
89+
thread_local LRUCache<int, std::shared_ptr<BaseIfftPlanC>> cache{FFT_CACHE_SIZE};
90+
if (!cache.exists(n)) {
91+
auto plan = _get_ifft_plan(n);
92+
cache.put(n, plan);
93+
return plan;
94+
}
95+
return cache.get(n);
96+
}
97+
98+
std::shared_ptr<BaseIfftPlanR> create_irfft_plan(int n) {
99+
thread_local LRUCache<int, std::shared_ptr<BaseIfftPlanR>> cache{FFT_CACHE_SIZE};
100+
if (!cache.exists(n)) {
101+
auto plan = _get_irfft_plan(n);
102+
cache.put(n, plan);
103+
return plan;
104+
}
105+
return cache.get(n);
106+
}
107+
77108
} // namespace dsplib

lib/fft/factory.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
#pragma once
22

3+
#include <dsplib/ifft.h>
34
#include <dsplib/fft.h>
45

56
namespace dsplib {
67

78
//instance or get cached fft plan
89

10+
//FFT
911
std::shared_ptr<BaseFftPlanC> create_fft_plan(int n);
10-
1112
std::shared_ptr<BaseFftPlanR> create_rfft_plan(int n);
1213

14+
//IFFT
15+
std::shared_ptr<BaseIfftPlanC> create_ifft_plan(int n);
16+
std::shared_ptr<BaseIfftPlanR> create_irfft_plan(int n);
17+
1318
} // namespace dsplib

lib/fft/fft.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
#include "fft/factory.h"
66

7-
#include <cassert>
8-
97
namespace dsplib {
108

119
//-------------------------------------------------------------------------------------------------

lib/fft/ifft.cpp

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,95 +2,42 @@
22
#include <dsplib/fft.h>
33
#include <dsplib/math.h>
44

5-
namespace dsplib {
6-
7-
namespace {
5+
#include "fft/factory.h"
86

9-
void _inplace_conj(arr_cmplx& x) {
10-
for (auto& v : x) {
11-
v.im = -v.im;
12-
}
13-
}
14-
15-
std::vector<cmplx_t> _irfft_coeffs(int n) noexcept {
16-
assert(n % 4 == 0);
17-
const int n4 = n / 4;
18-
const int n2 = n / 2;
19-
std::vector<cmplx_t> res(n / 2);
20-
res[0] = {1, 0};
21-
res[n4] = {0, 1};
22-
//use only first n/4 samples
23-
for (int i = 1; i < n4; ++i) {
24-
const auto v = std::cos(2 * pi * i / n);
25-
res[i].re = v;
26-
res[n2 - i].re = -v;
27-
res[n4 + i].im = v;
28-
res[n4 - i].im = v;
29-
}
30-
return res;
31-
}
32-
33-
} // namespace
7+
namespace dsplib {
348

359
//---------------------------------------------------------------------------------
3610
IfftPlan::IfftPlan(int n)
37-
: _d{std::make_shared<FftPlan>(n)} {
11+
: ifft_{create_ifft_plan(n)} {
3812
}
3913

4014
arr_cmplx IfftPlan::operator()(const arr_cmplx& x) const {
4115
return this->solve(x);
4216
}
4317

4418
arr_cmplx IfftPlan::solve(const arr_cmplx& x) const {
45-
const real_t m = real_t(1) / x.size();
46-
arr_cmplx y = x * m;
47-
_inplace_conj(y);
48-
y = _d->solve(y);
49-
_inplace_conj(y);
50-
return y;
19+
return ifft_->solve(x);
5120
}
5221

5322
int IfftPlan::size() const noexcept {
54-
return _d->size();
23+
return ifft_->size();
5524
}
5625

5726
//---------------------------------------------------------------------------------
5827
IfftPlanR::IfftPlanR(int n)
59-
: _n{n}
60-
, _d{std::make_shared<FftPlan>(n / 2)}
61-
, _w(_irfft_coeffs(n)) {
62-
DSPLIB_ASSERT(n % 2 == 0, "ifft size must be even");
28+
: ifft_{create_irfft_plan(n)} {
6329
}
6430

6531
arr_real IfftPlanR::operator()(const arr_cmplx& x) const {
6632
return this->solve(x);
6733
}
6834

6935
arr_real IfftPlanR::solve(const arr_cmplx& x) const {
70-
assert(_n % 2 == 0);
71-
DSPLIB_ASSERT((x.size() == _n) || (x.size() == _n / 2 + 1), "input size must be n/2+1 or n");
72-
73-
const real_t dn = real_t(1) / _n;
74-
std::vector<cmplx_t> Z(_n / 2);
75-
for (int i = 0; i < _n / 2; ++i) {
76-
const auto v = conj(x[_n / 2 - i]);
77-
const cmplx_t Xe = (x[i] + v) * dn;
78-
const cmplx_t Xo = (x[i] - v) * dn * _w[i];
79-
Z[i].re = Xe.re - Xo.im;
80-
Z[i].im = -Xe.im - Xo.re;
81-
}
82-
83-
const auto z = _d->solve(Z);
84-
std::vector<real_t> r(_n);
85-
for (int i = 0; i < _n / 2; ++i) {
86-
r[2 * i] = z[i].re;
87-
r[2 * i + 1] = -z[i].im;
88-
}
89-
return r;
36+
return ifft_->solve(x);
9037
}
9138

9239
int IfftPlanR::size() const noexcept {
93-
return _n;
40+
return ifft_->size();
9441
}
9542

9643
//---------------------------------------------------------------------------------
@@ -104,7 +51,6 @@ arr_real irfft(const arr_cmplx& x) {
10451
}
10552

10653
arr_real irfft(const arr_cmplx& x, int n) {
107-
//FIXIT: _irfft_coeffs overhead, use cache
10854
IfftPlanR plan(n);
10955
return plan(x);
11056
}

lib/fft/real-fft.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#pragma once
2+
3+
#include <dsplib/fft.h>
4+
#include <dsplib/utils.h>
5+
#include <dsplib/math.h>
6+
7+
#include "fft/real-fft.h"
8+
#include "fft/factory.h"
9+
10+
namespace dsplib {
11+
12+
RealFftPlan::RealFftPlan(int n)
13+
: n_{n}
14+
, fft_{create_fft_plan(n / 2)}
15+
, w_(expj(-2 * pi * arange(n / 2) / n).to_vec()) { //TODO: optimization (like in real ifft module)
16+
DSPLIB_ASSERT(n % 2 == 0, "FFT size must be even");
17+
}
18+
19+
arr_cmplx RealFftPlan::solve(const arr_real& x) const {
20+
using namespace std::complex_literals;
21+
DSPLIB_ASSERT(x.size() == n_, "Input size must be equal FFT size");
22+
const int n2 = n_ / 2;
23+
24+
arr_cmplx z(reinterpret_cast<const cmplx_t*>(x.data()), n2);
25+
const auto Z = fft_->solve(z * 0.5);
26+
27+
arr_cmplx res(n_);
28+
29+
{
30+
const auto Xe = Z[0] + conj(Z[0]);
31+
const auto Xo = (conj(Z[0]) - Z[0]) * w_[0];
32+
res[0].re = Xe.re - Xo.im;
33+
res[0].im = Xe.im + Xo.re;
34+
}
35+
36+
for (int i = 1; i < n2; ++i) {
37+
const auto Zc = conj(Z[n2 - i]);
38+
const auto Xe = Z[i] + Zc;
39+
const auto Xo = (Zc - Z[i]) * w_[i];
40+
res[i].re = Xe.re - Xo.im;
41+
res[i].im = Xe.im + Xo.re;
42+
res[n_ - i].re = res[i].re;
43+
res[n_ - i].im = -res[i].im;
44+
}
45+
46+
{
47+
const auto Xe = Z[0] + conj(Z[0]);
48+
const auto Xo = conj(Z[0]) - Z[0];
49+
res[n2] = Xe.re + Xo.im;
50+
}
51+
52+
return res;
53+
}
54+
55+
int RealFftPlan::size() const noexcept {
56+
return n_;
57+
}
58+
59+
} // namespace dsplib

0 commit comments

Comments
 (0)