2012-03-15 16 views
9

Vorrei aiuto per migliorare l'efficienza del mio codice buffer circolare.miglioramento dell'efficienza del buffer circolare C

Ho dato un'occhiata allo stackoverflow e ho scoperto che (quasi) tutti gli argomenti sui buffer circolari riguardano gli usi di tale buffer o l'implementazione di base di un buffer circolare. Ho davvero bisogno di informazioni su come renderlo super efficiente.

Il piano prevede l'utilizzo di questo buffer con il microcontrollore STM32F4 che ha una FPU a precettore singolo. Ho intenzione di fare un uso pesante soprattutto delle funzioni write() e readn(). Stiamo letteralmente parlando di qualche milione di chiamate al secondo qui, quindi la rasatura di alcuni cicli di clock qui e là farà davvero la differenza.

metterò i pezzi più importanti di codice qui, il codice di buffer completo è disponibile tramite http://dl.dropbox.com/u/39710897/circular%20buffer.rar

Qualcuno può fornirmi alcune indicazioni su come migliorare l'efficienza di questo buffer?

#define BUFF_SIZE 3    // buffer size set at compile time 

typedef struct buffer{ 
    float buff[BUFF_SIZE]; 
    int readIndex; 
    int writeIndex; 
}buffer; 

/********************************\ 
* void write(buffer* buffer, float value) 
* writes value into the buffer 
* @param buffer* buffer 
* pointer to buffer to be used 
* @param float value 
* valueto be written in buffer 
\********************************/ 
void write(buffer* buffer,float value){ 
    buffer->buff[buffer->writeIndex]=value; 
    buffer->writeIndex++; 
    if(buffer->writeIndex==BUFF_SIZE) 
     buffer->writeIndex=0; 
} 

/********************************\ 
* float readn(buffer* buffer, int Xn) 
* reads specified value from buffer 
* @param buffer* buffer 
* pointer to buffer to be read from 
* @param int Xn 
* specifies the value to be read from buffer counting backwards from the most recently written value 
* i.e. the most recently writen value can be read with readn(buffer, 0), the value written before that with readn(buffer, 1) 
\********************************/ 
float readn(buffer* buffer, int Xn){ 
    int tempIndex; 

    tempIndex=buffer->writeIndex-(Xn+1); 
    while(tempIndex<0){ 
     tempIndex+=BUFF_SIZE; 
    } 

    return buffer->buff[tempIndex]; 
} 
+0

'readIndex' non è utilizzato. – valdo

+0

readindex non è effettivamente utilizzato nelle funzioni sopra elencate. È usato però nella funzione read() che può essere trovata nel file rar allegato. La funzione readn() elencata qui serve a leggere un valore specifico dal buffer (ovvero il penultimo valore scritto) – Gurba

risposta

12

Come suggerito da "Oli Charlesworth", sarebbe possibile semplificare le cose se la dimensione del buffer è la potenza di 2. Vorrei scrivere i corpi delle funzioni di lettura/scrittura, in modo che l'intento sia più chiaro.

#define BUFF_SIZE 4 
#define BUFF_SIZE_MASK (BUFF_SIZE-1) 

typedef struct buffer{ 
    float buff[BUFF_SIZE]; 
    int writeIndex; 
}buffer; 

void write(buffer* buffer,float value){ 
    buffer->buff[(buffer->writeIndex++) & BUFF_SIZE_MASK] = value; 
} 

float readn(buffer* buffer, int Xn){ 
    return buffer->buff[(buffer->writeIndex + (~Xn)) & BUFF_SIZE_MASK]; 
} 

Alcune spiegazioni. Si noti che non vi è alcuna diramazione (if). Non limitiamo l'indice dell'array ai limiti dell'array, ma lo stiamo AND-ing con la maschera.

In readn invece di calcolare l'indice di accesso come

writeIndex - (Xn+1)

stiamo usando:

writeIndex + (~Xn)

questo funziona assumendo 2-complemento aritmetica. Cioè, per rendere negativo il numero intero, NON facciamo tutti i suoi bit, e quindi aggiungiamo 1. Ma poiché vuoi sottrarre 1 dal numero - fare solo NOT è tutto ciò che è necessario.

