Throughput of flag LASP_PARALLEL. If not set, some downstream code will be broken as JobQueues will not go empty for this case. For all code in the library, a serial implementation works off the not present job queue

This commit is contained in:
Anne de Jong 2021-09-15 21:10:27 +02:00
parent b159f4d144
commit 9983ca6096
9 changed files with 495 additions and 427 deletions

View File

@ -3,7 +3,8 @@
// Author: J.A. de Jong - ASCEE // Author: J.A. de Jong - ASCEE
// //
// Description: Implementation of an equalizer using the Second Order Sections // Description: Implementation of an equalizer using the Second Order Sections
// filter bank implementation. // filter bank implementation. Applies all filterbanks in parallel and
// recombines to create a single output signal.
#pragma once #pragma once
#ifndef LASP_EQ_H #ifndef LASP_EQ_H
#define LASP_EQ_H #define LASP_EQ_H
@ -51,8 +52,6 @@ void Eq_setLevels(Eq* eq, const vd* levels);
* @param[in] eq Equalizer handle * @param[in] eq Equalizer handle
*/ */
void Eq_free(Eq* eq); void Eq_free(Eq* eq);
#endif // LASP_EQ_H #endif // LASP_EQ_H

View File

@ -24,313 +24,344 @@
/* #endif */ /* #endif */
typedef struct { typedef struct {
void* job_ptr; void* job_ptr;
bool running; bool running;
bool ready; bool ready;
} Job; } Job;
typedef struct JobQueue_s { typedef struct JobQueue_s {
pthread_mutex_t mutex; #ifdef LASP_PARALLEL
pthread_cond_t cv_plus; /**< Condition variable for the pthread_mutex_t mutex;
* "workers". */ pthread_cond_t cv_plus; /**< Condition variable for the
pthread_cond_t cv_minus; /**< Condition variable for the * "workers". */
* main thread. */ pthread_cond_t cv_minus; /**< Condition variable for the
* main thread. */
Job* jobs; /**< Pointer to job vector */ #endif
us max_jobs; /**< Stores the maximum number of Job* jobs; /**< Pointer to job vector */
* items */ us max_jobs; /**< Stores the maximum number of
* items */
} JobQueue; } JobQueue;
static us count_jobs(JobQueue* jq) { static us count_jobs(JobQueue* jq) {
fsTRACE(15); fsTRACE(15);
us njobs = 0; us njobs = 0;
for(us i=0;i<jq->max_jobs;i++){ for(us i=0;i<jq->max_jobs;i++){
if(jq->jobs[i].ready) if(jq->jobs[i].ready)
njobs++; njobs++;
} }
return njobs; return njobs;
} }
static Job* get_ready_job(JobQueue* jq) { static Job* get_ready_job(JobQueue* jq) {
fsTRACE(15); fsTRACE(15);
Job* j = jq->jobs; Job* j = jq->jobs;
for(us i=0;i<jq->max_jobs;i++){ for(us i=0;i<jq->max_jobs;i++){
if(j->ready && !j->running) if(j->ready && !j->running)
return j; return j;
j++; j++;
} }
return NULL; return NULL;
} }
void print_job_queue(JobQueue* jq) { void print_job_queue(JobQueue* jq) {
fsTRACE(15); fsTRACE(15);
for(us i=0;i<jq->max_jobs;i++) { for(us i=0;i<jq->max_jobs;i++) {
printf("Job %zu", i); printf("Job %zu", i);
if(jq->jobs[i].ready) if(jq->jobs[i].ready)
printf(" available"); printf(" available");
if(jq->jobs[i].running) if(jq->jobs[i].running)
printf(" running"); printf(" running");
printf(" - ptr %zu\n", (us) jq->jobs[i].job_ptr); printf(" - ptr %zu\n", (us) jq->jobs[i].job_ptr);
} }
feTRACE(15); feTRACE(15);
} }
#ifdef LASP_PARALLEL
#define LOCK_MUTEX \ #define LOCK_MUTEX \
/* Lock the mutex to let the threads wait initially */ \ /* Lock the mutex to let the threads wait initially */ \
int rv = pthread_mutex_lock(&jq->mutex); \ int rv = pthread_mutex_lock(&jq->mutex); \
if(rv !=0) { \ if(rv !=0) { \
WARN("Mutex lock failed"); \ WARN("Mutex lock failed"); \
} }
#define UNLOCK_MUTEX \ #define UNLOCK_MUTEX \
rv = pthread_mutex_unlock(&jq->mutex); \ rv = pthread_mutex_unlock(&jq->mutex); \
if(rv !=0) { \ if(rv !=0) { \
WARN("Mutex unlock failed"); \ WARN("Mutex unlock failed"); \
} }
#else
#define LOCK_MUTEX
#define UNLOCK_MUTEX
#endif // LASP_PARALLEL
JobQueue* JobQueue_alloc(const us max_jobs) { JobQueue* JobQueue_alloc(const us max_jobs) {
TRACE(15,"JobQueue_alloc"); TRACE(15,"JobQueue_alloc");
if(max_jobs > LASP_MAX_NUM_CHANNELS) { if(max_jobs > LASP_MAX_NUM_CHANNELS) {
WARN("Max jobs restricted to LASP_MAX_NUM_CHANNELS"); WARN("Max jobs restricted to LASP_MAX_NUM_CHANNELS");
return NULL; return NULL;
} }
JobQueue* jq = a_malloc(sizeof(JobQueue)); JobQueue* jq = a_malloc(sizeof(JobQueue));
if(!jq) {
WARN("Allocation of JobQueue failed");
return NULL;
}
jq->max_jobs = max_jobs;
jq->jobs = a_malloc(max_jobs*sizeof(Job)); if(!jq) {
if(!jq->jobs) { WARN("Allocation of JobQueue failed");
WARN("Allocation of JobQueue jobs failed"); return NULL;
return NULL; }
} jq->max_jobs = max_jobs;
Job* j = jq->jobs; jq->jobs = a_malloc(max_jobs*sizeof(Job));
for(us jindex=0;jindex<max_jobs;jindex++) { if(!jq->jobs) {
j->job_ptr = NULL; WARN("Allocation of JobQueue jobs failed");
j->ready = false; return NULL;
j->running = false; }
j++;
}
/* Initialize thread mutex */ Job* j = jq->jobs;
int rv = pthread_mutex_init(&jq->mutex,NULL); for(us jindex=0;jindex<max_jobs;jindex++) {
if(rv !=0) { j->job_ptr = NULL;
WARN("Mutex initialization failed"); j->ready = false;
return NULL; j->running = false;
} j++;
rv = pthread_cond_init(&jq->cv_plus,NULL); }
if(rv !=0) {
WARN("Condition variable initialization failed");
return NULL;
}
rv = pthread_cond_init(&jq->cv_minus,NULL); #ifdef LASP_PARALLEL
if(rv !=0) { /* Initialize thread mutex */
WARN("Condition variable initialization failed"); int rv = pthread_mutex_init(&jq->mutex,NULL);
return NULL; if(rv !=0) {
} WARN("Mutex initialization failed");
return NULL;
}
rv = pthread_cond_init(&jq->cv_plus,NULL);
if(rv !=0) {
WARN("Condition variable initialization failed");
return NULL;
}
/* print_job_queue(jq); */ rv = pthread_cond_init(&jq->cv_minus,NULL);
return jq; if(rv !=0) {
WARN("Condition variable initialization failed");
return NULL;
}
#endif // LASP_PARALLEL
/* print_job_queue(jq); */
return jq;
} }
void JobQueue_free(JobQueue* jq) { void JobQueue_free(JobQueue* jq) {
TRACE(15,"JobQueue_free"); TRACE(15,"JobQueue_free");
dbgassert(jq,NULLPTRDEREF "jq in JobQueue_free"); dbgassert(jq,NULLPTRDEREF "jq in JobQueue_free");
int rv;
if(count_jobs(jq) != 0) {
WARN("Job queue not empty!");
}
a_free(jq->jobs); int rv;
/* Destroy the mutexes and condition variables */ if(count_jobs(jq) != 0) {
rv = pthread_mutex_destroy(&jq->mutex); WARN("Job queue not empty!");
if(rv != 0){ }
WARN("Mutex destroy failed. Do not know what to do.");
}
rv = pthread_cond_destroy(&jq->cv_plus); a_free(jq->jobs);
if(rv != 0){
WARN("Condition variable destruction failed. "
"Do not know what to do.");
}
rv = pthread_cond_destroy(&jq->cv_minus); #ifdef LASP_PARALLEL
if(rv != 0){ /* Destroy the mutexes and condition variables */
WARN("Condition variable destruction failed. " rv = pthread_mutex_destroy(&jq->mutex);
"Do not know what to do."); if(rv != 0){
} WARN("Mutex destroy failed. Do not know what to do.");
}
rv = pthread_cond_destroy(&jq->cv_plus);
if(rv != 0){
WARN("Condition variable destruction failed. "
"Do not know what to do.");
}
rv = pthread_cond_destroy(&jq->cv_minus);
if(rv != 0){
WARN("Condition variable destruction failed. "
"Do not know what to do.");
}
#endif // LASP_PARALLEL
} }
int JobQueue_push(JobQueue* jq,void* job_ptr) { int JobQueue_push(JobQueue* jq,void* job_ptr) {
TRACE(15,"JobQueue_push");
dbgassert(jq,NULLPTRDEREF "jq in JobQueue_push");
/* print_job_queue(jq); */ TRACE(15,"JobQueue_push");
/* uVARTRACE(15,(us) job_ptr); */ dbgassert(jq,NULLPTRDEREF "jq in JobQueue_push");
LOCK_MUTEX;
us max_jobs = jq->max_jobs;
/* Check if queue is full */ /* print_job_queue(jq); */
while(count_jobs(jq) == max_jobs) { /* uVARTRACE(15,(us) job_ptr); */
WARN("Queue full. Wait until some jobs are done."); LOCK_MUTEX;
rv = pthread_cond_wait(&jq->cv_minus,&jq->mutex);
if(rv !=0) { us max_jobs = jq->max_jobs;
WARN("Condition variable wait failed");
} #ifdef LASP_PARALLEL
/* Check if queue is full */
while(count_jobs(jq) == max_jobs) {
WARN("Queue full. Wait until some jobs are done.");
rv = pthread_cond_wait(&jq->cv_minus,&jq->mutex);
if(rv !=0) {
WARN("Condition variable wait failed");
}
}
#else
/* If job queue is full, not in parallel, we just fail to add something
* without waiting*/
if(count_jobs(jq) == max_jobs) {
return LASP_FAILURE;
}
#endif // LASP_PARALLEL
dbgassert(count_jobs(jq) != max_jobs,
"Queue cannot be full!");
/* Queue is not full try to find a place, fill it */
Job* j = jq->jobs;
us i;
for(i=0;i<max_jobs;i++) {
if(j->ready == false ) {
dbgassert(j->job_ptr==NULL,"Job ptr should be 0");
dbgassert(j->ready==false,"Job cannot be assigned");
break;
}
j++;
}
dbgassert(i!=jq->max_jobs,"Should have found a job!");
j->job_ptr = job_ptr;
j->ready = true;
#ifdef LASP_PARALLEL
/* Notify worker threads that a new job has arrived */
if(count_jobs(jq) == max_jobs) {
/* Notify ALL threads. Action required! */
rv = pthread_cond_broadcast(&jq->cv_plus);
if(rv !=0) {
WARN("Condition variable broadcast failed");
} }
dbgassert(count_jobs(jq) != max_jobs, } else {
"Queue cannot be full!"); /* Notify some thread that there has been some change to
* the Queue */
/* Queue is not full try to find a place, fill it */ rv = pthread_cond_signal(&jq->cv_plus);
Job* j = jq->jobs; if(rv !=0) {
us i; WARN("Condition variable signal failed");
for(i=0;i<max_jobs;i++) {
if(j->ready == false ) {
dbgassert(j->job_ptr==NULL,"Job ptr should be 0");
dbgassert(j->ready==false,"Job cannot be assigned");
break;
}
j++;
} }
dbgassert(i!=jq->max_jobs,"Should have found a job!"); }
#endif // LASP_PARALLEL
j->job_ptr = job_ptr; /* print_job_queue(jq); */
j->ready = true;
/* Notify worker threads that a new job has arrived */ UNLOCK_MUTEX;
if(count_jobs(jq) == max_jobs) {
/* Notify ALL threads. Action required! */
rv = pthread_cond_broadcast(&jq->cv_plus);
if(rv !=0) {
WARN("Condition variable broadcast failed");
}
} else { return LASP_SUCCESS;
/* Notify some thread that there has been some change to
* the Queue */
rv = pthread_cond_signal(&jq->cv_plus);
if(rv !=0) {
WARN("Condition variable signal failed");
}
}
/* print_job_queue(jq); */
UNLOCK_MUTEX;
return LASP_SUCCESS;
} }
void* JobQueue_assign(JobQueue* jq) { void* JobQueue_assign(JobQueue* jq) {
TRACE(15,"JobQueue_assign"); TRACE(15,"JobQueue_assign");
LOCK_MUTEX; LOCK_MUTEX;
/* Wait until a job is available */ Job* j;
Job* j; #ifdef LASP_PARALLEL
while ((j=get_ready_job(jq))==NULL) { /* Wait until a job is available */
while ((j=get_ready_job(jq))==NULL) {
TRACE(15,"JobQueue_assign: no ready job"); TRACE(15,"JobQueue_assign: no ready job");
pthread_cond_wait(&jq->cv_plus,&jq->mutex); pthread_cond_wait(&jq->cv_plus,&jq->mutex);
}
#else
if(count_jobs(jq) == 0) { return NULL; }
else { j = get_ready_job(jq); }
#endif // LASP_PARALLEL
TRACE(16,"JobQueue_assign: found ready job. Assigned to:");
#ifdef LASP_DEBUG
#ifdef LASP_PARALLEL
pthread_t thisthread = pthread_self();
iVARTRACE(16,thisthread);
#endif
#endif
/* print_job_queue(jq); */
/* Find a job from the queue, assign it and return it */
j->running = true;
#ifdef LASP_PARALLEL
if(count_jobs(jq) > 1) {
/* Signal different thread that there is more work to do */
rv = pthread_cond_signal(&jq->cv_plus);
if(rv !=0) {
WARN("Condition variable broadcast failed");
} }
}
#endif
TRACE(16,"JobQueue_assign: found ready job. Assigned to:"); UNLOCK_MUTEX;
#ifdef LASP_DEBUG
pthread_t thisthread = pthread_self();
iVARTRACE(16,thisthread);
#endif
/* print_job_queue(jq); */ TRACE(15,"End JobQueue_assign");
/* Find a job from the queue, assign it and return it */
j->running = true;
if(count_jobs(jq) > 1) {
/* Signal different thread that there is more work to do */
rv = pthread_cond_signal(&jq->cv_plus);
if(rv !=0) {
WARN("Condition variable broadcast failed");
}
}
UNLOCK_MUTEX; return j->job_ptr;
TRACE(15,"End JobQueue_assign");
return j->job_ptr;
} }
void JobQueue_done(JobQueue* jq,void* job_ptr) { void JobQueue_done(JobQueue* jq,void* job_ptr) {
TRACE(15,"JobQueue_done"); TRACE(15,"JobQueue_done");
dbgassert(jq,NULLPTRDEREF "jq in JobQueue_done"); dbgassert(jq,NULLPTRDEREF "jq in JobQueue_done");
LOCK_MUTEX; LOCK_MUTEX;
/* print_job_queue(jq); */
/* Find the job from the queue, belonging to the job_ptr */ /* print_job_queue(jq); */
Job* j=jq->jobs;
us i; /* Find the job from the queue, belonging to the job_ptr */
for(i=0;i<jq->max_jobs;i++) { Job* j=jq->jobs;
iVARTRACE(10,i); us i;
if(j->ready && j->running && j->job_ptr == job_ptr) { for(i=0;i<jq->max_jobs;i++) {
TRACE(15,"Found the job that has been done:"); iVARTRACE(10,i);
j->ready = false; if(j->ready && j->running && j->job_ptr == job_ptr) {
j->job_ptr = NULL; TRACE(15,"Found the job that has been done:");
j->running = false; j->ready = false;
break; j->job_ptr = NULL;
} j->running = false;
j++; break;
} }
j++;
}
/* print_job_queue(jq); */ /* print_job_queue(jq); */
/* Job done, broadcast this */
rv = pthread_cond_signal(&jq->cv_minus);
if(rv !=0) {
WARN("Condition variable broadcast failed");
}
UNLOCK_MUTEX; #ifdef LASP_PARALLEL
/* Job done, broadcast this */
rv = pthread_cond_signal(&jq->cv_minus);
if(rv !=0) {
WARN("Condition variable broadcast failed");
}
#endif
UNLOCK_MUTEX;
} }
#ifdef LASP_PARALLEL
void JobQueue_wait_alldone(JobQueue* jq) { void JobQueue_wait_alldone(JobQueue* jq) {
TRACE(15,"JobQueue_wait_alldone"); TRACE(15,"JobQueue_wait_alldone");
dbgassert(jq,NULLPTRDEREF "jq in JobQueue_wait_alldone"); dbgassert(jq,NULLPTRDEREF "jq in JobQueue_wait_alldone");
LOCK_MUTEX; LOCK_MUTEX;
/* Wait until number of jobs is 0 */ /* Wait until number of jobs is 0 */
while (count_jobs(jq)!=0) { while (count_jobs(jq)!=0) {
if(rv !=0) { if(rv !=0) {
WARN("Condition variable broadcast failed"); WARN("Condition variable broadcast failed");
}
pthread_cond_wait(&jq->cv_minus,&jq->mutex);
} }
UNLOCK_MUTEX; pthread_cond_wait(&jq->cv_minus,&jq->mutex);
}
UNLOCK_MUTEX;
} }
#endif
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -33,7 +33,8 @@ void JobQueue_free(JobQueue* jq);
/** /**
* Pops a job from the queue. Waits indefinitely until some job is * Pops a job from the queue. Waits indefinitely until some job is
* available. * available. If in parallel mode. In serial mode, it returns NULL if no job
* is available (LASP_PARALLEL compilation flag not set).
* *
* @param jq: JobQueue handle * @param jq: JobQueue handle
* @return Pointer to the job, NULL on error. * @return Pointer to the job, NULL on error.
@ -68,7 +69,10 @@ int JobQueue_push(JobQueue* jq,void* job_ptr);
* until all task workers are finished. * until all task workers are finished.
* *
*/ */
#ifdef LASP_PARALLEL
void JobQueue_wait_alldone(JobQueue*); void JobQueue_wait_alldone(JobQueue*);
#endif // LASP_PARALLEL
#endif // MQ_H #endif // MQ_H
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -1,10 +1,10 @@
#include "lasp_nprocs.h"
#ifdef MS_WIN64 #ifdef MS_WIN64
#include <windows.h> #include <windows.h>
#else #else
// Used for obtaining the number of processors // Used for obtaining the number of processors
#include <sys/sysinfo.h> #include <sys/sysinfo.h>
#endif #endif
#include "lasp_nprocs.h"
us getNumberOfProcs() { us getNumberOfProcs() {
#if MS_WIN64 #if MS_WIN64
@ -12,7 +12,7 @@ us getNumberOfProcs() {
SYSTEM_INFO sysinfo; SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo); GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors; return sysinfo.dwNumberOfProcessors;
#else #else // Linux, easy
return get_nprocs(); return get_nprocs();
#endif #endif
} }

