Quanto è eccessivo l'utilizzo di macro "probabili" e "improbabili"?


12

Le macro spesso conosciute come likelye le unlikelymacro aiutano il compilatore a sapere se di ifsolito verrà immesso o ignorato un. Usarlo comporta alcuni (piuttosto minori) miglioramenti delle prestazioni.

Ho iniziato a usarli di recente e non sono sicuro di quanto spesso debbano essere usati tali suggerimenti. Attualmente lo uso con controllo errori ifs, che di solito sono contrassegnati come unlikely. Per esempio:

mem = malloc(size);
if (unlikely(mem == NULL))
  goto exit_no_mem;

Sembra ok, ma il controllo degli errori ifavviene abbastanza spesso e di conseguenza l'uso di tali macro.

La mia domanda è: è troppo avere likelye unlikelymacro su ogni controllo degli errori if?

Mentre ci siamo, in quali altri luoghi vengono spesso utilizzati?


Nel mio attuale utilizzo è in una libreria che fa un'astrazione dal sottosistema in tempo reale, quindi i programmi diventerebbero portatili tra RTAI, QNX e altri. Detto questo, la maggior parte delle funzioni sono piuttosto piccole e chiamano direttamente una o due altre funzioni. Molte sono persino static inlinefunzioni.

Quindi, prima di tutto, non è un'applicazione che potrei profilare. Non ha senso "identificare i colli di bottiglia" poiché è una libreria, non un'applicazione autonoma.

In secondo luogo, è un po 'come "So che è improbabile, potrei anche dirlo al compilatore". Non cerco attivamente di ottimizzare il if.


7
puzza di micro ottimizzazione per me ...
maniaco del cricchetto

2
Per il codice dell'applicazione, li aggiungerei solo se la profilazione mostrasse che questo codice viene utilizzato in un hot path.
Codici InChaos


@james, che dice solo likelyed unlikelyesiste e cosa fanno. Non ho trovato nulla che suggerisca effettivamente quando e dove è meglio usarli.
Shahbaz

@Shahbaz "Se la condizione è spesso falsa, l'esecuzione non è lineare. Al centro c'è un grosso frammento di codice inutilizzato che non solo inquina l'L1i a causa del prefetch, ma può anche causare problemi con la previsione del ramo. Se il la previsione del ramo è sbagliata, l'espressione condizionale può essere molto inefficiente ". Quindi, loop stretti in cui si desidera assicurarsi che le istruzioni necessarie siano nella cache L1i
James

Risposte:


12

Hai bisogno di prestazioni così gravi da essere disposto a inquinare il tuo codice con quello? È un'ottimizzazione minore.

  • Il codice viene eseguito in un ciclo stretto?
  • La tua applicazione ha problemi di prestazioni?
  • Hai profilato la tua applicazione e determinato che questo particolare ciclo costa molto tempo alla CPU?

A meno che tu non possa rispondere yesa tutto quanto sopra, non preoccuparti di cose come questa.

Modifica: in risposta alla modifica. Anche quando non riesci a creare il profilo, di solito puoi stimare gli hotspot. Una funzione di allocazione della memoria che viene chiamata da tutti è un buon candidato, soprattutto perché richiede un solo uso della macro per funzionare per l'intera libreria.


1
Per essere chiari, non ti ho votato in negativo. Tuttavia, la tua risposta non risponde davvero alla mia domanda. Stai cercando di dire che la (un)likelymacro viene utilizzata raramente e solo in un codice estremamente critico per le prestazioni? È "cattiva pratica" usarlo spesso, o semplicemente "non necessario"?
Shahbaz

@Shahbaz Rende il codice meno leggibile e l'ottimizzazione delle prestazioni può variare da un guadagno banale a una perdita banale. Quest'ultimo, quando il presupposto circa la probabilità era errato o è cambiato per essere errato a causa di una successiva modifica ad altre parti del codice. Se non dovrebbe mai essere usato a meno che non sia necessario.
Peter,

3
@Peter: Anche se è un peccato che la sintassi non sia migliore, le annotazioni su ciò che è probabile o improbabile possono fornire informazioni utili agli umani che stanno leggendo il codice. Ad esempio, qualcuno che ha visto if (likely(x==2 || x==3)) doOneThing(); else switch(x) { ... }, potrebbe giudicare che l'uso da parte del programmatore di un ifper i valori 2 e 3 non era semplicemente una conseguenza del fatto che il programmatore non sapeva che C può associare due caseetichette a un singolo gestore.
supercat

Nessuno ha menzionato ciò che ritengo sia un punto critico. Non è solo che il percorso "improbabile" si verifichi meno spesso, può essere quel condizionamento su quel percorso che si verifica che non ti interessa affatto della velocità. Ad esempio, una periferica non risponde, quindi è necessario ripristinarla e dormire comunque.
Benjamin Lindqvist,

2

Se stai scrivendo per x86 / x64 (e non stai utilizzando CPU di 20 anni), il guadagno in termini di prestazioni derivante dall'uso di __builtin_expect () sarà trascurabile. Il motivo è che le moderne CPU x86 / x64 (non sicure al 100% su Atom), hanno una previsione dinamica del ramo, quindi essenzialmente la CPU "impara" sul ramo che viene preso più spesso. Certo, queste informazioni possono essere archiviate solo per un numero limitato di filiali, tuttavia sono possibili solo due casi. Se (a) è un ramo "usato frequentemente", allora il tuo programma trarrà beneficio da quella previsione dinamica del ramo, e se (b) è un ramo "raro", non vedrai davvero alcun impatto realistico sulle prestazioni a causa di errate previsioni in rami così rari (20 cicli di CPU di cattiva previsione sui rami non sono TROPPO se si verificano una volta in una luna blu).

NB: questo NON implica che sulla moderna x86 / x64 l'importanza della cattiva reputazione del ramo sia diminuita: qualsiasi ramo con 50-50 possibilità di salto-no-jump subirà comunque una penalità (cicli CPU 10-20 IIRC), quindi nei circuiti interni i rami possono devono ancora essere evitati. È solo importante di __builtin_expect () su x86 / x64 che è diminuito (IIRC, circa 10-15 anni fa), principalmente a causa della previsione dinamica del ramo.

NB2: per altre piattaforme oltre x86 / x64, YMMV.


Bene, il compilatore sa quale ramo la CPU si aspetterà di avere meno probabilità. E può far sì che quello sia effettivamente improbabile. Ma probabilmente il compilatore conosce già quel modello senza la unlikelynotazione.
Deduplicatore,

@Deduplicator: con la previsione del ramo dinamico, il compilatore non sa quale ramo è più probabile poiché la CPU lo calcola in runtime sulla base di precedenti esecuzioni proprio in questo punto del codice.
No-Bugs Hare
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.