+1

Non appena usi le maschere di bit, devi semplicemente passare ai tipi di indice 'unsigned'. Quindi non devi discutere di 2-complementi o cose del genere. –

+0

Ti ringrazio molto, questo ha reso il mio codice approssimativamente 6,5 volte più veloce! Lo definirei un miglioramento piuttosto drammatico, – Gurba

+0

, dal momento che posso impostare solo 1 risposta come accettata, e io con quella per gli esempi, anche se Oli Charlesworth ha trovato una risposta simile. – Gurba

9

Se potete fare la vostra dimensione del buffer un power-di-2, quindi il controllo contro zero può essere sostituito con incondizionata bit-mascheramento. Nella maggior parte dei processori, dovrebbe essere più veloce.

+1

Accetto. Inoltre, non sarà necessario controllare l'indice di over/under-flow. Basta incrementarlo e decrementarlo senza alcun controllo, solo AND-mascherarlo per l'effettivo accesso all'elemento – valdo

+0

Lo studio, rendendolo una potenza di 2 è abbastanza semplice (basta impostare BUFF_SIZE a 4). Dovrò scavare un po 'per mascherare il bit anche se – Gurba

+0

@Gurba presumo che l'applicazione finale abbia una dimensione del buffer molto più grande? Altrimenti dubito fortemente dell'uso del buffer anulare ADT in primo luogo. – Lundin

3

Questo potrebbe non sembrare elegante ma efficiente. L'accesso agli elementi della struttura tramite il puntatore richiede molte istruzioni. Perché non rimuovere la struttura del tutto e rendere buffer e writeIndex come variabili globali? Ciò ridurrà notevolmente le dimensioni delle tue funzioni readn e write.

ho provato in gcc e qui è l'uscita con e senza la struttura

Con Struttura

_write: 
    pushl %ebp 
    movl %esp, %ebp 
    movl 8(%ebp), %ecx 
    movl 8(%ebp), %eax 
    movl 16(%eax), %edx 
    movl 12(%ebp), %eax 
    movl %eax, (%ecx,%edx,4) 
    movl 8(%ebp), %eax 
    incl 16(%eax) 
    movl 8(%ebp), %eax 
    cmpl $3, 16(%eax) 
    jne L1 
    movl 8(%ebp), %eax 
    movl $0, 16(%eax) 
L1: 
    popl %ebp 
    ret 

senza struttura.cioè Rendere buffer e writeIndex come globale

_write: 
    pushl %ebp 
    movl %esp, %ebp 
    movl _writeIndex, %edx 
    movl 8(%ebp), %eax 
    movl %eax, _buff(,%edx,4) 
    incl _writeIndex 
    cmpl $3, _writeIndex 
    jne L1 
    movl $0, _writeIndex 
L1: 
    popl %ebp 
    ret 
+0

Con le ottimizzazioni abilitate non ci dovrebbe essere una grande differenza. Si può risolvere una volta l'indirizzo di 'buff' e' writeIndex', tutto il resto dovrebbe essere lo stesso. OTOH senza ottimizzazioni qualsiasi accesso come 'buff -> ...' sarebbe lungo – valdo

+1

Questo non funzionerà se l'OP ha bisogno di più di un'istanza di buffer. –

+0

@valdo Hmmm .. Ma il compilatore sembra fare qualcos'altro. Ho provato con '-O2' e il codice risultante sembra essere molto più grande di quelli precedenti: O –

3

Tenere traccia di inizio e la fine del buffer circolare con puntatori, è probabilmente un po 'più veloce di matrice indicizzazione, poiché l'indirizzo verrà calcolato in runtime in caso di quest'ultimo. Prova a sostituire readIndex e writeIndex con float*. Il codice sarebbe allora

*buffer->writeIndex = value; 
buffer->writeIndex++; 
if(buffer->writeIndex == buffer + BUFF_SIZE) 
    buffer->writeIndex=buffer->buff; 

buffer + BUFF_SIZE sarà ancora un'espressione costante e il compilatore tradurrà che ad un indirizzo fisso in fase di compilazione.