2016-05-31 7 views
2

Prozessoren sind bekannt, spezielle Anweisungen zum Dekrementieren eines Zählers und Verzweigung, wenn der Zähler Null ist mit sehr niedrigen Latenz, wie der Verzweigung Befehl nicht warten müssen das Zählerdekrement, das durch eine Integer-Einheit läuft. HierWie schreibt man Schleife in C so Compiler kann Verzweigung auf Null nach Dekrement verwenden

ist ein Link auf die ppc Anweisung:

https://www.ibm.com/support/knowledgecenter/ssw_aix_53/com.ibm.aix.aixassem/doc/alangref/bc.htm

Meine übliche Art und Weise zu tun, was ich glaube, löst einen Compiler die entsprechenden Anweisungen zu erzeugen, ist wie folgt:

unsigned int ctr = n; 
while(ctr--) 
    a[ctr] += b[ctr]; 

Ablesbarkeit ist hoch und es ist eine dekrementierende Schleife, die auf Null verzweigt. Wie Sie sehen, tritt der Zweig technisch auf, wenn der Zähler vor dem Dekrement null ist. Ich hatte gehofft, der Compiler könnte etwas Magie machen und es trotzdem funktionieren lassen. F: Wäre ein Compiler gezwungen, irgendwelche fundamentalen Regeln von C zu brechen, um sie zu speziellen Dekrement- und Verzweigungs-bedingten Anweisungen (falls vorhanden) zu verändern?

Ein anderer Ansatz:

unsigned int ctr = n+1; 
while(--ctr) { 
    a[ctr-1] += b[ctr-1]; 
} 

Der Zweig jetzt nach Abnahme passieren, aber es gibt Roaming-Konstanten hässlich Code zu machen. Eine "Index" -Variable, die um eins kleiner ist als der Zähler, würde es etwas hübscher aussehen lassen, denke ich. Mit Blick auf verfügbare ppc-Anweisungen kann die zusätzliche Berechnung beim Finden der a- und b-Adresse immer noch für einen einzelnen Befehl passen, da Laden auch Adressarithmetik (addieren) ausführen kann. Nicht so sicher über andere Befehlssätze. Mein Hauptproblem ist jedoch, wenn n + 1 größer als ein max ist. F: Wird das Dekrement wie üblich zum Maximum und zum Loop zurückkehren?

F: Gibt es in C ein häufiger verwendetes Muster für die gemeinsame Anweisung?

Edit: ARM hat eine Dekrement- und Verzweigungsoperation, aber verzweigt nur, wenn der Wert NICHT Null ist. Es scheint eine zusätzliche Bedingung zu geben, genau wie die ppc bc. Wie ich es sehe, ist es vom C-Standpunkt aus gesehen dasselbe, also erwarte ich, dass ein Code-Snippet auch ohne C-Standard-Verletzung zu dieser Form kompilierbar ist. http://www.heyrick.co.uk/armwiki/Conditional_execution


Edit: Intel hat praktisch die gleiche Verzweigungsanweisung wie ARM: http://cse.unl.edu/~goddard/Courses/CSCE351/IntelArchitecture/InstructionSetSummary.pdf

+0

Lesbarkeit ist hoch? Die Lesbarkeit ist so hoch, dass Pre/Post Inkrement/Dekrement aus Swift3 entfernt wurde. Ich würde versuchen Memcpy oder Memmove. – gnasher729

+0

Ich persönlich habe kein Problem mit Pre/Post Inkrement. memcpy/memmove ist keine Option: Er kopiert nicht, er fügt Werte hinzu ('+ =' anstelle von '='). – Aconcagua

Antwort

1

Was dazu:

do 
{ 
    a[ctr] += b[ctr]; 
} 
while(--ctr); 

Sie würden eine zusätzliche Kontrolle benötigen, aber:

if(n != 0) 
{ 
    /*...*/ 
} 

wenn Sie das nicht mit anderen Mitteln garantieren können ...

Oh, und sich bewusst sein, dass ctr je unterschiedliche Endwerte hat, auf der Schleife Variante Sie wählen (0 in meine und Ihre zweite, ~ 0 in Ihrem ersten) ...

2

Das wird auf abhängen die Bemühungen der Optimierungsautoren Ihres Compilers.