View File

@ -7,246 +7,258 @@
typedef struct Sosfilterbank { typedef struct Sosfilterbank {
/// The filter_coefs matrix contains filter coefficients for a SOS filter. /// The filter_coefs matrix contains filter coefficients for a SOS filter.
us filterbank_size; us filterbank_size;
us nsections; us nsections;
/// The filter coefficients for each of the filters in the Filterbank /// The filter coefficients for each of the filters in the Filterbank
/// The *first* axis is the filter no, the second axis contains the /// The *first* axis is the filter no, the second axis contains the
/// filter coefficients, in the order, b_0, b_1, b_2, a_0, a_1, a_2, which /// filter coefficients, in the order, b_0, b_1, b_2, a_0, a_1, a_2, which
/// corresponds to the transfer function /// corresponds to the transfer function
/// b_0 + b_1 z^-1 + b_2 z^-2 /// b_0 + b_1 z^-1 + b_2 z^-2
/// H[z] = ------------------------- /// H[z] = -------------------------
/// a_0 + a_1 z^-1 + a_2 z^-2 /// a_0 + a_1 z^-1 + a_2 z^-2
dmat sos; /// sos[filter_no, coeff] dmat sos; /// sos[filter_no, coeff]
/// Storage for the current state of the output, first axis correspond to /// Storage for the current state of the output, first axis correspond to
/// the filter number axis, the second axis contains state coefficients /// the filter number axis, the second axis contains state coefficients
dmat state; dmat state;
#ifdef LASP_PARALLEL
JobQueue* jq; JobQueue* jq;
Workers* workers; Workers* workers;
#endif // LASP_PARALLEL
} Sosfilterbank; } Sosfilterbank;
us Sosfilterbank_getFilterbankSize(const Sosfilterbank* fb) { us Sosfilterbank_getFilterbankSize(const Sosfilterbank* fb) {
fsTRACE(15); fsTRACE(15);
assertvalidptr(fb); assertvalidptr(fb);
return fb->filterbank_size; return fb->filterbank_size;
feTRACE(15); feTRACE(15);
} }
int filter_single(void* worker_data,void* job); int filter_single(void* worker_data,void* job);
static inline us min(us a, us b){ static inline us min(us a, us b){
return a<b?a:b; return a<b?a:b;
} }
static inline us max(us a, us b){ static inline us max(us a, us b){
return a>b?a:b; return a>b?a:b;
} }
Sosfilterbank* Sosfilterbank_create( Sosfilterbank* Sosfilterbank_create(
const us nthreads_, const us nthreads_,
const us filterbank_size, const us filterbank_size,
const us nsections) { const us nsections) {
fsTRACE(15); fsTRACE(15);
dbgassert(nthreads_ <= LASP_MAX_NUM_THREADS, "Illegal number of threads");
dbgassert(filterbank_size <= MAX_SOS_FILTER_BANK_SIZE, dbgassert(filterbank_size <= MAX_SOS_FILTER_BANK_SIZE,
"Illegal filterbank size. Max size is " "Illegal filterbank size. Max size is "
annestr(MAX_SOS_FILTER_BANK_SIZE)); annestr(MAX_SOS_FILTER_BANK_SIZE));
Sosfilterbank* fb = (Sosfilterbank*) a_malloc(sizeof(Sosfilterbank)); Sosfilterbank* fb = (Sosfilterbank*) a_malloc(sizeof(Sosfilterbank));
fb->jq = NULL; fb->filterbank_size = filterbank_size;
fb->workers = NULL; dbgassert(nsections < MAX_SOS_FILTER_BANK_NSECTIONS,"Illegal number of sections");
fb->filterbank_size = filterbank_size; fb->nsections = nsections;
dbgassert(nsections < MAX_SOS_FILTER_BANK_NSECTIONS,"Illegal number of sections");
fb->nsections = nsections;
/// Allocate filter coefficients matrix /// Allocate filter coefficients matrix
fb->sos = dmat_alloc(filterbank_size, nsections*6); fb->sos = dmat_alloc(filterbank_size, nsections*6);
fb->state = dmat_alloc(filterbank_size, nsections*2); fb->state = dmat_alloc(filterbank_size, nsections*2);
dmat_set(&(fb->state), 0); dmat_set(&(fb->state), 0);
/// Set all filter coefficients to unit impulse response /// Set all filter coefficients to unit impulse response
vd imp_response = vd_alloc(6*nsections); vd imp_response = vd_alloc(6*nsections);
vd_set(&imp_response,0); vd_set(&imp_response,0);
for(us section = 0;section < nsections; section++) { for(us section = 0;section < nsections; section++) {
// Set b0 coefficient to 1 // Set b0 coefficient to 1
setvecval(&imp_response, 0 + 6*section, 1); setvecval(&imp_response, 0 + 6*section, 1);
// Set a0 coefficient to 1 // Set a0 coefficient to 1
setvecval(&imp_response, 3 + 6*section, 1); setvecval(&imp_response, 3 + 6*section, 1);
}
// Initialize all filters with a simple impulse response, single pass
for(us filter_no = 0; filter_no < filterbank_size; filter_no++) {
Sosfilterbank_setFilter(fb,filter_no,imp_response);
}
// Check if coefficients are properly initialized
// print_dmat(&(fb->sos));
vd_free(&imp_response);
#ifdef LASP_PARALLEL
fb->jq = NULL;
fb->workers = NULL;
dbgassert(nthreads_ <= LASP_MAX_NUM_THREADS, "Illegal number of threads");
us nthreads;
us nprocs = getNumberOfProcs();
if(nthreads_ == 0) {
nthreads = min(max(nprocs/2,1), filterbank_size);
} else {
nthreads = nthreads_;
}
iVARTRACE(15, nthreads);
if(nthreads > 1) {
if(!(fb->jq = JobQueue_alloc(filterbank_size))) {
Sosfilterbank_free(fb);
feTRACE(15);
return NULL;
} }
// Initialize all filters with a simple impulse response, single pass if(!(fb->workers = Workers_create(nthreads,
for(us filter_no = 0; filter_no < filterbank_size; filter_no++) { fb->jq,
Sosfilterbank_setFilter(fb,filter_no,imp_response); NULL,
&filter_single,
NULL,
NULL))) {
Sosfilterbank_free(fb);
feTRACE(15);
return NULL;
} }
// Check if coefficients are properly initialized }
// print_dmat(&(fb->sos));
vd_free(&imp_response);
us nthreads; #endif // LASP_PARALLEL
us nprocs = getNumberOfProcs(); feTRACE(15);
return fb;
if(nthreads_ == 0) {
nthreads = min(max(nprocs/2,1), filterbank_size);
} else {
nthreads = nthreads_;
}
iVARTRACE(15, nthreads);
if(nthreads > 1) {
if(!(fb->jq = JobQueue_alloc(filterbank_size))) {
Sosfilterbank_free(fb);
feTRACE(15);
return NULL;
}
if(!(fb->workers = Workers_create(nthreads,
fb->jq,
NULL,
&filter_single,
NULL,
NULL))) {
Sosfilterbank_free(fb);
feTRACE(15);
return NULL;
}
}
feTRACE(15);
return fb;
} }
void Sosfilterbank_setFilter(Sosfilterbank* fb,const us filter_no, void Sosfilterbank_setFilter(Sosfilterbank* fb,const us filter_no,
const vd filter_coefs) { const vd filter_coefs) {
fsTRACE(15); fsTRACE(15);
assertvalidptr(fb); assertvalidptr(fb);
assert_vx(&filter_coefs); assert_vx(&filter_coefs);
iVARTRACE(15, filter_coefs.n_rows); iVARTRACE(15, filter_coefs.n_rows);
iVARTRACE(15, filter_no); iVARTRACE(15, filter_no);
dbgassert(filter_no < fb->filterbank_size, "Illegal filter number"); dbgassert(filter_no < fb->filterbank_size, "Illegal filter number");
dbgassert(filter_coefs.n_rows == fb->nsections * 6, dbgassert(filter_coefs.n_rows == fb->nsections * 6,
"Illegal filter coefficient length"); "Illegal filter coefficient length");
dmat *sos = &fb->sos; dmat *sos = &fb->sos;
/* dmat *state = &fb->state; */ /* dmat *state = &fb->state; */
us nsections = fb->nsections; us nsections = fb->nsections;
for(us index=0;index<nsections*6;index++){ for(us index=0;index<nsections*6;index++){
// Copy contents to position in sos matrix // Copy contents to position in sos matrix
*getdmatval(sos,filter_no,index) = *getvdval(&filter_coefs,index); *getdmatval(sos,filter_no,index) = *getvdval(&filter_coefs,index);
} }
feTRACE(15); feTRACE(15);
} }
void Sosfilterbank_free(Sosfilterbank* fb) { void Sosfilterbank_free(Sosfilterbank* fb) {
fsTRACE(15); fsTRACE(15);
assertvalidptr(fb); assertvalidptr(fb);
dmat_free(&(fb->sos)); dmat_free(&(fb->sos));
dmat_free(&(fb->state)); dmat_free(&(fb->state));
if(fb->workers) Workers_free(fb->workers); #ifdef LASP_PARALLEL
if(fb->jq) JobQueue_free(fb->jq); if(fb->workers) Workers_free(fb->workers);
if(fb->jq) JobQueue_free(fb->jq);
#endif // LASP_PARALLEL
a_free(fb); a_free(fb);
feTRACE(15); feTRACE(15);
} }
typedef struct { typedef struct {
us filter_no; us filter_no;
us nsections; us nsections;
us nsamples; us nsamples;
dmat sos; dmat sos;
dmat state; dmat state;
dmat ys; dmat ys;
} Job; } Job;
int filter_single(void* worker_data, void* job_) { int filter_single(void* worker_data, void* job_) {
fsTRACE(15); fsTRACE(15);
Job* job = (Job*) job_; Job* job = (Job*) job_;
us nsections = job->nsections; us nsections = job->nsections;
us nsamples = job->nsamples; us nsamples = job->nsamples;
dmat sos = job->sos; dmat sos = job->sos;
for(us section=0;section<nsections;section++) { for(us section=0;section<nsections;section++) {
d w1 = *getdmatval(&(job->state),job->filter_no,section*2); d w1 = *getdmatval(&(job->state),job->filter_no,section*2);
d w2 = *getdmatval(&(job->state),job->filter_no,section*2+1); d w2 = *getdmatval(&(job->state),job->filter_no,section*2+1);
d b0 = *getdmatval(&sos,job->filter_no,section*6+0); d b0 = *getdmatval(&sos,job->filter_no,section*6+0);
d b1 = *getdmatval(&sos,job->filter_no,section*6+1); d b1 = *getdmatval(&sos,job->filter_no,section*6+1);
d b2 = *getdmatval(&sos,job->filter_no,section*6+2); d b2 = *getdmatval(&sos,job->filter_no,section*6+2);
/* d a0 = *getdmatval(&sos,job->filter_no,section*6+3); */ /* d a0 = *getdmatval(&sos,job->filter_no,section*6+3); */
d a1 = *getdmatval(&sos,job->filter_no,section*6+4); d a1 = *getdmatval(&sos,job->filter_no,section*6+4);
d a2 = *getdmatval(&sos,job->filter_no,section*6+5); d a2 = *getdmatval(&sos,job->filter_no,section*6+5);
d* y = getdmatval(&(job->ys), 0, job->filter_no); d* y = getdmatval(&(job->ys), 0, job->filter_no);
for(us sample=0;sample<nsamples;sample++){
d w0 = *y - a1*w1 - a2*w2;
d yn = b0*w0 + b1*w1 + b2*w2;
w2 = w1;
w1 = w0;
*y++ = yn;
}
*getdmatval(&(job->state),job->filter_no,section*2) = w1;
*getdmatval(&(job->state),job->filter_no,section*2+1) = w2;
for(us sample=0;sample<nsamples;sample++){
d w0 = *y - a1*w1 - a2*w2;
d yn = b0*w0 + b1*w1 + b2*w2;
w2 = w1;
w1 = w0;
*y++ = yn;
} }
*getdmatval(&(job->state),job->filter_no,section*2) = w1;
*getdmatval(&(job->state),job->filter_no,section*2+1) = w2;
feTRACE(15); }
return 0;
feTRACE(15);
return 0;
} }
dmat Sosfilterbank_filter(Sosfilterbank* fb,const vd* xs) { dmat Sosfilterbank_filter(Sosfilterbank* fb,const vd* xs) {
fsTRACE(15); fsTRACE(15);
assertvalidptr(fb); assertvalidptr(fb);
assert_vx(xs); assert_vx(xs);
dmat state = fb->state; dmat state = fb->state;
dmat sos = fb->sos; dmat sos = fb->sos;
us nsections = fb->nsections; us nsections = fb->nsections;
us filterbank_size = fb->filterbank_size; us filterbank_size = fb->filterbank_size;
us nsamples = xs->n_rows; us nsamples = xs->n_rows;
dmat ys = dmat_alloc(nsamples, filterbank_size); dmat ys = dmat_alloc(nsamples, filterbank_size);
/// Copy input signal to output array /// Copy input signal to output array
for(us filter=0;filter<filterbank_size;filter++) { for(us filter=0;filter<filterbank_size;filter++) {
d_copy(getdmatval(&ys,0,filter),getvdval(xs,0),nsamples,1,1); d_copy(getdmatval(&ys,0,filter),getvdval(xs,0),nsamples,1,1);
} }
Job jobs[MAX_SOS_FILTER_BANK_SIZE]; Job jobs[MAX_SOS_FILTER_BANK_SIZE];
/// Implementation is based on Proakis & Manolakis - Digital Signal /// Implementation is based on Proakis & Manolakis - Digital Signal
/// Processing, Fourth Edition, p. 550 /// Processing, Fourth Edition, p. 550
Job job_template = {0, nsections, nsamples, sos, state, ys}; Job job_template = {0, nsections, nsamples, sos, state, ys};
for(us filter=0;filter<filterbank_size;filter++) { for(us filter=0;filter<filterbank_size;filter++) {
/// Obtain state information for current section, and all filters /// Obtain state information for current section, and all filters
jobs[filter] = job_template; jobs[filter] = job_template;
jobs[filter].filter_no = filter; jobs[filter].filter_no = filter;
if(fb->workers) { #ifdef LASP_PARALLEL
assertvalidptr(fb->jq);
JobQueue_push(fb->jq, &(jobs[filter]));
} else {
/* No workers, we have to do it ourselves */
filter_single(NULL,(void*) &(jobs[filter]));
}
}
if(fb->workers) { if(fb->workers) {
JobQueue_wait_alldone(fb->jq); assertvalidptr(fb->jq);
JobQueue_push(fb->jq, &(jobs[filter]));
} else {
#endif // LASP_PARALLEL
/* No workers, we have to do it ourselves */
filter_single(NULL,(void*) &(jobs[filter]));
#ifdef LASP_PARALLEL
} }
feTRACE(15); #endif // LASP_PARALLEL
return ys; }
#ifdef LASP_PARALLEL
if(fb->workers) {
JobQueue_wait_alldone(fb->jq);
}
#endif // LASP_PARALLEL
feTRACE(15);
return ys;
} }

