constexpr Tiefenbegrenzung mit Clang (fcontexpr-Tiefe scheint nicht zu funktionieren)

8

Gibt es überhaupt eine Konfiguration für die Instantiierungstiefe von conexpr? Ich laufe mit -fconstexpr-depth = 4096 (mit clang / XCode).

Trotzdem kann dieser Code mit Fehler nicht kompiliert werden: Die Contex-Variable fib_1 muss durch einen konstanten Ausdruck initialisiert werden. Der Code schlägt fehl, unabhängig davon, ob die Option -fconstexpr-depth = 4096 gesetzt ist oder nicht.

Ist das ein Bug mit Klängen oder wird erwartet, dass er sich so verhält? Hinweis: Dies funktioniert gut bis fib_cxpr (26), 27 ist, wenn es zu versagen beginnt.

Code:

%Vor%     
Sarang 05.07.2014, 23:42
quelle

1 Antwort

16

TL; DR:

Für clang wollen Sie das Kommandozeilenargument -fconstexpr-steps=1271242 und Sie brauchen nicht mehr als -fconstexpr-depth=27

Die rekursive Methode zur Berechnung von Fibonacci-Zahlen erfordert nicht sehr viel Rekursionstiefe. Die für fib(n) erforderliche Tiefe ist tatsächlich nicht mehr als n . Dies liegt daran, dass die längste Kette von Aufrufen über den rekursiven Aufruf fib(i-1) erfolgt.

%Vor%

Daraus können wir schließen, dass -fconstexpr-depth nicht die richtige Einstellung ist.

Außerdem weisen die Fehlermeldungen auf einen Unterschied hin:

%Vor%

Kompiliert mit -fconstexpr-depth=26 , um sicher zu sein, dass wir dieses Limit erreicht haben, erzeugt clang die folgende Meldung:

%Vor%

Aber das Kompilieren mit -fconstexpr-depth=27 , was genug Tiefe ist, erzeugt die Nachricht:

%Vor%

Wir wissen also, dass Clang zwischen zwei Fehlern unterscheidet: Rekursionstiefe und "Step-Limit".

Die Top-Google-Ergebnisse für 'clang maximum step limit' führen zu Seiten über den clang-Patch, der diese Funktion implementiert, einschließlich der Implementierung der Befehlszeilenoption: -fconstexpr-steps . Weiteres Googlen dieser Option zeigt an, dass keine Dokumentation auf Benutzerebene vorhanden ist.

Es gibt also keine Dokumentation darüber, was clang für " fib(27) " als "Schritt" oder wie viele "steps" clang benötigt. Wir könnten das sehr hoch machen, aber ich denke, das ist eine schlechte Idee. Stattdessen zeigen einige Experimente:

%Vor%

Dies zeigt an, dass Schritte ( fib(n) ) == Schritte ( fib(n-1) ) + Schritte ( fib(n-2) ) + 2. Ein bisschen Berechnung zeigt, dass fib(27) demnach 1.271.242 von clangs Schritten benötigen sollte . Das Kompilieren mit -fconstexpr-steps=1271242 sollte daher die Kompilierung des Programms erlauben, was tatsächlich tut . Das Kompilieren mit -fconstexpr-steps=1271241 führt zu einem Fehler wie zuvor, also wissen wir, dass wir ein genaues Limit haben.

Eine alternative, weniger genaue Methode besteht darin, aus dem Patch zu sehen, dass das Standard-Stufenlimit 1.048.576 (2 20 ) ist, was offensichtlich für fib(26) ausreicht. Intuitiv, verdoppelt das sollte reichlich sein, und aus der früheren Analyse wissen wir, dass zwei Millionen reichlich ist. Eine enge Grenze wäre ⌈φ · Schritte ( fib(26) ) which (was zufällig genau 1.271.242 ist).

Eine weitere Sache, die zu beachten ist, ist, dass diese Ergebnisse klar zeigen, dass clang keine Memoisierung der consExpr-Evaluierung vornimmt. GCC tut , aber es scheint, dass dies überhaupt nicht im Clang implementiert ist. Obwohl Memoization die Speicheranforderungen erhöht, kann es manchmal, wie in diesem Fall, die Zeit für die Auswertung erheblich reduzieren. Die zwei Schlüsse, die ich daraus ziehe, sind, dass das Schreiben von constexpr-Code, der Memoization für gute Kompilierzeiten erfordert, keine gute Idee für portablen Code ist und dass clang mit der Unterstützung für conexpr memoization und einer Kommandozeilenoption zum Aktivieren / Deaktivieren verbessert werden könnte.

    
bames53 06.07.2014, 01:04
quelle

Tags und Links