blob: 90d36ca3372c634073128bb458718b4adae64766 [file] [log] [blame]
/*
Copyright (c) 2013 Julien Pommier.
Copyright (c) 2019 Hayati Ayguen ( h_ayguen@web.de )
*/
#define _WANT_SNAN 1
#include "pffft.h"
#include "pffastconv.h"
#include <math.h>
#include <float.h>
#include <limits.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <string.h>
#ifdef HAVE_SYS_TIMES
# include <sys/times.h>
# include <unistd.h>
#endif
/*
vector support macros: the rest of the code is independant of
SSE/Altivec/NEON -- adding support for other platforms with 4-element
vectors should be limited to these macros
*/
#if 0
#include "simd/pf_float.h"
#endif
#if defined(_MSC_VER)
# define RESTRICT __restrict
#elif defined(__GNUC__)
# define RESTRICT __restrict
#else
# define RESTRICT
#endif
#if defined(_MSC_VER)
#pragma warning( disable : 4244 )
#endif
#ifdef SNANF
#define INVALID_FLOAT_VAL SNANF
#elif defined(SNAN)
#define INVALID_FLOAT_VAL SNAN
#elif defined(NAN)
#define INVALID_FLOAT_VAL NAN
#elif defined(INFINITY)
#define INVALID_FLOAT_VAL INFINITY
#else
#define INVALID_FLOAT_VAL FLT_MAX
#endif
#if defined(HAVE_SYS_TIMES)
inline double uclock_sec(void) {
static double ttclk = 0.;
struct tms t;
if (ttclk == 0.)
ttclk = sysconf(_SC_CLK_TCK);
times(&t);
/* use only the user time of this process - not realtime, which depends on OS-scheduler .. */
return ((double)t.tms_utime)) / ttclk;
}
# else
double uclock_sec(void)
{ return (double)clock()/(double)CLOCKS_PER_SEC; }
#endif
typedef int (*pfnConvolution) (void * setup, const float * X, int len, float *Y, const float *Yref, int applyFlush);
typedef void* (*pfnConvSetup) (float *Hfwd, int Nf, int * BlkLen, int flags);
typedef pfnConvolution (*pfnGetConvFnPtr) (void * setup);
typedef void (*pfnConvDestroy) (void * setup);
struct ConvSetup
{
pfnConvolution pfn;
int N;
int B;
float * H;
int flags;
};
void * convSetupRev( float * H, int N, int * BlkLen, int flags )
{
struct ConvSetup * s = pffastconv_malloc( sizeof(struct ConvSetup) );
int i, Nr = N;
if (flags & PFFASTCONV_CPLX_INP_OUT)
Nr *= 2;
Nr += 4;
s->pfn = NULL;
s->N = N;
s->B = *BlkLen;
s->H = pffastconv_malloc((unsigned)Nr * sizeof(float));
s->flags = flags;
memset(s->H, 0, (unsigned)Nr * sizeof(float));
if (flags & PFFASTCONV_CPLX_INP_OUT)
{
for ( i = 0; i < N; ++i ) {
s->H[2*(N-1 -i) ] = H[i];
s->H[2*(N-1 -i)+1] = H[i];
}
/* simpler detection of overruns */
s->H[ 2*N ] = INVALID_FLOAT_VAL;
s->H[ 2*N +1 ] = INVALID_FLOAT_VAL;
s->H[ 2*N +2 ] = INVALID_FLOAT_VAL;
s->H[ 2*N +3 ] = INVALID_FLOAT_VAL;
}
else
{
for ( i = 0; i < N; ++i )
s->H[ N-1 -i ] = H[i];
/* simpler detection of overruns */
s->H[ N ] = INVALID_FLOAT_VAL;
s->H[ N +1 ] = INVALID_FLOAT_VAL;
s->H[ N +2 ] = INVALID_FLOAT_VAL;
s->H[ N +3 ] = INVALID_FLOAT_VAL;
}
return s;
}
void convDestroyRev( void * setup )
{
struct ConvSetup * s = (struct ConvSetup*)setup;
pffastconv_free(s->H);
pffastconv_free(setup);
}
pfnConvolution ConvGetFnPtrRev( void * setup )
{
struct ConvSetup * s = (struct ConvSetup*)setup;
if (!s)
return NULL;
return s->pfn;
}
void convSimdDestroy( void * setup )
{
convDestroyRev(setup);
}
void * fastConvSetup( float * H, int N, int * BlkLen, int flags )
{
void * p = pffastconv_new_setup( H, N, BlkLen, flags );
if (!p)
printf("fastConvSetup(N = %d, *BlkLen = %d, flags = %d) = NULL\n", N, *BlkLen, flags);
return p;
}
void fastConvDestroy( void * setup )
{
pffastconv_destroy_setup( (PFFASTCONV_Setup*)setup );
}
int slow_conv_R(void * setup, const float * input, int len, float *output, const float *Yref, int applyFlush)
{
struct ConvSetup * p = (struct ConvSetup*)setup;
const float * RESTRICT X = input;
const float * RESTRICT Hrev = p->H;
float * RESTRICT Y = output;
const int Nr = ((p->flags & PFFASTCONV_CPLX_INP_OUT) ? 2 : 1) * p->N;
const int lenNr = ((p->flags & PFFASTCONV_CPLX_INP_OUT) ? 2 : 1) * (len - p->N);
int i, j;
(void)Yref;
(void)applyFlush;
if (p->flags & PFFASTCONV_CPLX_INP_OUT)
{
for ( i = 0; i <= lenNr; i += 2 )
{
float sumRe = 0.0F, sumIm = 0.0F;
for ( j = 0; j < Nr; j += 2 )
{
sumRe += X[i+j ] * Hrev[j];
sumIm += X[i+j+1] * Hrev[j+1];
}
Y[i ] = sumRe;
Y[i+1] = sumIm;
}
return i/2;
}
else
{
for ( i = 0; i <= lenNr; ++i )
{
float sum = 0.0F;
for (j = 0; j < Nr; ++j )
sum += X[i+j] * Hrev[j];
Y[i] = sum;
}
return i;
}
}
int slow_conv_A(void * setup, const float * input, int len, float *output, const float *Yref, int applyFlush)
{
float sum[4];
struct ConvSetup * p = (struct ConvSetup*)setup;
const float * RESTRICT X = input;
const float * RESTRICT Hrev = p->H;
float * RESTRICT Y = output;
const int Nr = ((p->flags & PFFASTCONV_CPLX_INP_OUT) ? 2 : 1) * p->N;
const int lenNr = ((p->flags & PFFASTCONV_CPLX_INP_OUT) ? 2 : 1) * (len - p->N);
int i, j;
(void)Yref;
(void)applyFlush;
if (p->flags & PFFASTCONV_CPLX_INP_OUT)
{
if ( (Nr & 3) == 0 )
{
for ( i = 0; i <= lenNr; i += 2 )
{
sum[0] = sum[1] = sum[2] = sum[3] = 0.0F;
for (j = 0; j < Nr; j += 4 )
{
sum[0] += X[i+j] * Hrev[j];
sum[1] += X[i+j+1] * Hrev[j+1];
sum[2] += X[i+j+2] * Hrev[j+2];
sum[3] += X[i+j+3] * Hrev[j+3];
}
Y[i ] = sum[0] + sum[2];
Y[i+1] = sum[1] + sum[3];
}
}
else
{
const int M = Nr & (~3);
for ( i = 0; i <= lenNr; i += 2 )
{
float tailSumRe = 0.0F, tailSumIm = 0.0F;
sum[0] = sum[1] = sum[2] = sum[3] = 0.0F;
for (j = 0; j < M; j += 4 )
{
sum[0] += X[i+j ] * Hrev[j ];
sum[1] += X[i+j+1] * Hrev[j+1];
sum[2] += X[i+j+2] * Hrev[j+2];
sum[3] += X[i+j+3] * Hrev[j+3];
}
for ( ; j < Nr; j += 2 ) {
tailSumRe += X[i+j ] * Hrev[j ];
tailSumIm += X[i+j+1] * Hrev[j+1];
}
Y[i ] = ( sum[0] + sum[2] ) + tailSumRe;
Y[i+1] = ( sum[1] + sum[3] ) + tailSumIm;
}
}
return i/2;
}
else
{
if ( (Nr & 3) == 0 )
{
for ( i = 0; i <= lenNr; ++i )
{
sum[0] = sum[1] = sum[2] = sum[3] = 0.0F;
for (j = 0; j < Nr; j += 4 )
{
sum[0] += X[i+j] * Hrev[j];
sum[1] += X[i+j+1] * Hrev[j+1];
sum[2] += X[i+j+2] * Hrev[j+2];
sum[3] += X[i+j+3] * Hrev[j+3];
}
Y[i] = sum[0] + sum[1] + sum[2] + sum[3];
}
return i;
}
else
{
const int M = Nr & (~3);
/* printf("A: Nr = %d, M = %d, H[M] = %f, H[M+1] = %f, H[M+2] = %f, H[M+3] = %f\n", Nr, M, Hrev[M], Hrev[M+1], Hrev[M+2], Hrev[M+3] ); */
for ( i = 0; i <= lenNr; ++i )
{
float tailSum = 0.0;
sum[0] = sum[1] = sum[2] = sum[3] = 0.0F;
for (j = 0; j < M; j += 4 )
{
sum[0] += X[i+j] * Hrev[j];
sum[1] += X[i+j+1] * Hrev[j+1];
sum[2] += X[i+j+2] * Hrev[j+2];
sum[3] += X[i+j+3] * Hrev[j+3];
}
for ( ; j < Nr; ++j )
tailSum += X[i+j] * Hrev[j];
Y[i] = (sum[0] + sum[1]) + (sum[2] + sum[3]) + tailSum;
}
return i;
}
}
}
int slow_conv_B(void * setup, const float * input, int len, float *output, const float *Yref, int applyFlush)
{
float sum[4];
struct ConvSetup * p = (struct ConvSetup*)setup;
(void)Yref;
(void)applyFlush;
if (p->flags & PFFASTCONV_SYMMETRIC)
{
const float * RESTRICT X = input;
const float * RESTRICT Hrev = p->H;
float * RESTRICT Y = output;
const int Nr = ((p->flags & PFFASTCONV_CPLX_INP_OUT) ? 2 : 1) * p->N;
const int lenNr = ((p->flags & PFFASTCONV_CPLX_INP_OUT) ? 2 : 1) * (len - p->N);
const int h = Nr / 2 -4;
const int E = Nr -4;
int i, j;
if (p->flags & PFFASTCONV_CPLX_INP_OUT)
{
for ( i = 0; i <= lenNr; i += 2 )
{
const int k = i + E;
sum[0] = sum[1] = sum[2] = sum[3] = 0.0F;
for (j = 0; j <= h; j += 4 )
{
sum[0] += Hrev[j ] * ( X[i+j ] + X[k-j+2] );
sum[1] += Hrev[j+1] * ( X[i+j+1] + X[k-j+3] );
sum[2] += Hrev[j+2] * ( X[i+j+2] + X[k-j ] );
sum[3] += Hrev[j+3] * ( X[i+j+3] + X[k-j+1] );
}
Y[i ] = sum[0] + sum[2];
Y[i+1] = sum[1] + sum[3];
}
return i/2;
}
else
{
for ( i = 0; i <= lenNr; ++i )
{
const int k = i + E;
sum[0] = sum[1] = sum[2] = sum[3] = 0.0F;
for (j = 0; j <= h; j += 4 )
{
sum[0] += Hrev[j ] * ( X[i+j ] + X[k-j+3] );
sum[1] += Hrev[j+1] * ( X[i+j+1] + X[k-j+2] );
sum[2] += Hrev[j+2] * ( X[i+j+2] + X[k-j+1] );
sum[3] += Hrev[j+3] * ( X[i+j+3] + X[k-j ] );
}
Y[i] = sum[0] + sum[1] + sum[2] + sum[3];
}
return i;
}
}
else
{
const float * RESTRICT X = input;
const float * RESTRICT Hrev = p->H;
float * RESTRICT Y = output;
const int Nr = ((p->flags & PFFASTCONV_CPLX_INP_OUT) ? 2 : 1) * p->N;
const int lenNr = ((p->flags & PFFASTCONV_CPLX_INP_OUT) ? 2 : 1) * (len - p->N);
int i, j;
if (p->flags & PFFASTCONV_CPLX_INP_OUT)
{
for ( i = 0; i <= lenNr; i += 2 )
{
sum[0] = sum[1] = sum[2] = sum[3] = 0.0F;
for (j = 0; j < Nr; j += 4 )
{
sum[0] += X[i+j] * Hrev[j];
sum[1] += X[i+j+1] * Hrev[j+1];
sum[2] += X[i+j+2] * Hrev[j+2];
sum[3] += X[i+j+3] * Hrev[j+3];
}
Y[i ] = sum[0] + sum[2];
Y[i+1] = sum[1] + sum[3];
}
return i/2;
}
else
{
if ( (Nr & 3) == 0 )
{
for ( i = 0; i <= lenNr; ++i )
{
sum[0] = sum[1] = sum[2] = sum[3] = 0.0F;
for (j = 0; j < Nr; j += 4 )
{
sum[0] += X[i+j] * Hrev[j];
sum[1] += X[i+j+1] * Hrev[j+1];
sum[2] += X[i+j+2] * Hrev[j+2];
sum[3] += X[i+j+3] * Hrev[j+3];
}
Y[i] = (sum[0] + sum[1]) + (sum[2] + sum[3]);
}
return i;
}
else
{
const int M = Nr & (~3);
/* printf("B: Nr = %d\n", Nr ); */
for ( i = 0; i <= lenNr; ++i )
{
float tailSum = 0.0;
sum[0] = sum[1] = sum[2] = sum[3] = 0.0F;
for (j = 0; j < M; j += 4 )
{
sum[0] += X[i+j] * Hrev[j];
sum[1] += X[i+j+1] * Hrev[j+1];
sum[2] += X[i+j+2] * Hrev[j+2];
sum[3] += X[i+j+3] * Hrev[j+3];
}
for ( ; j < Nr; ++j )
tailSum += X[i+j] * Hrev[j];
Y[i] = (sum[0] + sum[1]) + (sum[2] + sum[3]) + tailSum;
}
return i;
}
}
}
}
int fast_conv(void * setup, const float * X, int len, float *Y, const float *Yref, int applyFlush)
{
(void)Yref;
return pffastconv_apply( (PFFASTCONV_Setup*)setup, X, len, Y, applyFlush );
}
void printFirst( const float * V, const char * st, const int N, const int perLine )
{
(void)V; (void)st; (void)N; (void)perLine;
return;
#if 0
int i;
for ( i = 0; i < N; ++i )
{
if ( (i % perLine) == 0 )
printf("\n%s[%d]", st, i);
printf("\t%.1f", V[i]);
}
printf("\n");
#endif
}
#define NUMY 11
int test(int FILTERLEN, int convFlags, const int testOutLen, int printDbg, int printSpeed) {
double t0, t1, tstop, td, tdref;
float *X, *H;
float *Y[NUMY];
int64_t outN[NUMY];
/* 256 KFloats or 16 MFloats data */
#if 1
const int len = testOutLen ? (1 << 18) : (1 << 24);
#elif 0
const int len = testOutLen ? (1 << 18) : (1 << 13);
#else
const int len = testOutLen ? (1 << 18) : (1024);
#endif
const int cplxFactor = ( convFlags & PFFASTCONV_CPLX_INP_OUT ) ? 2 : 1;
const int lenC = len / cplxFactor;
int yi, yc, posMaxErr;
float yRangeMin, yRangeMax, yErrLimit, maxErr = 0.0;
int i, j, numErrOverLimit, iter;
int retErr = 0;
/* 0 1 2 3 4 5 6 7 8 9 */
pfnConvSetup aSetup[NUMY] = { convSetupRev, convSetupRev, convSetupRev, fastConvSetup, fastConvSetup, fastConvSetup, fastConvSetup, fastConvSetup, fastConvSetup, fastConvSetup };
pfnConvDestroy aDestroy[NUMY] = { convDestroyRev, convDestroyRev, convDestroyRev, fastConvDestroy, fastConvDestroy, fastConvDestroy, fastConvDestroy, fastConvDestroy, fastConvDestroy, fastConvDestroy };
pfnGetConvFnPtr aGetFnPtr[NUMY] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, };
pfnConvolution aConv[NUMY] = { slow_conv_R, slow_conv_A, slow_conv_B, fast_conv, fast_conv, fast_conv, fast_conv, fast_conv, fast_conv, fast_conv };
const char * convText[NUMY] = { "R(non-simd)", "A(non-simd)", "B(non-simd)", "fast_conv_64", "fast_conv_128", "fast_conv_256", "fast_conv_512", "fast_conv_1K", "fast_conv_2K", "fast_conv_4K" };
int aFastAlgo[NUMY] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 };
void * aSetupCfg[NUMY] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
int aBlkLen[NUMY] = { 1024, 1024, 1024, 64, 128, 256, 512, 1024, 2048, 4096 };
#if 1
int aRunAlgo[NUMY] = { 1, 1, 1, FILTERLEN<64, FILTERLEN<128, FILTERLEN<256, FILTERLEN<512, FILTERLEN<1024, FILTERLEN<2048, FILTERLEN<4096 };
#elif 0
int aRunAlgo[NUMY] = { 1, 0, 0, 0 && FILTERLEN<64, 1 && FILTERLEN<128, 1 && FILTERLEN<256, 0 && FILTERLEN<512, 0 && FILTERLEN<1024, 0 && FILTERLEN<2048, 0 && FILTERLEN<4096 };
#else
int aRunAlgo[NUMY] = { 1, 1, 1, 0 && FILTERLEN<64, 0 && FILTERLEN<128, 1 && FILTERLEN<256, 0 && FILTERLEN<512, 0 && FILTERLEN<1024, 0 && FILTERLEN<2048, 0 && FILTERLEN<4096 };
#endif
double aSpeedFactor[NUMY], aDuration[NUMY], procSmpPerSec[NUMY];
X = pffastconv_malloc( (unsigned)(len+4) * sizeof(float) );
for ( i=0; i < NUMY; ++i)
{
if ( 1 || i < 2 )
Y[i] = pffastconv_malloc( (unsigned)len * sizeof(float) );
else
Y[i] = Y[1];
Y[i][0] = 123.F; /* test for pffft_zconvolve_no_accu() */
aSpeedFactor[i] = -1.0;
aDuration[i] = -1.0;
procSmpPerSec[i] = -1.0;
}
H = pffastconv_malloc((unsigned)FILTERLEN * sizeof(float));
/* initialize input */
if ( convFlags & PFFASTCONV_CPLX_INP_OUT )
{
for ( i = 0; i < lenC; ++i )
{
X[2*i ] = (float)(i % 4093); /* 4094 is a prime number. see https://en.wikipedia.org/wiki/List_of_prime_numbers */
X[2*i+1] = (float)((i+2048) % 4093);
}
}
else
{
for ( i = 0; i < len; ++i )
X[i] = (float)(i % 4093); /* 4094 is a prime number. see https://en.wikipedia.org/wiki/List_of_prime_numbers */
}
X[ len ] = INVALID_FLOAT_VAL;
X[ len +1 ] = INVALID_FLOAT_VAL;
X[ len +2 ] = INVALID_FLOAT_VAL;
X[ len +3 ] = INVALID_FLOAT_VAL;
if (!testOutLen)
printFirst( X, "X", 64, 8 );
/* filter coeffs */
memset( H, 0, FILTERLEN * sizeof(float) );
#if 1
if ( convFlags & PFFASTCONV_SYMMETRIC )
{
const int half = FILTERLEN / 2;
for ( j = 0; j < half; ++j ) {
switch (j % 3) {
case 0: H[j] = H[FILTERLEN-1-j] = -1.0F; break;
case 1: H[j] = H[FILTERLEN-1-j] = 1.0F; break;
case 2: H[j] = H[FILTERLEN-1-j] = 0.5F; break;
}
}
}
else
{
for ( j = 0; j < FILTERLEN; ++j ) {
switch (j % 3) {
case 0: H[j] = -1.0F; break;
case 1: H[j] = 1.0F; break;
case 2: H[j] = 0.5F; break;
}
}
}
#else
H[0] = 1.0F;
H[FILTERLEN -1] = 1.0F;
#endif
if (!testOutLen)
printFirst( H, "H", FILTERLEN, 8 );
printf("\n");
printf("filterLen = %d\t%s%s\t%s:\n", FILTERLEN,
((convFlags & PFFASTCONV_CPLX_INP_OUT)?"cplx":"real"),
(convFlags & PFFASTCONV_CPLX_INP_OUT)?((convFlags & PFFASTCONV_CPLX_SINGLE_FFT)?" single":" 2x") : "",
((convFlags & PFFASTCONV_SYMMETRIC)?"symmetric":"non-sym") );
while (1)
{
for ( yi = 0; yi < NUMY; ++yi )
{
if (!aRunAlgo[yi])
continue;
aSetupCfg[yi] = aSetup[yi]( H, FILTERLEN, &aBlkLen[yi], convFlags );
/* get effective apply function ptr */
if ( aSetupCfg[yi] && aGetFnPtr[yi] )
aConv[yi] = aGetFnPtr[yi]( aSetupCfg[yi] );
if ( aSetupCfg[yi] && aConv[yi] ) {
if (testOutLen)
{
t0 = uclock_sec();
outN[yi] = aConv[yi]( aSetupCfg[yi], X, lenC, Y[yi], Y[0], 1 /* applyFlush */ );
t1 = uclock_sec();
td = t1 - t0;
}
else
{
const int blkLen = 4096; /* required for 'fast_conv_4K' */
int64_t offC = 0, offS, Nout;
int k;
iter = 0;
outN[yi] = 0;
t0 = uclock_sec();
tstop = t0 + 0.25; /* benchmark duration: 250 ms */
do {
for ( k = 0; k < 128 && offC +blkLen < lenC; ++k )
{
offS = cplxFactor * offC;
Nout = aConv[yi]( aSetupCfg[yi], X +offS, blkLen, Y[yi] +offS, Y[0], (offC +blkLen >= lenC) /* applyFlush */ );
offC += Nout;
++iter;
if ( !Nout )
break;
if ( offC +blkLen >= lenC )
{
outN[yi] += offC;
offC = 0;
}
}
t1 = uclock_sec();
} while ( t1 < tstop );
outN[yi] += offC;
td = t1 - t0;
procSmpPerSec[yi] = cplxFactor * (double)outN[yi] / td;
}
}
else
{
t0 = t1 = td = 0.0;
outN[yi] = 0;
}
aDuration[yi] = td;
if ( yi == 0 ) {
const float * Yvals = Y[0];
const int64_t refOutLen = cplxFactor * outN[0];
tdref = td;
if (printDbg) {
printf("convolution '%s' took: %f ms\n", convText[yi], td*1000.0);
printf(" convolution '%s' output size %" PRId64 " == (cplx) len %d + %" PRId64 "\n", convText[yi], outN[yi], len / cplxFactor, outN[yi] - len / cplxFactor);
}
aSpeedFactor[yi] = 1.0;
/* */
yRangeMin = FLT_MAX;
yRangeMax = FLT_MIN;
for ( i = 0; i < refOutLen; ++i )
{
if ( yRangeMax < Yvals[i] ) yRangeMax = Yvals[i];
if ( yRangeMin > Yvals[i] ) yRangeMin = Yvals[i];
}
yErrLimit = fabsf(yRangeMax - yRangeMin) / ( 100.0F * 1000.0F );
/* yErrLimit = 0.01F; */
if (testOutLen) {
if (1) {
printf("reference output len = %" PRId64 " smp\n", outN[0]);
printf("reference output range |%.1f ..%.1f| = %.1f ==> err limit = %f\n", yRangeMin, yRangeMax, yRangeMax - yRangeMin, yErrLimit);
}
printFirst( Yvals, "Yref", 64, 8 );
}
}
else
{
aSpeedFactor[yi] = tdref / td;
if (printDbg) {
printf("\nconvolution '%s' took: %f ms == %f %% == %f X\n", convText[yi], td*1000.0, td * 100 / tdref, tdref / td);
printf(" convolution '%s' output size %" PRId64 " == (cplx) len %d + %" PRId64 "\n", convText[yi], outN[yi], len / cplxFactor, outN[yi] - len / cplxFactor);
}
}
}
int iMaxSpeedSlowAlgo = -1;
int iFirstFastAlgo = -1;
int iMaxSpeedFastAlgo = -1;
int iPrintedRefOutLen = 0;
{
for ( yc = 1; yc < NUMY; ++yc )
{
if (!aRunAlgo[yc])
continue;
if (aFastAlgo[yc]) {
if ( iMaxSpeedFastAlgo < 0 || aSpeedFactor[yc] > aSpeedFactor[iMaxSpeedFastAlgo] )
iMaxSpeedFastAlgo = yc;
if (iFirstFastAlgo < 0)
iFirstFastAlgo = yc;
}
else
{
if ( iMaxSpeedSlowAlgo < 0 || aSpeedFactor[yc] > aSpeedFactor[iMaxSpeedSlowAlgo] )
iMaxSpeedSlowAlgo = yc;
}
}
if (printSpeed)
{
if (testOutLen)
{
if (iMaxSpeedSlowAlgo >= 0 )
printf("fastest slow algorithm is '%s' at speed %f X ; abs duration %f ms\n", convText[iMaxSpeedSlowAlgo], aSpeedFactor[iMaxSpeedSlowAlgo], 1000.0 * aDuration[iMaxSpeedSlowAlgo]);
if (0 != iMaxSpeedSlowAlgo && aRunAlgo[0])
printf("slow algorithm '%s' at speed %f X ; abs duration %f ms\n", convText[0], aSpeedFactor[0], 1000.0 * aDuration[0]);
if (1 != iMaxSpeedSlowAlgo && aRunAlgo[1])
printf("slow algorithm '%s' at speed %f X ; abs duration %f ms\n", convText[1], aSpeedFactor[1], 1000.0 * aDuration[1]);
if (iFirstFastAlgo >= 0 && iFirstFastAlgo != iMaxSpeedFastAlgo && aRunAlgo[iFirstFastAlgo])
printf("first fast algorithm is '%s' at speed %f X ; abs duration %f ms\n", convText[iFirstFastAlgo], aSpeedFactor[iFirstFastAlgo], 1000.0 * aDuration[iFirstFastAlgo]);
if (iFirstFastAlgo >= 0 && iFirstFastAlgo+1 != iMaxSpeedFastAlgo && iFirstFastAlgo+1 < NUMY && aRunAlgo[iFirstFastAlgo+1])
printf("2nd fast algorithm is '%s' at speed %f X ; abs duration %f ms\n", convText[iFirstFastAlgo+1], aSpeedFactor[iFirstFastAlgo+1], 1000.0 * aDuration[iFirstFastAlgo+1]);
if ( 0 <= iMaxSpeedFastAlgo && iMaxSpeedFastAlgo < NUMY && aRunAlgo[iMaxSpeedFastAlgo] )
{
printf("fastest fast algorithm is '%s' at speed %f X ; abs duration %f ms\n", convText[iMaxSpeedFastAlgo], aSpeedFactor[iMaxSpeedFastAlgo], 1000.0 * aDuration[iMaxSpeedFastAlgo]);
if ( 0 <= iMaxSpeedSlowAlgo && iMaxSpeedSlowAlgo < NUMY && aRunAlgo[iMaxSpeedSlowAlgo] )
printf("fast / slow ratio: %f X\n", aSpeedFactor[iMaxSpeedFastAlgo] / aSpeedFactor[iMaxSpeedSlowAlgo] );
}
printf("\n");
}
else
{
for ( yc = 0; yc < NUMY; ++yc )
{
if (!aRunAlgo[yc] || procSmpPerSec[yc] <= 0.0)
continue;
printf("algo '%s':\t%.2f MSmp\tin\t%.1f ms\t= %g kSmpPerSec\n",
convText[yc], (double)outN[yc]/(1000.0 * 1000.0), 1000.0 * aDuration[yc], procSmpPerSec[yc] * 0.001 );
}
}
}
}
for ( yc = 1; yc < NUMY; ++yc )
{
const float * Yref;
const float * Ycurr;
int outMin;
if (!aRunAlgo[yc])
continue;
if (printDbg)
printf("\n");
if ( outN[yc] == 0 )
{
printf("output size 0: '%s' not implemented\n", convText[yc]);
}
else if ( outN[0] != outN[yc] /* && aFastAlgo[yc] */ && testOutLen )
{
if (!iPrintedRefOutLen)
{
printf("reference output size = %" PRId64 ", delta to (cplx) input length = %" PRId64 " smp\n", outN[0], (len / cplxFactor) - outN[0]);
iPrintedRefOutLen = 1;
}
printf("output size doesn't match!: ref (FILTERLEN %d) returned %" PRId64 " smp, '%s' returned %" PRId64 " smp : delta = %" PRId64 " smp\n",
FILTERLEN, outN[0], convText[yc], outN[yc], outN[yc] - outN[0] );
retErr = 1;
}
posMaxErr = 0;
maxErr = -1.0;
Yref = Y[0];
Ycurr = Y[yc];
outMin = ( outN[yc] < outN[0] ) ? outN[yc] : outN[0];
numErrOverLimit = 0;
for ( i = 0; i < outMin; ++i )
{
if ( numErrOverLimit < 6 && fabs(Ycurr[i] - Yref[i]) >= yErrLimit )
{
printf("algo '%s': at %d: ***ERROR*** = %f, errLimit = %f, ref = %f, actual = %f\n",
convText[yc], i, fabs(Ycurr[i] - Yref[i]), yErrLimit, Yref[i], Ycurr[i] );
++numErrOverLimit;
}
if ( fabs(Ycurr[i] - Yref[i]) > maxErr )
{
maxErr = fabsf(Ycurr[i] - Yref[i]);
posMaxErr = i;
}
}
if ( printDbg || (iMaxSpeedSlowAlgo == i) || (iMaxSpeedFastAlgo == i) )
printf("max difference for '%s' is %g at sample idx %d of max inp 4093-1 == %f %%\n", convText[yc], maxErr, posMaxErr, maxErr * 100.0 / 4092.0 );
}
break;
}
pffastconv_free(X);
for ( i=0; i < NUMY; ++i)
{
if ( 1 || i < 2 )
pffastconv_free( Y[i] );
if (!aRunAlgo[i])
continue;
aDestroy[i]( aSetupCfg[i] );
}
pffastconv_free(H);
return retErr;
}
/* small functions inside pffft.c that will detect (compiler) bugs with respect to simd instructions */
void validate_pffft_simd();
int validate_pffft_simd_ex(FILE * DbgOut);
int main(int argc, char **argv)
{
int result = 0;
int i, k, M, flagsA, flagsB, flagsC, testOutLen, printDbg, printSpeed;
int testOutLens = 1, benchConv = 1, quickTest = 0, slowTest = 0;
int testReal = 1, testCplx = 1, testSymetric = 0;
for ( i = 1; i < argc; ++i ) {
if (!strcmp(argv[i], "--test-simd")) {
int numErrs = validate_pffft_simd_ex(stdout);
fprintf( ( numErrs != 0 ? stderr : stdout ), "validate_pffft_simd_ex() returned %d errors!\n", numErrs);
return ( numErrs > 0 ? 1 : 0 );
}
if (!strcmp(argv[i], "--no-len")) {
testOutLens = 0;
}
else if (!strcmp(argv[i], "--no-bench")) {
benchConv = 0;
}
else if (!strcmp(argv[i], "--quick")) {
quickTest = 1;
}
else if (!strcmp(argv[i], "--slow")) {
slowTest = 1;
}
else if (!strcmp(argv[i], "--real")) {
testCplx = 0;
}
else if (!strcmp(argv[i], "--cplx")) {
testReal = 0;
}
else if (!strcmp(argv[i], "--sym")) {
testSymetric = 1;
}
else /* if (!strcmp(argv[i], "--help")) */ {
printf("usage: %s [--test-simd] [--no-len] [--no-bench] [--quick|--slow] [--real|--cplx] [--sym]\n", argv[0]);
exit(1);
}
}
if (testOutLens)
{
for ( k = 0; k < 3; ++k )
{
if ( (k == 0 && !testReal) || (k > 0 && !testCplx) )
continue;
printf("\n\n==========\n");
printf("testing %s %s output lengths ..\n", (k == 0 ? "real" : "cplx"), ( k == 0 ? "" : (k==1 ? "2x" : "single") ) );
printf("==========\n");
flagsA = (k == 0) ? 0 : PFFASTCONV_CPLX_INP_OUT;
flagsB = flagsA | ( testSymetric ? PFFASTCONV_SYMMETRIC : 0 );
flagsC = flagsB | PFFASTCONV_CPLX_SINGLE_FFT;
testOutLen = 1;
printDbg = 0;
printSpeed = 0;
for ( M = 128 - 4; M <= (quickTest ? 128+16 : 256); ++M )
{
if ( (M % 16) != 0 && testSymetric )
continue;
result |= test(M, flagsB, testOutLen, printDbg, printSpeed);
}
}
}
if (benchConv)
{
for ( k = 0; k < 3; ++k )
{
if ( (k == 0 && !testReal) || (k > 0 && !testCplx) )
continue;
printf("\n\n==========\n");
printf("starting %s %s benchmark against linear convolutions ..\n", (k == 0 ? "real" : "cplx"), ( k == 0 ? "" : (k==1 ? "2x" : "single") ) );
printf("==========\n");
flagsA = (k == 0) ? 0 : PFFASTCONV_CPLX_INP_OUT;
flagsB = flagsA | ( testSymetric ? PFFASTCONV_SYMMETRIC : 0 );
flagsC = flagsB | ( k == 2 ? PFFASTCONV_CPLX_SINGLE_FFT : 0 );
testOutLen = 0;
printDbg = 0;
printSpeed = 1;
if (!slowTest) {
result |= test( 32, flagsC, testOutLen, printDbg, printSpeed);
result |= test( 32+ 16, flagsC, testOutLen, printDbg, printSpeed);
result |= test( 64, flagsC, testOutLen, printDbg, printSpeed);
result |= test( 64+ 32, flagsC, testOutLen, printDbg, printSpeed);
result |= test(128, flagsC, testOutLen, printDbg, printSpeed);
}
if (!quickTest) {
result |= test(128+ 64, flagsC, testOutLen, printDbg, printSpeed);
result |= test(256, flagsC, testOutLen, printDbg, printSpeed);
result |= test(256+128, flagsC, testOutLen, printDbg, printSpeed);
result |= test(512, flagsC, testOutLen, printDbg, printSpeed);
result |= test(1024, flagsC, testOutLen, printDbg, printSpeed);
}
}
}
return result;
}