View File

@ -2,8 +2,11 @@
// //
// Author: J.A. de Jong - ASCEE // Author: J.A. de Jong - ASCEE
// //
// Description: Implemententation of a discrete filterbank using cascaded // Description: Implemententation of a discrete parallel filterbank using
// second order sections (sos), also called BiQuads. // cascaded second order sections (sos) for each filter in the filterbank. In
// parallel mode, the filters are allocated over a set of Worker threads that
// actually perform the computation. In serial mode, all filters in the bank
// are computed in series.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
#pragma once #pragma once
#ifndef LASP_FILTERBANK_H #ifndef LASP_FILTERBANK_H
@ -28,8 +31,8 @@ typedef struct Sosfilterbank Sosfilterbank;
* @return Sosfilterbank handle * @return Sosfilterbank handle
*/ */
Sosfilterbank* Sosfilterbank_create(const us nthreads, Sosfilterbank* Sosfilterbank_create(const us nthreads,
const us filterbank_size, const us filterbank_size,
const us nsections); const us nsections);
/** /**
* Returns the number of channels in the filterbank (the filberbank size). * Returns the number of channels in the filterbank (the filberbank size).
@ -51,7 +54,7 @@ us Sosfilterbank_getFilterbankSize(const Sosfilterbank* fb);
* *
*/ */
void Sosfilterbank_setFilter(Sosfilterbank* fb,const us filter_no, void Sosfilterbank_setFilter(Sosfilterbank* fb,const us filter_no,
const vd coefs); const vd coefs);
/** /**
* Filters x using h, returns y * Filters x using h, returns y
@ -61,10 +64,10 @@ void Sosfilterbank_setFilter(Sosfilterbank* fb,const us filter_no,
* @return Filtered output in an allocated array. The number of * @return Filtered output in an allocated array. The number of
* columns in this array equals the number of filters in the * columns in this array equals the number of filters in the
* filterbank. The number of output samples is equal to the number of * filterbank. The number of output samples is equal to the number of
* input samples in x. * input samples in x (and is equal to the number of rows in the output).
*/ */
dmat Sosfilterbank_filter(Sosfilterbank* fb, dmat Sosfilterbank_filter(Sosfilterbank* fb,
const vd* x); const vd* x);
/** /**
* Cleans up an existing filter bank. * Cleans up an existing filter bank.

View File

@ -6,7 +6,12 @@
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
#define TRACERPLUS (-5) #define TRACERPLUS (-5)
/* The code in this file is not of any use for systems that are not able to do
* simultaneous multithreading, or should be adjusted for it. It is therefore
* only compiled in case LASP_PARALLEL flag is set */
#include "lasp_worker.h" #include "lasp_worker.h"
#ifdef LASP_PARALLEL
#include "lasp_mq.h" #include "lasp_mq.h"
#include "lasp_alloc.h" #include "lasp_alloc.h"
#include <pthread.h> #include <pthread.h>
@ -196,5 +201,6 @@ static void* threadfcn(void* thread_global_data) {
return NULL; return NULL;
} }
#endif // LASP_PARALLEL
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -10,8 +10,10 @@
#pragma once #pragma once
#ifndef LASP_WORKER_H #ifndef LASP_WORKER_H
#define LASP_WORKER_H #define LASP_WORKER_H
#include "lasp_config.h"
#include "lasp_types.h" #include "lasp_types.h"
#ifdef LASP_PARALLEL
typedef struct Workers_s Workers; typedef struct Workers_s Workers;
typedef struct JobQueue_s JobQueue; typedef struct JobQueue_s JobQueue;
@ -57,5 +59,7 @@ Workers* Workers_create(const us num_workers,
*/ */
void Workers_free(Workers* w); void Workers_free(Workers* w);
#endif // LASP_PARALLEL
#endif // LASP_WORKER_H #endif // LASP_WORKER_H
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -5,14 +5,19 @@
// Description: // Description:
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
#include "lasp_worker.h" #include "lasp_config.h"
#include "lasp_mq.h"
#include "lasp_tracer.h" #include "lasp_tracer.h"
#include "lasp_assert.h" #include "lasp_assert.h"
#include <unistd.h> #include <unistd.h>
#ifdef LASP_PARALLEL
#include "lasp_worker.h"
#include "lasp_mq.h"
static void* walloc(void*); static void* walloc(void*);
static int worker(void*,void*); static int worker(void*,void*);
static void wfree(void*); static void wfree(void*);
#endif // LASP_PARALLEL
int main() { int main() {
@ -21,6 +26,7 @@ int main() {
iVARTRACE(15,getTracerLevel()); iVARTRACE(15,getTracerLevel());
#ifdef LASP_PARALLEL
us njobs = 4; us njobs = 4;
JobQueue* jq = JobQueue_alloc(njobs); JobQueue* jq = JobQueue_alloc(njobs);
dbgassert(jq,NULLPTRDEREF); dbgassert(jq,NULLPTRDEREF);
@ -42,8 +48,10 @@ int main() {
Workers_free(w); Workers_free(w);
JobQueue_free(jq); JobQueue_free(jq);
#endif // LASP_PARALLEL
return 0; return 0;
} }
#ifdef LASP_PARALLEL
static void* walloc(void* data) { static void* walloc(void* data) {
TRACE(15,"WALLOC"); TRACE(15,"WALLOC");
uVARTRACE(15,(us) data); uVARTRACE(15,(us) data);
@ -63,6 +71,7 @@ static void wfree(void* w_data) {
TRACE(15,"wfree"); TRACE(15,"wfree");
} }
#endif // LASP_PARALLEL
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////