@@ -1961,7 +1961,12 @@ class RFFT {
19611961 X[N / 2 ].im = 0 .0f ;
19621962
19631963 const int * RESTRICT kin = KINV .data ();
1964- for (int k = 1 ; k < N / 2 ; ++k) {
1964+ int k = 1 ;
1965+ for (; k + 3 < N / 2 ; k += 4 ) {
1966+ pack4_residues_to_complex_f32 (X + k, work,
1967+ kin[k], kin[k + 1 ], kin[k + 2 ], kin[k + 3 ]);
1968+ }
1969+ for (; k < N / 2 ; ++k) {
19651970 const int m = kin[k];
19661971 X[k].re = work[2 *m];
19671972 X[k].im = -work[2 *m + 1 ];
@@ -1983,7 +1988,15 @@ class RFFT {
19831988 // Native heapopt order is a layout permutation; keep the output
19841989 // stream linear and pay the permutation on residue reads.
19851990 const int * RESTRICT native_leaf = NATIVE_LEAF .data ();
1986- for (int pos = 1 ; pos < N / 2 ; ++pos) {
1991+ int pos = 1 ;
1992+ for (; pos + 3 < N / 2 ; pos += 4 ) {
1993+ pack4_residues_to_complex_f32 (X + pos, work,
1994+ native_leaf[pos],
1995+ native_leaf[pos + 1 ],
1996+ native_leaf[pos + 2 ],
1997+ native_leaf[pos + 3 ]);
1998+ }
1999+ for (; pos < N / 2 ; ++pos) {
19872000 const int m = native_leaf[pos];
19882001 X[pos].re = work[2 *m];
19892002 X[pos].im = -work[2 *m + 1 ];
@@ -2079,7 +2092,12 @@ class RFFT {
20792092 void inverse_f32 (const complex_f32_t * RESTRICT X, float * RESTRICT out) const {
20802093 out[0 ] = 0 .5f * (X[0 ].re + X[N / 2 ].re );
20812094 out[1 ] = 0 .5f * (X[0 ].re - X[N / 2 ].re );
2082- for (int m = 1 ; m < N / 2 ; ++m) {
2095+ int m = 1 ;
2096+ for (; m + 3 < N / 2 ; m += 4 ) {
2097+ unpack4_complex_to_residues_f32 (out + 2 *m, X,
2098+ IDX [m], IDX [m + 1 ], IDX [m + 2 ], IDX [m + 3 ]);
2099+ }
2100+ for (; m < N / 2 ; ++m) {
20832101 const int k = IDX [m];
20842102 out[2 * m] = X[k].re ;
20852103 out[2 * m + 1 ] = -X[k].im ;
@@ -2095,8 +2113,17 @@ class RFFT {
20952113 }
20962114 out[0 ] = 0 .5f * (Xnative[0 ].re + Xnative[N / 2 ].re );
20972115 out[1 ] = 0 .5f * (Xnative[0 ].re - Xnative[N / 2 ].re );
2098- for (int pos = 1 ; pos < N / 2 ; ++pos) {
2099- const int m = NATIVE_LEAF [pos];
2116+ const int * RESTRICT native_pos = NATIVE_POS .data ();
2117+ int m = 1 ;
2118+ for (; m + 3 < N / 2 ; m += 4 ) {
2119+ unpack4_complex_to_residues_f32 (out + 2 *m, Xnative,
2120+ native_pos[m],
2121+ native_pos[m + 1 ],
2122+ native_pos[m + 2 ],
2123+ native_pos[m + 3 ]);
2124+ }
2125+ for (; m < N / 2 ; ++m) {
2126+ const int pos = native_pos[m];
21002127 out[2 * m] = Xnative[pos].re ;
21012128 out[2 * m + 1 ] = -Xnative[pos].im ;
21022129 }
@@ -2171,7 +2198,12 @@ class RFFT {
21712198 standardX[0 ] = nativeX[0 ];
21722199 standardX[N / 2 ] = nativeX[N / 2 ];
21732200 const int * RESTRICT map = STANDARD_NATIVE_POS .data ();
2174- for (int k = 1 ; k < N / 2 ; ++k) {
2201+ int k = 1 ;
2202+ for (; k + 3 < N / 2 ; k += 4 ) {
2203+ copy4_complex_f32 (standardX + k, nativeX,
2204+ map[k], map[k + 1 ], map[k + 2 ], map[k + 3 ]);
2205+ }
2206+ for (; k < N / 2 ; ++k) {
21752207 standardX[k] = nativeX[map[k]];
21762208 }
21772209#else
@@ -2189,7 +2221,12 @@ class RFFT {
21892221 nativeX[0 ] = standardX[0 ];
21902222 nativeX[N / 2 ] = standardX[N / 2 ];
21912223 const int * RESTRICT map = NATIVE_STANDARD_BIN .data ();
2192- for (int pos = 1 ; pos < N / 2 ; ++pos) {
2224+ int pos = 1 ;
2225+ for (; pos + 3 < N / 2 ; pos += 4 ) {
2226+ copy4_complex_f32 (nativeX + pos, standardX,
2227+ map[pos], map[pos + 1 ], map[pos + 2 ], map[pos + 3 ]);
2228+ }
2229+ for (; pos < N / 2 ; ++pos) {
21932230 nativeX[pos] = standardX[map[pos]];
21942231 }
21952232#else
@@ -2432,6 +2469,67 @@ class RFFT {
24322469 int m;
24332470 };
24342471
2472+ static inline void store4_interleaved_f32 (float * RESTRICT dst,
2473+ float re0, float im0,
2474+ float re1, float im1,
2475+ float re2, float im2,
2476+ float re3, float im3) {
2477+ #if BRUUN_LEVEL >= 1
2478+ const bruun_v4f re = V4F_SET4 (re0, re1, re2, re3);
2479+ const bruun_v4f im = V4F_SET4 (im0, im1, im2, im3);
2480+ V4F_ST (dst, V4F_ZIPLO (re, im));
2481+ V4F_ST (dst + 4 , V4F_ZIPHI (re, im));
2482+ #else
2483+ dst[0 ] = re0;
2484+ dst[1 ] = im0;
2485+ dst[2 ] = re1;
2486+ dst[3 ] = im1;
2487+ dst[4 ] = re2;
2488+ dst[5 ] = im2;
2489+ dst[6 ] = re3;
2490+ dst[7 ] = im3;
2491+ #endif
2492+ }
2493+
2494+ static inline void store4_complex_f32 (complex_f32_t * RESTRICT dst,
2495+ float re0, float im0,
2496+ float re1, float im1,
2497+ float re2, float im2,
2498+ float re3, float im3) {
2499+ store4_interleaved_f32 (reinterpret_cast <float *>(dst),
2500+ re0, im0, re1, im1, re2, im2, re3, im3);
2501+ }
2502+
2503+ static inline void pack4_residues_to_complex_f32 (complex_f32_t * RESTRICT dst,
2504+ const float * RESTRICT residues,
2505+ int m0, int m1, int m2, int m3) {
2506+ store4_complex_f32 (dst,
2507+ residues[2 *m0], -residues[2 *m0 + 1 ],
2508+ residues[2 *m1], -residues[2 *m1 + 1 ],
2509+ residues[2 *m2], -residues[2 *m2 + 1 ],
2510+ residues[2 *m3], -residues[2 *m3 + 1 ]);
2511+ }
2512+
2513+ static inline void copy4_complex_f32 (complex_f32_t * RESTRICT dst,
2514+ const complex_f32_t * RESTRICT src,
2515+ int i0, int i1, int i2, int i3) {
2516+ store4_complex_f32 (dst,
2517+ src[i0].re , src[i0].im ,
2518+ src[i1].re , src[i1].im ,
2519+ src[i2].re , src[i2].im ,
2520+ src[i3].re , src[i3].im );
2521+ }
2522+
2523+ static inline void unpack4_complex_to_residues_f32 (float * RESTRICT dst,
2524+ const complex_f32_t * RESTRICT src,
2525+ int i0, int i1, int i2, int i3) {
2526+ store4_interleaved_f32 (dst,
2527+ src[i0].re , -src[i0].im ,
2528+ src[i1].re , -src[i1].im ,
2529+ src[i2].re , -src[i2].im ,
2530+ src[i3].re , -src[i3].im );
2531+ }
2532+
24352533 static inline void norm_q1_fwd_f32 (float * RESTRICT p, float c, float s) {
24362534 const float A0 = p[0 ];
24372535 const float B0 = p[1 ];
0 commit comments