Files
Uladzimir Karpenka 9960a1d0e3 resample: vectorize the polyphase tap loop
The tap loop wrapped the ring with a test on every tap:

    if ((idx_out = idx_in + j) >= ringsize) idx_out -= ringsize;

which made the address non-affine and stopped the vectorizer. Split the
walk at the wrap point instead, so both halves are unit-stride, and split
the complex ring into separate I/Q arrays so the taps load contiguously
rather than through a de-interleaving ld2.

The dot product also could not be vectorized as written: reassociating an
fp reduction needs -ffast-math, which this library must not enable (it
relies on IEEE semantics for 0/0 = NaN and x/0 = Inf, see linux_port.h).
Carry four independent accumulator pairs instead, which both breaks the
FMA dependency chain and lets the vectorizer in on any compiler.

Measured on an Apple M1 Pro, 512-sample DSP buffers, best of 5:

  xresample, decimation to 48 kHz     before      after    speedup
    192k -> 48k  (561 taps)          342.0 us    84.2 us     4.06x
    384k -> 48k  (1121 taps)         708.9 us   175.1 us     4.05x
    576k -> 48k  (1681 taps)        1074.4 us   266.9 us     4.03x
    768k -> 48k  (2241 taps)        1438.9 us   360.0 us     4.00x

  full xrxa() chain, 576k input      1148.2 us   337.6 us     3.40x
                                     10.76%       3.16%   of one core

  xresampleF (float, host audio)                          2.6x - 3.4x

Summation order changes, so the double path is not bit-identical: over
400 buffers of the full RX chain the worst deviation is 1.1e-12, an SNR
of 251 dB. The float path is bit-identical, as the cast to float absorbs
the difference. With the resampler bypassed (48k in, 48k out) the chain
is unchanged bit-for-bit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 22:44:08 +03:00

123 lines
3.8 KiB
C

/* resample.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2013 Warren Pratt, NR0V
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
/************************************************************************************************
* *
* VERSION FOR COMPLEX DOUBLE-PRECISION *
* *
************************************************************************************************/
#ifndef _resample_h
#define _resample_h
typedef struct _resample
{
int run; // run
int size; // number of input samples per buffer
double* in; // input buffer for resampler
double* out; // output buffer for resampler
int in_rate;
int out_rate;
double fcin;
double fc;
double fc_low;
double gain;
int idx_in; // index for input into ring
int ncoefin;
int ncoef; // number of coefficients
int L; // interpolation factor
int M; // decimation factor
double* h; // coefficients
int ringsize; // number of complex pairs the ring buffer holds
double* ringI; // ring buffer, in-phase
double* ringQ; // ring buffer, quadrature (split from I so the tap loop
// reads unit-stride and vectorizes)
int cpp; // coefficients of the phase
int phnum; // phase number
} resample, *RESAMPLE;
__declspec (dllexport)
RESAMPLE create_resample (int run, int size, double* in, double* out, int in_rate, int out_rate, double fc, int ncoef, double gain);
__declspec (dllexport)
void destroy_resample (RESAMPLE a);
__declspec (dllexport)
void flush_resample (RESAMPLE a);
__declspec (dllexport)
int xresample (RESAMPLE a);
extern void setBuffers_resample (RESAMPLE a, double* in, double* out);
extern void setSize_resample(RESAMPLE a, int size);
extern void setInRate_resample(RESAMPLE a, int rate);
extern void setOutRate_resample(RESAMPLE a, int rate);
extern void setFCLow_resample (RESAMPLE a, double fc_low);
extern void setBandwidth_resample (RESAMPLE a, double fc_low, double fc_high);
#endif
/************************************************************************************************
* *
* VERSION FOR NON-COMPLEX FLOATS *
* *
************************************************************************************************/
#ifndef _resampleF_h
#define _resampleF_h
typedef struct _resampleF
{
int run; // run
int size; // number of input samples per buffer
float* in; // input buffer for resampler
float* out; // output buffer for resampler
int idx_in; // index for input into ring
int ncoef; // number of coefficients
int L; // interpolation factor
int M; // decimation factor
double* h; // coefficients
int ringsize; // number of values the ring buffer holds
double* ring; // ring buffer
int cpp; // coefficients of the phase
int phnum; // phase number
} resampleF, *RESAMPLEF;
extern RESAMPLEF create_resampleF (int run, int size, float* in, float* out, int in_rate, int out_rate);
extern void destroy_resampleF (RESAMPLEF a);
extern void flush_resampleF (RESAMPLEF a);
extern int xresampleF (RESAMPLEF a);
#endif