Domanda

Se ho avuto la C99 restrict parola chiave giusta, qualifica un puntatore con esso è una promessa fatta che i dati che i riferimenti non saranno modificati alle spalle del compilatore attraverso aliasing.

Al contrario, il mio modo di capire il qualificatore const è la documentazione del compilatore-imposto che un dato oggetto non verrà modificato dietro la schiena di un codice di scrittura essere umano. Il compilatore potrebbe avere un suggerimento come effetto collaterale, ma come programmatore non mi interessa davvero.

In modo simile, sarebbe opportuno prendere in considerazione un qualificatore restrict in un prototipo di funzione come un requisito che l'utente garantisce l'accesso esclusivo ( "evitare l'aliasing", o forse qualcosa di più forte) per la durata della chiamata? Dovrebbe essere usato come "documentazione"?

Inoltre, c'è qualcosa da capire nel fatto che restrict qualifica un puntatore piuttosto che i dati a cui punta (come const fa)?

EDIT:. All'inizio ho creduto che restrict potrebbe avere implicazioni con il codice threaded, ma questo sembra sbagliato quindi rimuovere i riferimenti a discussioni dalla questione per evitare di confondere i lettori

È stato utile?

Soluzione

Il meglio 'intuizione' di avere circa il limitare parola chiave è che la sua garanzia (dal programmatore al compilatore) che, per tutta la vita del puntatore, la memoria accessibile tramite tale puntatore sarà solo accessibile via che il puntatore e non attraverso un altro puntatore o riferimento o indirizzo globale. Così la sua importante che la sua su un puntatore come una proprietà sia del puntatore e la memoria, legando i due insieme fino a quando il puntatore esce dall'ambito.

Altri suggerimenti

Chris Dodd ha la corretta descrizione della parola chiave. In alcune piattaforme può essere molto importante per motivi di prestazioni, perché permette al compilatore sa che, una volta che ha caricato i dati attraverso tale puntatore su un registro, non ha bisogno di farlo di nuovo. Senza questa garanzia, il compilatore deve ricaricare i dati tramite un puntatore ogni volta che qualsiasi altro puntatore possibilmente-aliasing è scritto attraverso, che può causare una grave condotta di stallo chiamato un load-hit-store .

const e restrict sono concetti diversi, e non è il caso che const implica restrict. Tutto dice const è che non sarà possibile scrivere attraverso questo puntatore nell'ambito di applicazione di tale funzione . Un puntatore const può ancora essere alias. Ad esempio si consideri:

int foo( const int *a, int * b )
{
   *b *= 2;
   return *a + *b; // induces LHS: *a must be read back immediately
                   // after write has cleared the store queue
}

Mentre non è possibile scrivere direttamente a a in questa funzione, sarebbe perfettamente legale per voi chiamare foo come:

int x = 3;
foo( &x, &x );  // returns 12

restrict è una garanzia diversa:. Una promessa che a != b a tutti gli inviti a foo()

scritto su la parola chiave restrict e le sue implicazioni di performance a lungo , e ha così Mike Acton . Anche se si parla di una specifica PowerPC in ordine, il problema del carico-hit-negozio esiste sul x86 pure, ma i x86 di out-of-ordine di esecuzione rende tale stallo più difficili da isolare in un profilo.

E proprio per sottolineare: questo è non un'ottimizzazione arcana o prematura, se vi preoccupate per le prestazioni a tutti. restrict può portare a incrementi nella velocità davvero significative se usato correttamente.

La maggior parte di ciò che si sa è sbagliato!

const fa non la garanzia che qualcosa non cambierà alle spalle del compilatore. Tutto ciò che fa è smettere di di scrivere in quel punto. Un'altra cosa potrebbe essere ancora in grado di scrivere a quella posizione, però, in modo che il compilatore non può assumere che sia costante.

Come altri hanno detto, il qualificatore limitare è di circa aliasing. Infatti, durante il primo turno di C standardizzazione, c'è stata una proposta di una parola chiave "noalias". Purtroppo, la proposta è stata abbastanza scritta male - ha spinto l'unico e unica volta che il Dennis Ritchie è stato coinvolto durante il processo, quando ha scritto una lettera che ha detto qualcosa per l'effetto che "noalias devono andare Questo non è oggetto di trattativa.. "

Inutile dire, 'noalias' non sono diventati parte di C. Quando è arrivato il momento di provare di nuovo, la proposta è stato scritto abbastanza meglio che limitano è stato incluso nella norma - e anche se noalias sarebbero stati probabilmente un nome più significativo per lui, quel nome era così contaminato che dubito nessuno nemmeno presa in considerazione cercando di usarlo.

In ogni caso, l'intento primario di limitare è di informare il compilatore che non ci sarà un alias di questo oggetto. Una ragione di questo è quello di lasciare che le cose di essere memorizzati nei registri temporaneamente. Ad esempio, considerare qualcosa come:

