2014-07-05 9 views
8

Esiste comunque la possibilità di configurare la profondità di istanza di constexpr? Sto correndo con -fconstexpr-depth = 4096 (usando clang/XCode).limite di profondità constexpr con clang (non sembra funzionare fconstexpr profondità)

Ma non riesco ancora a compilare questo codice con errore: La variabile di Constexpr fib_1 deve essere inizializzata da un'espressione costante. Il codice fallisce indipendentemente dal fatto che l'opzione -fconstexpr-depth = 4096 sia impostata o meno.

Si tratta di un bug con clang o si prevede che si comporti in questo modo. Nota: questo funziona bene fino a quando fib_cxpr (26), 27 è quando inizia a fallire.

Codice:

constexpr int fib_cxpr(int idx) { 
    return idx == 0 ? 0 : 
      idx == 1 ? 1 : 
      fib_cxpr(idx-1) + fib_cxpr(idx-2); 
} 

int main() { 
    constexpr auto fib_1 = fib_cxpr(27); 
    return 0; 
} 
+0

@Brian: grazie. In qualche modo non ero in grado di formattarlo correttamente. – Sarang

+0

Hm, GCC lo gestisce bene ... –

+0

D'altra parte, Clang è perfettamente soddisfatto di un tradizionale metaprogramma in stile modello. –

risposta

16

TL; DR:

Per clang si desidera l'argomento della riga di comando -fconstexpr-steps=1271242 e non avete bisogno di più di -fconstexpr-depth=27


Il metodo ricorsivo di calcolo I numeri di Fibonacci non richiedono molta profondità di ricorsione. La profondità richiesta per fib(n) non è in realtà più di n. Questo perché la più lunga catena di chiamate è attraverso la chiamata ricorsiva fib(i-1).

constexpr auto fib_1 = fib_cxpr(3); // fails with -fconstexpr-depth=2, works with -fconstexpr-depth=3 
constexpr auto fib_1 = fib_cxpr(4); // fails with -fconstexpr-depth=3, works with -fconstexpr-depth=4 

Quindi possiamo concludere che -fconstexpr-depth non è l'impostazione che conta.

Inoltre, i messaggi di errore indicano anche una differenza:

constexpr auto fib_1 = fib_cxpr(27); 

Compilato con -fconstexpr-depth=26, per essere sicuri ci ha colpito quel limite, clang genera il messaggio:

note: constexpr evaluation exceeded maximum depth of 26 calls 

Ma compilazione con -fconstexpr-depth=27, che è abbastanza profondità, produce il messaggio:

note: constexpr evaluation hit maximum step limit; possible infinite loop? 

Quindi sappiamo che clang distingue tra due errori: profondità di ricorsione e "limite di passo".


I primi risultati di Google per 'limite massimo passo clang' portare alla consultabili per il cerotto clangore di attuazione questa funzionalità, compresa l'attuazione dell'opzione della riga di comando: -fconstexpr-steps. Googling di questa opzione indica che non esiste documentazione a livello utente.

Quindi non c'è documentazione su ciò che conta il clang come un "passo" o quanti clang "passaggi" richiede per fib(27). Potremmo semplicemente impostarlo molto in alto, ma penso che sia una cattiva idea. Invece qualche sperimentazione mostra:

n : steps 
0 : 2 
1 : 2 
2 : 6 
3 : 10 
4 : 18 

che indica che passi (fib(n)) == gradini (fib(n-1)) + passi (fib(n-2)) + 2. Un po 'di calcolo mostra che, in base a questo, fib(27) dovrebbe richiedere 1.271.242 di Clang di passi. Quindi compilare con -fconstexpr-steps=1271242 dovrebbe consentire la compilazione del programma, che in effetti è it does.La compilazione con -fconstexpr-steps=1271241 produce un errore come prima, quindi sappiamo che abbiamo un limite esatto.

Un'alternativa, meno esatto metodo comporta l'osservare dal cerotto che il limite passo predefinito è 1.048.576 (2), che è ovviamente sufficiente per fib(26). Intuitivamente, il raddoppio dovrebbe essere abbondante, e dalla precedente analisi sappiamo che due milioni sono abbondanti. Un limite stretto sarebbe ⌈φ · passi (fib(26)) ⌉ (che corrisponde esattamente a 1.271.242).


Un'altra cosa da notare è che questi risultati mostrano chiaramente che clang non sta facendo alcuna memoizzazione della valutazione di constexpr. GCC does, ma sembra che questo non sia implementato in clang. Sebbene la memoizzazione aumenti i requisiti di memoria, a volte può, come in questo caso, ridurre notevolmente il tempo necessario per la valutazione. Le due conclusioni che traggo da questo sono che scrivere codice constexpr che richiede la memoizzazione per buoni tempi di compilazione non è una buona idea per il codice portatile, e che clang potrebbe essere migliorato con il supporto per la memoization di constexpr e un'opzione della riga di comando per abilitarlo/disabilitarlo.

+0

Quindi i passaggi sono esso stesso una sequenza in stile Fibonacci? –

+0

@ BenVoigt Sì, credo sia un po 'strano ma ha senso. (e puoi vedere nell'esempio compilato a cui ho collegato che ho appena modificato una copia della funzione originale per calcolare il numero di passaggi, invece di elaborare il modulo chiuso o fare qualcosa di altrettanto intelligente.) – bames53

+0

@ bames53: grazie per i dettagli spiegazione. Immagino di aver perso l'espansione del problema semantico segnalato. XCode ha dato un'ottima spiegazione di come è andato storto. – Sarang

Problemi correlati