2012-04-18 12 views
8

Es ist leicht zu sehen, dass:Logische Operationen auf Multiple-Modulus-Operationen optimiert?

(i % 3 == 0) && (i % 5 == 0) 

vereinfacht werden kann:

(i % 15 == 0) 

Doch in den Ausgang des GCC suchen, so scheint es, dies nicht auch bei hohen Optimierungsstufen durchgeführt wird.

Tun Compiler diese Art von Optimierungen, oder gibt es einen guten Grund, warum diese beiden Tests nicht semantisch gleichwertig sind?

Edit: Als Antwort auf diejenigen, die sagen, dass dies eine Rand Fall ist, wird die folgende ist ein ähnlicher Fall: dass

(i < 3) && (i < 5) 

Eine beliebige Anzahl von weniger als 3, muss immer kleiner als 5. Zweiter Test überflüssig ist.

Ich würde auch die folgenden in Reaktion auf die Antwort hinzufügen, dass der Compiler nicht, wenn die Umgebung beeinflußt wird wissen können ... auf diesen Code ein:

void foo(void) 
{ 
    int i; 
    for (i = 0; i <= 10; i++) 
    { 
     if (i > 20) 
     { 
      puts("Hi"); 
     } 
    } 
} 

Ganze Funktion reduziert auf „repz ret "von GCC mit -O2. Das ist viel komplexer als alles, worüber ich spreche.

+1

meine Vermutung ist es, Kurzschlussauswertung zu garantieren ... – Anycorn

+0

Prüfen Compiler im Allgemeinen für mehrere Vergleiche auf der gleichen Variable für die Optimierung? Es sieht aus wie ein Randfall ... – trutheality

+2

@Anycorn, Willst du sagen, dass die Bewertung "i" kann Nebenwirkungen haben, und der Compiler nicht, ob es tut oder nicht? – ikegami

Antwort

5

Ignoriere alle dummen Antworten, die behaupten, dass dies für einen Compiler sehr schwierig/unmöglich ist. Ich sehe keinen Grund, warum es schwierig sein würde, aber vermutlich dachte niemand daran, es zu tun oder dachte, es wäre wichtig genug, um es zu optimieren. Wenn Sie eine bessere Antwort als diese wollen, müssen Sie es auf dem GCC-Bug-Tracker als eine Anfrage für die Verbesserung melden und sehen, was die Entwickler sagen.

+0

Ich denke du meinst * in * ausreichend. Aber ja, ich stimme dir zu. – Matt

+1

Meine Grammatik war korrekt. Das Thema ist immer noch "niemand", was die negative Semantik liefert. :-) –

3

Ihr Beispiel ist ziemlich einfach und in der Tat leicht zu einem einzigen Vorgang zu kondensieren. Der allgemeine Fall einer solchen Aussage ist jedoch nicht so einfach. Nehmen Sie die folgenden, zum Beispiel:

((++i) % 3 == 0) && ((++i) % 5 == 0) 

Diese Variante nicht so leicht nach unten zu einem einzigen Modul Betrieb vereinfacht werden kann (ich weiß, diese Aussage alle möglichen anderen Probleme damit hat, aber der Punkt ist, dass das Problem isn‘ t so einfach, wenn Sie etwas anderes als eine einfache Variablenreferenz verwenden). Compiler werden im Allgemeinen nicht darauf achten, dass Ihr Fall nur einfache Variablen enthält und diese anders als im allgemeinen Fall optimiert. Sie versuchen, Optimierungen möglichst konsistent und vorhersehbar zu halten.

Update: Die zusätzlichen Fälle, die Sie in eine ganz andere Klasse von Optimierungs auf Ihre Frage fällt hinzugefügt als Ihr ursprünglicher Code-Schnipsel. Sie sind beide optimiert entfernt, weil sie nicht erreichbaren Code sind, und kann so zur Kompilierzeit als solche bewiesen werden. Ihre ursprüngliche Frage bestand darin, zwei Operationen in eine einzige, vom Original abweichende Operation umzuschreiben. Die zwei Snippets, die Sie hinzugefügt haben, schreiben den vorhandenen Code nicht neu, sondern entfernen nur Code, der nie ausgeführt werden kann. Compiler sind typischerweise sehr gut darin, toten Code zu erkennen und zu entfernen.

Die Optimierung, die Sie suchen, ist eine Form von mathematische Festigkeitsreduktion. Die meisten Compiler bieten ein gewisses Maß an MSR-Optimierungen, auch wenn sie je nach Compiler und den Fähigkeiten der Zielplattform unterschiedlich detailliert sind. Zum Beispiel kann ein Compiler, der auf eine CPU-Architektur abzielt, die keine Modulus-Anweisung hat (was bedeutet, dass eine möglicherweise lange Bibliotheksfunktion stattdessen verwendet werden muss), solche Anweisungen aggressiver optimieren. Wenn die Ziel-CPU eine Hardware-Modul-Unterstützung hat, könnte der Compiler-Schreiber die zwei oder drei gespeicherten Anweisungen als zu gering für einen Nutzen ansehen, um die Zeit und Mühe zu wert, die zum Implementieren und Testen der Optimierungsverbesserungen benötigt würde. Ich habe das in der Vergangenheit mit bestimmten Operationen gesehen, die in einer einzigen Anweisung auf einer x86-CPU (zum Beispiel) durchgeführt werden können, aber Dutzende von Anweisungen auf einer RISC-CPU erfordern würden.

+0

Ich stimme nicht zu. Compiler sind gut darin, gemeinsame Teilausdrücke (von denen einfach "i" ein trivialer Fall ist) zu finden und ihre Verwendung zu optimieren. –

+1

@R ..- Dies ist jedoch nicht als allgemeiner Unterausdruck zu klassifizieren. CSE-Optimierungen suchen nach einer Unterkomponente, die mehrere Anweisungen gemeinsam haben, und verwenden dann einen vorab zwischengespeicherten Wert, anstatt den Ausdruck mehrmals neu zu berechnen. Hier ist der einzige gemeinsame Teil "i%", der keine vollständige Aussage ist und nicht berechenbar ist. Was das OP verlangt, ist enger mit der Optimierungsfamilie der "mathematischen Stärkenreduzierung" verbunden. – bta

+0

Beachten Sie außerdem, dass der Operator '&&' einen Sequenzpunkt eingibt (für den "Kurzschluss" -Betrieb). Wenn die linke Seite null ergibt, wird die rechte Seite überhaupt nicht ausgeführt. Aufgrund der vielen möglichen Nebenwirkungen sind Compiler sehr zögerlich, einen Sequenzpunkt zu optimieren. Für einfache Fälle scheint das einfach zu sein, aber als allgemeines Problem ist es viel schwieriger. – bta

1

Ehrlich gesagt, das ist ein sehr spezifischer Fall. Wenn ein Teil der Gleichung geändert wird, würde die Optimierung nicht länger gelten. Ich denke, es ist eine Frage von Kosten gegen Gewinne, und die Kosten der Implementierung dieser Optimierung (erhöhte Kompilierungszeit, erhöhte Wartungsschwierigkeiten) überwiegen die Vorteile (einige weniger schnelle Operationen in seltenen Fällen).

Im Allgemeinen kann das mathematische Refactoring aufgrund der Präzisionsbeschränkung und der Möglichkeit eines Überlaufs nicht angewendet werden. Obwohl ich denke, dass es hier kein Problem gibt.