void f(int *a, int *b, int *c) { 
    for (int i=0; i<*a; i++)
        *b += c[i];
}

Il compilatore vuole davvero mettere i in un registro, e caricare * una in un registro, in modo che quando arriva il momento di decidere se eseguire un'altra iterazione del ciclo, semplicemente confronta i valori in quelli ai registri gli uni agli altri . Purtroppo, non si può fare che - se qualcuno che ha usato questa funzione era completamente pazzo, e lo ha chiamato con un == b, ogni volta che si scrive a * b all'interno del ciclo, che il nuovo valore è anche il valore di un * - quindi deve leggere * una dalla memoria a ogni iterazione del ciclo, nel caso in cui chi chiamava era completamente folle. Utilizzando restringere comunica al compilatore può generare il codice supponendo che A e B sarà sempre distinti, così scrivendo a * un cambierà mai * b (o viceversa).

La vostra comprensione è in gran parte corretto. Il qualificatore restrict afferma semplicemente che i dati a cui si accede da un puntatore in modo qualificato è solo si accede da tale puntatore esatto. Essa si applica a legge come pozzi come scrive.

Il compilatore non si preoccupa di thread simultanei, non aveva intenzione di generare il codice in modo diverso, e si può clobber i propri dati come volete. Ma ha bisogno di sapere quali operazioni puntatore può cambiare ciò che la memoria globale.

Restrict comporta anche un avvertimento API per l'uomo che una determinata funzione è implementata tramite l'assunzione di parametri unaliased.

n bloccaggio da parte dell'utente è necessaria per quanto riguarda il compilatore è interessato. Si vuole solo assicurarsi che legge correttamente i dati che è stato suppone per essere colpita, dal codice di il compilatore doveva generare , nel caso in cui non v'è alcun qualificatore restrict. L'aggiunta di restrict lo libera da questa preoccupazione.

Infine, ricordiamo che il compilatore è probabile che sia già possibile analizzare aliasing basata su tipi di dati, ai livelli più alti di ottimizzazione, in modo restrict è importante soprattutto per le funzioni con più puntatori allo stesso tipo di dati. Si può prendere una lezione da questo tema e fare in modo che qualsiasi aliasing intenzionale che fate è fatto tramite un union.

Possiamo vedere restrict in azione:

void move(int *a, int *b) {     void move(int *__restrict a, int *__restrict b) {
    a[0] = b[0];                    a[0] = b[0];
    a[1] = b[0];                    a[1] = b[0];
}                               }
    movl    (%edx), %eax            movl    (%edx), %edx
    movl    %eax, (%ecx)            movl    %edx, (%eax)
    movl    (%edx), %eax            movl    %edx, 4(%eax)
    movl    %eax, 4(%ecx)

Nella colonna di destra, con restrict, il compilatore non ha bisogno di rileggere b[0] dalla memoria. E 'stato in grado di leggere b[0] e tenerlo nel registro %edx, e poi basta memorizzare il registro due volte per la memoria. Nella colonna di sinistra, ma non sapeva se il negozio per a potrebbe essere cambiato b.

Qualcuno più familiarità con lo standard potrebbe probabilmente dare una risposta migliore, ma io dare un colpo.

"I dati non saranno modificati alle spalle del compilatore" suona più come il contrario di "volatile" per me.

"const" indica i dati non saranno modificati davanti al programmatore; cioè, non può modificare i dati attraverso il significante contrassegnato come "const" (scrivo "significante", perché in int const *pi, il nome pi non è const, ma *pi è). I dati potrebbero essere modificabili tramite un altro significante (dati non-const possono essere passate ad una funzione come dati const, dopo tutto).

Questo "limitare" qualifica puntatori è la chiave. I puntatori sono l'unico modo per i dati alias in C, quindi sono l'unico modo in cui è possibile accedere a qualche pezzo di dati tramite due nomi diversi. "Limitare" è tutto di limitare l'accesso ai dati al percorso un accesso.

Questo potrebbe essere un esempio da un molto dominio stretta, ma la piattaforma di Altera Nios II è un microcontrollore soft-core che è possibile personalizzare all'interno di un FPGA. Poi, all'interno del codice sorgente C per quella micro, è possibile utilizzare uno strumento C-to-hardware per velocizzare cicli interni utilizzando hardware personalizzato, piuttosto che nel software.

Lì, uso della parola chiave __restrict__ (che è la stessa restrict di C99) permette all'utensile C2H per ottimizzare correttamente l'accelerazione hardware di funzionamento puntatore in parallelo anziché sequenziale. Almeno in questo caso, il restrict è semplicemente non significava per il consumo umano. Vedi anche pagina di Sun su restrict , dove la prima riga dice

  

Uso della qualificazione restrict adeguatamente nei programmi C può consentire al compilatore di produrre eseguibili significativamente più veloce.

Se qualcuno è interessato a saperne di più su C2H, questo PDF discute ottimizzando risultati C2H. La sezione sulla __restrict__ è a pagina 20.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top