Widać że nie rozumiesz tego triku... |
Ależ doskonale rozumiem. I właśnie przez to wiem, że nie ma sensu tego stosować, bo kompilator sam to zrobi, a kod niech lepiej będzie czytelny. Przykład w C++:
unsigned foo( unsigned x ) {
return x % 8;
}
Co się stanie po kompilacji?
.globl _Z3fooj
.def _Z3fooj; .scl 2; .type 32; .endef
.seh_proc _Z3fooj
_Z3fooj:
.LFB0:
.seh_endprologue
mov eax, ecx
and eax, 7
ret
.seh_endproc
O, and.
Żeby była jasność, że to trywialna i bardzo często spotykana optymalizacja, przykład w Common Lispie, czyli bardzo dynamicznym języku, gdzie można np. definiować funkcje w trakcie działania programu, z dynamicznym typowaniem:
(defun foo (x)
(declare (type (unsigned-byte 32) x))
(rem x 8))
Jeśli poprosić SBCL o wygenerowany kod, to otrzymamy coś takiego — obcięte do właściwego ciała funkcji, bo przecież sprawdzanie, czy
x to naprawdę 32-bitowa liczba bez znaku, a nie na przykład string, nas tu nie interesuje; jakby kogoś interesowało, to może użyć
sb-disassem:disassemble-code-component zamiast
disassemble:
CL-USER> (disassemble #'foo)
; disassembly for FOO
; Size: 52 bytes. Origin: #x10044284B4
; B4: 84042500000F20 TEST AL, [#x200F0000] ; safepoint
; no-arg-parsing entry point
; BB: 488BD1 MOV RDX, RCX
; BE: 4883E20E AND RDX, 14
; C2: 488BE5 MOV RSP, RBP
; C5: F8 CLC
; C6: 5D POP RBP
; C7: C3 RET
O, nawet tutaj jest and.
Ten test sprawdzał operacje, gdzie obydwa operandy są zmiennymi. Liczba 8 jest stałą, a dzielenie przez stałą, nawet niekoniecznie potęgę dwójki, optymalizuje praktycznie każdy kompilator.