switch
คอมไพเลอร์เป็นสิ่งที่ดีจริงๆที่เพิ่มประสิทธิภาพ GCC if
ล่าสุดยังเป็นสิ่งที่ดีในการเพิ่มประสิทธิภาพพวงของเงื่อนไขในการให้
ฉันทำกรณีทดสอบบางอย่างเกี่ยวกับgodbolt
เมื่อcase
ค่าต่างๆถูกจัดกลุ่มใกล้กัน gcc, clang และ icc นั้นฉลาดพอที่จะใช้บิตแมปเพื่อตรวจสอบว่าค่าเป็นหนึ่งในค่าพิเศษหรือไม่
เช่น gcc 5.2 -O3 รวบรวมswitch
ถึง (และif
สิ่งที่คล้ายกันมาก):
errhandler_switch(errtype): # gcc 5.2 -O3
cmpl $32, %edi
ja .L5
movabsq $4301325442, %rax # highest set bit is bit 32 (the 33rd bit)
btq %rdi, %rax
jc .L10
.L5:
rep ret
.L10:
jmp fire_special_event()
ขอให้สังเกตว่าบิตแมปเป็นข้อมูลทันทีดังนั้นจึงไม่มีแคชข้อมูลที่เป็นไปได้ที่จะเข้าถึงหรือตารางกระโดด
gcc 4.9.2 -O3 คอมไพล์switch
ไปยังบิตแมป, แต่ทำได้1U<<errNumber
ด้วย mov / shift มันรวบรวมif
รุ่นเพื่อซีรีส์ของสาขา
errhandler_switch(errtype): # gcc 4.9.2 -O3
leal -1(%rdi), %ecx
cmpl $31, %ecx # cmpl $32, %edi wouldn't have to wait an extra cycle for lea's output.
# However, register read ports are limited on pre-SnB Intel
ja .L5
movl $1, %eax
salq %cl, %rax # with -march=haswell, it will use BMI's shlx to avoid moving the shift count into ecx
testl $2150662721, %eax
jne .L10
.L5:
rep ret
.L10:
jmp fire_special_event()
ให้สังเกตว่ามันลบ 1 จากerrNumber
(ด้วยlea
เพื่อรวมการดำเนินการนั้นกับการย้าย) ซึ่งช่วยให้บิตแมปเป็น 32 บิตได้ทันทีโดยหลีกเลี่ยง 64 บิตทันทีmovabsq
ซึ่งต้องใช้คำสั่งไบต์เพิ่มเติม
ลำดับที่สั้นลง (ในรหัสเครื่อง) จะเป็น:
cmpl $32, %edi
ja .L5
mov $2150662721, %eax
dec %edi # movabsq and btq is fewer instructions / fewer Intel uops, but this saves several bytes
bt %edi, %eax
jc fire_special_event
.L5:
ret
(ความล้มเหลวในการใช้jc fire_special_event
อยู่ทั่วไปทุกหนทุกแห่งและเป็นข้อผิดพลาดของคอมไพเลอร์ )
rep ret
ถูกใช้ในเป้าหมายสาขาและสาขาที่มีเงื่อนไขเพื่อประโยชน์ของ AMD K8 และ K10 เก่า (pre-Bulldozer): `rep ret` หมายถึงอะไร . ถ้าไม่มีมันการคาดการณ์ของสาขาก็ไม่สามารถทำงานได้ดีกับซีพียูที่ล้าสมัยเหล่านั้น
bt
(ทดสอบบิต) ด้วยการลงทะเบียนหาเรื่องรวดเร็ว มันรวมการทำงานของการเลื่อนซ้ายไปทีละ 1 errNumber
บิตและการทำ a test
แต่ยังคงมีความหน่วงแฝงอยู่ 1 รอบและมีเพียง Intel uop เดียว มันช้ากับ ARG หน่วยความจำเพราะความหมายของมันเกินไป CISC: ด้วยตัวถูกดำเนินการหน่วยความจำสำหรับ "บิตสตริง" ที่อยู่ของไบต์ที่จะทดสอบจะถูกคำนวณตาม ARG อื่น ๆ (หารด้วย 8) และ isn ไม่ จำกัด ก้อนที่ 1, 2, 4 หรือ 8byte ที่ชี้ไปยังตัวถูกดำเนินการโดยหน่วยความจำ
จากตารางคำสั่งของ Agner Fog คำสั่งการนับจำนวนตัวแปรจะช้ากว่าของbt
Intel ล่าสุด (2 uops แทนที่จะเป็น 1 และ shift ไม่ได้ทำทุกอย่างที่จำเป็น)