2016-05-12 15 views
2

Die folgenden zwei Codesequenzen erzeugen, um das gleiche Ergebnis:Gibt es einen Fall, in dem "&" eine geringere Leistung als "%" liefert?

uint mod = val % 4; 

uint mod1 = val & 0x3; 

I kann sowohl das gleiche Ergebnis zu berechnen, verwenden. Ich weiß, dass in der Hardware der & Operator viel einfacher als der % Operator realisiert ist. Daher erwarte ich, dass es eine bessere Leistung als der % Operator hat.

Kann ich immer davon ausgehen, dass die & eine bessere oder gleiche Leistung hat? Wird dies von einem Compiler automatisch optimiert?

+5

Sie können nie etwas annehmen, was oder wie der Compiler optimiert. Der Standard überlässt dies explizit dem Compiler. Und falls Sie nicht nur den Rest, sondern auch den Quotienten benötigen, [siehe div()] (http://en.cppreference.com/w/c/numeric/math/div). – DevSolar

+1

Jeder einigermaßen moderne Compiler wird bei diesen Arten von Mikrooptimierungen ** weit ** jeden Menschen übertreffen. Verwenden Sie immer das Konstrukt, das am deutlichsten die Absicht darstellt. – sp2danny

+1

Nein, Sie können nicht wirklich davon ausgehen, dass der Compiler den Code immer optimieren wird. Ja, '&' ist normalerweise schneller. Was du schreibst, sollte das sein, was du wirklich vorhast. – Ian

Antwort

8

Sie können nichts von einer dieser Operationen annehmen, eine Kompilierung könnte beide zu den gleichen Anweisungen optimieren.

Und in der Tat, beide clang und gcc übersetzen sie in eine einzige and Anweisung.

Leider aufgrund der Natur von % mit einem angegebenen Rückgabewert für negative Werte seit ISO C99, ist einige zusätzliche Arbeit für signed ganze Zahlen erforderlich. Im Gegensatz zu ISO C90, wo negativer Modulo implementiert wurde.

Die resultierende Anordnung für beide Operationen auf beiden signed und unsigned Werte:

modulo mit signierten Zahlen:

mov  eax, DWORD PTR [esp+4]  ; grab `val` 
cdq         ; convert 32-bit EAX to 64-bit 
            ; and fill EDX with the sign bit 
shr  edx, 30      ; shift EDX by 30 positions to the right 
            ; leaving only the two left-most bits 
add  eax, edx      ; add EDX to EAX 
and  eax, 3      ; do the AND 
sub  eax, edx      ; subtract EDX from EAX 
mov  DWORD PTR [esp+8], eax  ; move result on stack 

Dieser netter Trick ist, haben richtig für negative Werte definiert Verhalten. Es macht ((val + 3) & 3) - 3 für negative Werte und val & 3 für positive.

and mit/ohne Vorzeichen und modulo mit unsigned:

mov  eax, DWORD PTR [esp+4] 
and  eax, 3 
mov  DWORD PTR [esp+12], eax 
+2

Der Grund für die unterschiedliche Assembly-Ausgabe zwischen & und% ist, dass sie nicht das gleiche Ergebnis liefern. '(-1 & 0x03) = 3' während' (-1% 3) = -1' –

+0

@ P.Kouvarakis Absolut richtig, behoben, und fügte auch eine Erklärung der verschiedenen Verhaltensweisen für 'signed' und' unsigned' hinzu ganze Zahlen. – Leandros

+0

@ElderBug Ich habe eine Erklärung hinzugefügt, um den Unterschied zwischen einer 'signierten' und 'unsignierten' Operation aufzuzeigen. – Leandros

1

Ein (moderner) Compiler sollte den gleichen Code aus Ihrem Beispiel generieren, und der Grund für diese Optimierung ist, dass die rhs eine Kompilierkonstante ist.

Soweit ich weiß, der & Operator ist mit einem Prozessor Anweisung and; Bei dem Modulo-Operator ist das nicht der Fall, was im Allgemeinen viel mehr Operationen bedeutet (Berechnen des Rests der Ganzzahl-Division). AFAIK, es ist in der Regel länger als eine Multiplikation; Ich halte es so lange für eine ganzzahlige Division.