Skip to content

Commit 3543769

Browse files
[Feature](mluOpExecFFT): add bluestein fft for complex-to-complex fft float dtype for support any size. (Cambricon#1236)
Co-authored-by: AndyQiao0828 <62641360+AndyQiao0828@users.noreply.github.com>
1 parent f7fee54 commit 3543769

10 files changed

Lines changed: 1451 additions & 170 deletions

File tree

185 KB
Loading

docs/design_docs/fft/fft.md

Lines changed: 173 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,83 @@ $$
105105

106106
可以将二维FFT看作是在输入的两个维度分别做一维FFT,因此其计算方式与一维FFT本质相同,这里不再对FFT计算细节进行赘述。
107107

108+
#### 1.2.1 bluestein 算法简述
109+
算法是一种用于计算任意长度离散傅里叶变换(DFT)的快速算法,通过将DFT转换为循环卷积实现计算优化。以下是其核心步骤及公式推导
110+
111+
##### 1.2.1.1 DFT二次项分解
112+
标准DFT表达式:
113+
114+
$$
115+
X[k] = \sum_{n=0}^{N-1} x[n] \cdot e^{-j\frac{2\pi}{N}kn}
116+
$$
117+
118+
通过引入二次项展开:
119+
120+
$$
121+
kn = \frac{1}{2}[k^2 + n^2 - (k-n)^2]
122+
$$
123+
124+
得到等效表达式:
125+
126+
$$
127+
e^{-j\frac{2\pi}{N}kn} = e^{-j\frac{\pi}{N}k^2} \cdot e^{j\frac{\pi}{N}(k-n)^2} \cdot e^{-j\frac{\pi}{N}n^2}
128+
$$
129+
130+
##### 1.2.1.2 卷积形式重构
131+
令:
132+
133+
$$
134+
\begin{aligned}
135+
w[n] &= e^{-j\frac{\pi}{N}n^2} (chrizp 信号) \\
136+
h[n] &= e^{j\frac{\pi}{N}(k-n)^2} (辅助信号)
137+
\end{aligned}
138+
$$
139+
140+
则DFT可表示为:
141+
142+
$$
143+
X[k] = w[k] \cdot \sum_{n=0}^{N-1} (x[n]w[n]) \cdot h[k-n]
144+
$$
145+
146+
##### 1.2.1.3 计算步骤
147+
148+
###### 1.2.1.3.1 预处理
149+
1. **输入信号调制**
150+
151+
$$
152+
a[n] = x[n] \cdot w[n], \quad 0 \leq n < N
153+
$$
154+
155+
2. **构造卷积核**
156+
157+
$$
158+
h[n] =
159+
\begin{aligned}
160+
e^{j\frac{\pi}{N}(k-n)^2}, & 0 \leq n < N \\
161+
\end{aligned}
162+
$$
163+
164+
###### 1.2.1.3.2 快速卷积
165+
1. **补零扩展**
166+
167+
$$
168+
\begin{aligned}
169+
a_{pad} &= [a[0],...,a[N-1], \underbrace{0,...,0}_{M-N}]
170+
\end{aligned}
171+
$$
172+
173+
$$
174+
\begin{aligned}
175+
h_{pad} &= [h[0],...,hj[N-1], \underbrace{0,...,0}_{M-N}]
176+
\end{aligned}
177+
$$
178+
179+
2. **FFT加速计算**
180+
181+
$$
182+
X[k] = w[k] \cdot \text{IFFT}\left( \text{FFT}(a_{pad}) \odot \text{FFT}(h_{pad}) \right)[k]
183+
$$
184+
108185
备注:
109186

110187
需要说明对nan/inf的特殊处理,输入存在nan/inf时,输出为按照IEEE754标准根据FFT计算公式得到,其中任何数乘nan为nan,0乘inf为nan,非0乘正负inf根据符号为正负inf,任何数加nan为nan,任何非nan数加正负inf根据符号为正负inf。
@@ -415,11 +492,106 @@ fft2d可以理解为先做一个bacth=n0, n=[n1]的行主序fft1d, 再做一个b
415492
416493
当前测试表明,列主序这样的分解以及对应的访存模式,可以大大减小stride过大带来的影响。
417494
418-
419495
##### 3.1.3.2 行主序batch循环软流水实现
420496
421497
对于fft2d而言,其对行主序fft1d的调用往往会有较多batch。我们拟增加对于batch之间的软流水优化,从而提升性能。
422498
499+
#### 3.1.2 任意长度bluestein fft实现
500+
#### 3.1.2.1 1d bluestein fft
501+
根据算法计算步骤 1.2.1.3 可知, 核心逻辑为对输入数据乘系数后得到a[n],进行pad后再进行fft,结果与另一个系数的fft 结果相乘再逆fft,结果再乘系数。
502+
其中关键步骤fft 及ifft 可直接调用前述fft 及ifft kernel 进行计算。需要实现的是1. x[n]*w[n], 复数矩阵每列乘以相应复数系数;2. fft(a_pad) * fft(h_pad), fft(h_pad)结果为1位复数向量, 其实质为也是一个复数矩每列阵乘以相应的复数系数;3. w[k]*ifft()也是计算复数矩阵每列乘以复数相应系数。这个三个计算步骤实质是相同的,所以实现完整的bluestein fft 算法除调用上述接口外还需要实现此功能, 命名接complex_coeff_matmul(),以及系数生成generate()
503+
方案如下:
504+
![bluesteinfft](bluestein_fft_01.jpg)
505+
506+
- generate() 伪代码:
507+
```C++
508+
// |---------------nram size---------------------------|
509+
// |nram_index|nram_temp| nram_temp2 |
510+
// nram_size 均分为4等份 结果存在gdram,供后续流程使用
511+
__bang_write_value(nram_index, 4 * num_deal, (float)0);
512+
__mluop_get_indices(nram_index, start, real_length);
513+
__bang_square(nram_index, nram_index, num_deal);
514+
__bang_mul_scalar(nram_index, nram_index, M_PI, num_deal);
515+
__bang_write_value(nram_temp, num_deal, (float)n);
516+
__cn_vector_div_f32_rn(num_deal, nram_temp, nram_index, nram_temp);
517+
__bang_move(nram_index, nram_temp, num_deal * sizeof(float));
518+
__cn_vector_cos_f32(num_deal, nram_index, nram_index);
519+
__cn_vector_sin_f32(num_deal, nram_temp, nram_temp);
520+
if (!direction) {
521+
__bang_mul_scalar(nram_temp, nram_temp, -1, num_deal);
522+
}
523+
__bang_transpose(nram_temp2, nram_index, 2, num_deal);
524+
525+
__memcpy(output_gdram + i * num_deal * 2, nram_temp2,
526+
2 * real_length * sizeof(float), NRAM2GDRAM);
527+
```
528+
```C++
529+
// |---------------nram size----------------------------------------|
530+
// |nram_index|nram_index_2|nram_temp| nram_temp2 |
531+
// nram_size 均分为8等份 结果存在gdram,供后续流程使用
532+
__bang_write_value(nram_index, 8 * num_deal, (float)0);
533+
__mluop_get_indices(nram_index, start, real_length);
534+
__bang_write_value(nram_temp, num_deal, (float)n);
535+
__bang_sub(nram_index_2, nram_temp, nram_index, num_deal);
536+
__bang_square(nram_index, nram_index, 2 * num_deal);
537+
__bang_mul_scalar(nram_index, nram_index, M_PI, 2 * num_deal);
538+
__bang_write_value(nram_temp, 2 * num_deal, (float)n);
539+
__cn_vector_div_f32_rn(2 * num_deal, nram_temp, nram_index, nram_temp);
540+
__bang_move(nram_index, nram_temp, 2 * num_deal * sizeof(float));
541+
__cn_vector_cos_f32(2 * num_deal, nram_index, nram_index);
542+
__cn_vector_sin_f32(2 * num_deal, nram_temp, nram_temp);
543+
if (direction) {
544+
__bang_mul_scalar(nram_temp, nram_temp, -1, 2 * num_deal);
545+
}
546+
__bang_transpose(nram_temp2, nram_index, 2, 2 * num_deal);
547+
548+
if (taskId == 0 && i == 0) {
549+
__bang_write_value(nram_temp2 + 2 * num_deal, 2, (float)0);
550+
}
551+
552+
uint32_t dst_stride = pad_n - n;
553+
__memcpy(output_gdram + i * num_deal * 2, nram_temp2,
554+
2 * real_length * sizeof(float), NRAM2GDRAM,
555+
2 * dst_stride * sizeof(float), 2 * num_deal * sizeof(float), 1);
556+
```
557+
558+
- complex_matrix_dot_vector() 核心计算步骤伪代码
559+
- 对x[b, length] 拆分b,当length 较长时,核内循环处理,先暂时没做三级流水
560+
- 设z1=a+bi,z2=c+di(a、b、c、d∈R)是任意两个复数,那么它们的积(a+bi)(c+di)=(ac-bd)+(bc+ad)i。
561+
```C++
562+
// input_gdram 为输入x_n的地址,output_gdram 为输出地址
563+
// 流水开始前对ouput空间刷零完成pad zero
564+
// pad_length 是bluestein fft 计算length 长度输入需要pad 到的长度
565+
// pad_length >= 2* length - 1
566+
gdram_set(output_gdram, b*pad_length, 0);
567+
//
568+
// nram 内存划分:
569+
// |-----no pipeline first------|
570+
// |input_x|output_x|temp|chirpz|
571+
// 指定一次可以处理长度为 l = 输入信号长度;
572+
int32_t num_deal = col_num;
573+
int32_t block_num =
574+
((MAX_NRAM_SIZE / sizeof(float) - 2 * num_deal) / 6) / num_deal;
575+
_memcpy(chirpz, gdram_buffer, GDRAM2NRAM, 2*num_deal);
576+
// z1_r, z2_r..., z1_i, z2_i....
577+
_memcpy(input_x, input_gdram, GDRAM2NRAM, 2*num_deal*block_num);
578+
//transpoze
579+
// a_r b_i c_r d_i
580+
// e_r f_i g_r e_i
581+
// 复数实数虚数分开
582+
// 转换成 a_r,c_r,e_r,g_r
583+
// b_i,d_i,f_i,e_i
584+
__bang_transpose(output_x, input_x, num_deal*block_num, 2);
585+
// 向量化计算复数
586+
complexMatrixDotVectorFunc();
587+
// tranpose转换回复数
588+
__bang_transpose(output_x, temp, 2, num_deal*block_num);
589+
// 转回gdram 存储
590+
memcpy(output_gdram, output_x, NRAM2GDRAM);
591+
```
592+
#### 3.1.2.1 2d bluestein fft
593+
用1d bluestein fft 方法做行主序 fft, 然后再做列主序fft
594+
423595
### 3.2调用方案设计
424596

425597
目前实现的方案如下:

0 commit comments

Comments
 (0)