Zum Beispiel könnte ein bdz Opcode am Ende einer Schleife verwendet werden, um einen anderen Sprung, der an die Spitze zurückgekehrt ist, "zu überspringen". (Das wäre eine schlechte Idee, aber es könnte passieren.)

Viel wahrscheinlicher wäre zu dekrementieren und zu verzweigen, wenn NICHT Null, was der PPC auch unterstützt.

loop: 
    blah 
    blah 

    bdnz ... loop 

fallthru: 

Sofern Sie einen zwingenden Grund haben, um zu versuchen, den OP-Codes zum Spiel, ich würde vorschlagen, dass Sie versuchen, sauber, lesbaren Code zu schreiben, die Nebenwirkungen minimiert. Ihr eigener Wechsel von Nach-Dekrement zu Vor-Dekrement ist ein gutes Beispiel dafür - ein weniger (nicht verwendeter) Nebeneffekt, um den sich der Compiler kümmern muss.

Auf diese Weise erhalten Sie den meisten Knall für Ihren optimierenden Dollar. Wenn es eine Plattform gibt, die eine spezielle Version Ihres Codes benötigt, können Sie #ifdef die ganze Sache, und enthalten Sie entweder Inline-Assembly, oder schreiben Sie den Code in Verbindung mit dem Lesen der Assembly-Ausgabe und Ausführen des Profilers.

+0

Wie verwalten diese n = 0? Ihre Antwort gab mehr Ideen. Ich hatte die Idee vor der bedingten Verzweigung war immer am Anfang der Schleife. Jetzt bin ich nicht so sicher ... Eine bedingte Suche nach Null vor der Schleife und der have condition Branch, die prüft, ob sie erneut ausgeführt werden sollte, könnte sinnvoller sein, abhängig davon, ob es eine gemeinsame Verzweigungsvorhersagegröße und wie viele Anweisungen gibt werden zu einer Zeit geholt. Ugh, ich bedauere es, die Frage überhaupt gestellt zu haben. So viel zu beachten. – Andreas

+0

Irgendwie eine Do-while, ist es nicht ... – Aconcagua

+1

Zurück in dem Tag, als dec/bnz war neu und cool, und Datenbusse waren 16 Bits breit, max, gab es eine Menge von Compilern, die Schleifen, die generiert im Grunde mit einem "Sprung nach unten" gestartet, dann hatte der untere Teil der Schleife einen "Dekrement-Zweig-nicht-Null-nach-oben" Opcode. (Dies war, bevor Cache-Misses der dominierende Faktor bei der Codegenerierung wurde.) Im Wesentlichen war eine Do-While-Schleife die "natürliche" Form und while- und for-Schleifen waren "do while" -Schleifen mit einem Sprung nach unten an der Spitze . :-) –

2

Kommt definitiv auf den Compiler an, aber es ist eine Anweisung, die für die Leistung großartig ist, also würde ich erwarten, dass Compiler versuchen, seine Verwendung zu maximieren.

Da Sie eine AIX-Referenz verknüpfen, gehe ich davon aus, dass Sie xlc ausführen. Ich habe keinen Zugriff auf eine AIX-Maschine, aber ich habe Zugriff auf xlc auf einer Z-Maschine. Das äquivalente Z-Gegenstück ist der Branch On Count (BCTR) Befehl.

versuchte ich 5 Beispiele und überprüft die Objekte

int len = strlen(argv[1]); 
//Loop header 
argv[1][counter] += argv[2][counter]; 

Mit den folgenden Schleife Header:

for (int i = 0; i < len; i++) 
for (int i = len-1; i >= 0; i--) 
while(--len) 
while(len--) 
while(len){ 
    len--; 

Alle 5 Beispiele verwenden Zweig auf Zahl bei -O1 und höher, und keiner von ihnen verwenden es bei opt 0.

Ich würde einem modernen Compiler vertrauen, um Verzweigung auf null Gelegenheiten mit irgendeiner Standardschleifenstruktur finden zu können.

+0

Generieren alle Beispiele die gleiche Baugruppe? Denn das wäre echt cool. Übrigens muss (- len) aufpassen, mit Null zu beginnen. – Andreas

+0

Nein, der Code ist etwas anders, aber insgesamt ziemlich ähnlich. Völlig identischer Code wäre beeindruckend lol. –