ให้ดูที่โปรแกรม C สองตัวที่ทำหน้าที่เป็นบิตและแบ่ง
#include <stdlib.h>
int main(int argc, char* argv[]) {
int i = atoi(argv[0]);
int b = i << 2;
}
#include <stdlib.h>
int main(int argc, char* argv[]) {
int i = atoi(argv[0]);
int d = i / 4;
}
เหล่านี้จะถูกรวบรวมgcc -S
เพื่อดูว่าชุดประกอบจริงจะเป็นอะไร
ด้วยรุ่น bit shift จากการเรียกไปยัง atoi
ยังการส่งคืน:
callq _atoi
movl $0, %ecx
movl %eax, -20(%rbp)
movl -20(%rbp), %eax
shll $2, %eax
movl %eax, -24(%rbp)
movl %ecx, %eax
addq $32, %rsp
popq %rbp
ret
ในขณะที่รุ่นแบ่ง:
callq _atoi
movl $0, %ecx
movl $4, %edx
movl %eax, -20(%rbp)
movl -20(%rbp), %eax
movl %edx, -28(%rbp) ## 4-byte Spill
cltd
movl -28(%rbp), %r8d ## 4-byte Reload
idivl %r8d
movl %eax, -24(%rbp)
movl %ecx, %eax
addq $32, %rsp
popq %rbp
ret
เพียงแค่ดูที่นี่มีคำแนะนำเพิ่มเติมในเวอร์ชันหารเมื่อเทียบกับการเปลี่ยนบิต
ที่สำคัญคือพวกเขาทำอะไร?
ในรุ่น shift shift คำสั่งที่สำคัญคือ shll $2, %eax
คือการเลื่อนไปทางซ้ายตรรกะ - มีการแบ่งและทุกสิ่งทุกอย่างเป็นเพียงการเคลื่อนย้ายค่าไปรอบ ๆ
ในเวอร์ชันหารคุณจะเห็นidivl %r8d
- แต่เหนือกว่านั้นคือcltd
(แปลงยาวเป็นสองเท่า) และตรรกะเพิ่มเติมบางส่วนรอบ ๆ การหกและโหลดใหม่ งานเพิ่มเติมนี้โดยรู้ว่าเรากำลังเผชิญกับคณิตศาสตร์มากกว่าบิตเป็นสิ่งที่จำเป็นเพื่อหลีกเลี่ยงข้อผิดพลาดต่าง ๆ ที่อาจเกิดขึ้นได้ด้วยการทำคณิตศาสตร์เพียงเล็กน้อย
ให้คูณอย่างรวดเร็ว:
#include <stdlib.h>
int main(int argc, char* argv[]) {
int i = atoi(argv[0]);
int b = i >> 2;
}
#include <stdlib.h>
int main(int argc, char* argv[]) {
int i = atoi(argv[0]);
int d = i * 4;
}
แทนที่จะผ่านทั้งหมดนี้มีหนึ่งบรรทัดที่แตกต่าง:
$ diff mult.s bit.s
24c24
> shll $ 2,% eax
---
<sarl $ 2,% eax
ที่นี่คอมไพเลอร์ก็สามารถระบุได้ว่าคณิตศาสตร์สามารถทำได้ด้วยการเปลี่ยน แต่แทนที่จะเป็นตรรกะการเปลี่ยนแปลงมันจะทำการเปลี่ยนแปลงทางคณิตศาสตร์ ความแตกต่างระหว่างสิ่งเหล่านี้จะเห็นได้ชัดถ้าเราวิ่งเหล่านี้ - sarl
รักษาสัญญาณ ดังนั้น-2 * 4 = -8
ในขณะที่shll
ไม่
ให้ดูที่นี้ในสคริปต์ Perl อย่างรวดเร็ว:
#!/usr/bin/perl
$foo = 4;
print $foo << 2, "\n";
print $foo * 4, "\n";
$foo = -4;
print $foo << 2, "\n";
print $foo * 4, "\n";
เอาท์พุท:
16
16
18446744073709551600
-16
อืม ... -4 << 2
เป็น18446744073709551600
ซึ่งไม่ได้เป็นสิ่งที่คุณมีแนวโน้มที่จะคาดหวังว่าเมื่อต้องรับมือกับการคูณและการหาร ถูกต้อง แต่ไม่ใช่การคูณจำนวนเต็ม
และระวังการเพิ่มประสิทธิภาพก่อนวัยอันควร ให้คอมไพเลอร์ปรับให้เหมาะสมสำหรับคุณ - มันรู้ว่าคุณกำลังพยายามทำอะไรจริงๆและมีแนวโน้มที่จะทำงานได้ดีขึ้นโดยมีข้อบกพร่องน้อยลง