จริง ๆ แล้วมันเร็วกว่าที่จะใช้พูด (i << 3) + (i << 1) เพื่อคูณด้วย 10 แทนที่จะใช้ i * 10 โดยตรงหรือไม่
อาจเป็นหรือไม่ได้อยู่ในเครื่องของคุณ - ถ้าคุณสนใจให้วัดการใช้งานจริงของคุณ
กรณีศึกษา - จาก 486 ถึง Core i7
การเปรียบเทียบเป็นเรื่องยากมากที่จะทำอย่างมีความหมาย แต่เราสามารถดูข้อเท็จจริงบางอย่างได้ จากhttp://www.penguin.cz/~literakl/intel/s.html#SALและ http://www.penguin.cz/~literakl/intel/i.html#IMULเราทราบถึงวงจรนาฬิกา x86 จำเป็นสำหรับการเปลี่ยนแปลงทางคณิตศาสตร์และการคูณ สมมติว่าเรายึดติดกับ "486" (รายการใหม่ล่าสุดที่จดทะเบียน), 32 บิตการลงทะเบียนและทันที IMUL ใช้เวลา 13-42 รอบและ IDIV 44 SAL แต่ละอันใช้เวลา 2 และเพิ่ม 1 ดังนั้นแม้จะมีไม่กี่คน เหมือนผู้ชนะ
วันนี้ด้วย Core i7:
(จากhttp://software.intel.com/en-us/forums/showthread.php?t=61481 )
แฝงเป็น1 รอบสำหรับนอกจากจำนวนเต็มและ 3 รอบสำหรับการคูณจำนวนเต็ม คุณสามารถค้นหาศักยภาพและ thoughput ในภาคผนวก C ของ "Intel® 64 และ IA-32 สถาปัตยกรรมการเพิ่มประสิทธิภาพ Reference Manual" ซึ่งตั้งอยู่บนhttp://www.intel.com/products/processor/manuals/
(จากบาง Intel แจ้งแจ้งความ)
การใช้ SSE นั้น Core i7 สามารถออกคำสั่งการเพิ่มและทวีคูณพร้อมกันส่งผลให้อัตราการดำเนินการจุดลอยตัว (FLOP) สูงสุด 8 ครั้งต่อรอบนาฬิกา
ที่ให้คุณทราบว่าสิ่งต่าง ๆ มาไกลแค่ไหน เรื่องเล็กน้อยเรื่องการปรับให้เหมาะสม - เช่นการเปลี่ยนบิตเมื่อเทียบกับ*
- ซึ่งได้รับการดำเนินการอย่างจริงจังแม้กระทั่งใน 90s นั้นล้าสมัยไปแล้วในตอนนี้ การเลื่อนบิตยังเร็วกว่า แต่สำหรับการเปลี่ยนแปลงที่ไม่ใช่พลังของสอง / เวลาตามเวลาที่คุณทำกะทั้งหมดและเพิ่มผลลัพธ์มันช้าลงอีกครั้ง จากนั้นคำแนะนำเพิ่มเติมหมายถึงข้อผิดพลาดแคชเพิ่มเติมปัญหาที่อาจเกิดขึ้นในการวางท่อการลงทะเบียนชั่วคราวมากขึ้นอาจหมายถึงการประหยัดและการคืนค่าเนื้อหาการลงทะเบียนจากสแต็กมากขึ้น ... มันซับซ้อนเกินไปอย่างรวดเร็วในการประเมินผลกระทบทั้งหมด ลบส่วนใหญ่
การทำงานในซอร์สโค้ดเทียบกับการนำไปใช้
โดยทั่วไปคำถามของคุณจะถูกแท็ก C และ C ++ ในฐานะภาษารุ่นที่ 3 พวกเขาได้รับการออกแบบมาโดยเฉพาะเพื่อซ่อนรายละเอียดของชุดคำสั่ง CPU พื้นฐาน เพื่อตอบสนองความมาตรฐานภาษาของพวกเขาพวกเขาจะต้องสนับสนุนการคูณและการดำเนินงานขยับ (และอื่น ๆ อีกมากมาย) แม้ว่าฮาร์ดแวร์พื้นฐานไม่ได้ ในกรณีเช่นนี้พวกเขาจะต้องสังเคราะห์ผลลัพธ์ที่ต้องการโดยใช้คำแนะนำอื่น ๆ อีกมากมาย ในทำนองเดียวกันพวกเขาจะต้องให้การสนับสนุนซอฟต์แวร์สำหรับการดำเนินการจุดลอยหาก CPU ขาดและไม่มี FPU CPU สมัยใหม่รองรับ*
และ<<
ดังนั้นสิ่งนี้อาจดูเหมือนเป็นเรื่องเหลวไหลทั้งทางทฤษฎีและประวัติศาสตร์ แต่สิ่งสำคัญคือเสรีภาพในการเลือกใช้งานนั้นมีสองวิธี: แม้ว่า CPU จะมีคำสั่งที่ใช้การดำเนินการที่ร้องขอในซอร์สโค้ดในกรณีทั่วไปคอมไพเลอร์ไม่มีค่าใช้จ่าย เลือกอย่างอื่นที่ชอบเพราะดีกว่าสำหรับกรณีเฉพาะที่คอมไพเลอร์ต้องเผชิญ
ตัวอย่าง (ด้วยภาษาประกอบสมมุติ)
source literal approach optimised approach
#define N 0
int x; .word x xor registerA, registerA
x *= N; move x -> registerA
move x -> registerB
A = B * immediate(0)
store registerA -> x
...............do something more with x...............
คำแนะนำอย่างเอกสิทธิ์เฉพาะบุคคลหรือ ( xor
) ไม่มีความสัมพันธ์กับซอร์สโค้ด แต่ xor-ing อะไรที่ตัวเองล้างบิตทั้งหมดดังนั้นจึงสามารถใช้เพื่อตั้งค่าบางอย่างเป็น 0 ซอร์สโค้ดที่บอกถึงที่อยู่หน่วยความจำอาจไม่เกี่ยวข้องกับการใช้งาน
แฮ็กชนิดนี้ใช้งานได้นานเท่าที่คอมพิวเตอร์ใช้งาน ในวันแรก ๆ ของ 3GLs เพื่อความปลอดภัยของนักพัฒนาในการทำความเข้าใจเอาท์พุทคอมไพเลอร์ต้องตอบสนองการพัฒนาภาษาแอสเซมบลีที่ใช้งานง่ายด้วยมือที่มีอยู่เดิม ชุมชนที่รหัสที่ผลิตไม่ได้ช้ากว่า verbose มากขึ้นหรือแย่ลง คอมไพเลอร์ปรับใช้การเพิ่มประสิทธิภาพที่ยอดเยี่ยมอย่างรวดเร็ว - พวกเขากลายเป็นศูนย์กลางการจัดเก็บที่ดีกว่าโปรแกรมเมอร์ภาษาแอสเซมบลีแต่ละตัวที่อาจเป็นไปได้แม้ว่าจะมีโอกาสที่พวกเขาจะพลาดการปรับแต่งเฉพาะที่เกิดขึ้น ดึงออกมาแล้วคว้านหาอะไรที่ดีกว่าในขณะที่คอมไพเลอร์ทำตามที่พวกเขาได้รับการบอกกล่าวจนกว่าจะมีคนดึงข้อมูลที่ได้รับประสบการณ์กลับคืนมา
ดังนั้นแม้ว่าการขยับและการเพิ่มยังเร็วกว่าสำหรับฮาร์ดแวร์บางตัวนักเขียนคอมไพเลอร์ก็มีแนวโน้มที่จะทำงานได้อย่างถูกต้องเมื่อมันปลอดภัยและเป็นประโยชน์
การบำรุงรักษา
หากการเปลี่ยนแปลงฮาร์ดแวร์ของคุณคุณสามารถคอมไพล์ใหม่และมันจะดู CPU เป้าหมายและเป็นทางเลือกที่ดีที่สุดในขณะที่คุณไม่ต้องการที่จะกลับมา "optimisations" ของคุณหรือรายการที่สภาพแวดล้อมการรวบรวมควรใช้การคูณและควรเปลี่ยน ลองนึกถึง "การเพิ่มประสิทธิภาพ" ที่ไม่ได้ใช้กำลังของสองบิตที่ได้รับการเขียนเมื่อ 10+ ปีที่แล้วซึ่งตอนนี้กำลังทำให้โค้ดช้าลงเพราะมันทำงานบนโปรเซสเซอร์ที่ทันสมัย ... !
โชคดีที่คอมไพเลอร์ที่ดีเช่น GCC สามารถแทนที่ชุดของบิตกะและเลขคณิตด้วยการคูณโดยตรงเมื่อเปิดใช้งานการเพิ่มประสิทธิภาพ (เช่น...main(...) { return (argc << 4) + (argc << 2) + argc; }
-> imull $21, 8(%ebp), %eax
) ดังนั้นการคอมไพล์ใหม่อาจช่วยได้โดยไม่ต้องแก้ไขโค้ด แต่ก็ไม่รับประกัน
รหัสบิตการเปลี่ยนรหัสที่ใช้การคูณหรือการแบ่งเป็นสิ่งที่แสดงให้เห็นถึงสิ่งที่คุณพยายามทำสำเร็จดังนั้นนักพัฒนาคนอื่น ๆ จะสับสนและนักเขียนโปรแกรมที่สับสนมักจะแนะนำบั๊กหรือกำจัดสิ่งที่จำเป็นในการฟื้นฟูสติ หากคุณเพียง แต่ทำสิ่งที่ไม่ชัดเจนเมื่อพวกมันมีประโยชน์เป็นรูปธรรมจริงๆและจากนั้นให้บันทึกไว้อย่างดี (แต่ไม่ต้องบันทึกสิ่งอื่นที่ใช้งานง่าย) ทุกคนจะมีความสุข
โซลูชันทั่วไปกับโซลูชันบางส่วน
หากคุณมีความรู้พิเศษเช่นคุณint
จริงๆจะได้รับการจัดเก็บค่าx
, y
และz
แล้วคุณอาจจะไม่สามารถที่จะทำงานออกคำแนะนำบางอย่างที่ทำงานสำหรับค่าเหล่านั้นและคุณได้รับผลของคุณมากขึ้นอย่างรวดเร็วกว่าเมื่อคอมไพเลอร์ไม่ได้มี ข้อมูลเชิงลึกนั้นและจำเป็นต้องมีการใช้งานที่ทำงานได้กับint
ค่าทั้งหมด ตัวอย่างเช่นพิจารณาคำถามของคุณ:
การคูณและการหารสามารถทำได้โดยใช้ตัวดำเนินการบิต ...
คุณแสดงการคูณ แต่วิธีการหาร?
int x;
x >> 1; // divide by 2?
ตามมาตรฐาน C ++ 5.8:
-3- ค่าของ E1 >> E2 คือตำแหน่ง E1 ที่เลื่อนไปทางขวา E1 หาก E1 มีประเภทที่ไม่ได้ลงนามหรือถ้า E1 มีประเภทที่ลงนามและค่าที่ไม่ใช่ค่าลบค่าของผลลัพธ์จะเป็นส่วนที่สำคัญของความฉลาดทาง E1 หารด้วยปริมาณ 2 ยกกำลัง E2 หาก E1 มีประเภทที่ลงนามและค่าลบค่าผลลัพธ์จะถูกกำหนดโดยการนำไปปฏิบัติ
ดังนั้นการเปลี่ยนบิตของคุณจะมีผลการดำเนินการตามที่กำหนดเมื่อx
เป็นลบ: มันอาจไม่ทำงานในลักษณะเดียวกันกับเครื่องที่แตกต่าง แต่/
คาดการณ์ได้ดีกว่ามาก (มันอาจจะไม่สอดคล้องกันอย่างสมบูรณ์เช่นกันเนื่องจากเครื่องจักรต่าง ๆ อาจมีตัวเลขแทนจำนวนต่างกันและช่วงที่แตกต่างกันแม้ว่าจะมีบิตจำนวนเท่ากันในการเป็นตัวแทน)
คุณอาจพูดว่า "ฉันไม่สนใจ ... นั่นint
คือการเก็บอายุของพนักงานมันจะไม่เป็นลบ" หากคุณมีข้อมูลเชิงลึกพิเศษเช่นนั้นใช่ - >>
การเพิ่มประสิทธิภาพที่ปลอดภัยของคุณอาจถูกส่งผ่านโดยคอมไพเลอร์เว้นแต่คุณจะทำอย่างชัดเจนในรหัสของคุณ แต่มันมีความเสี่ยงและไม่ค่อยมีประโยชน์เท่าที่คุณจะไม่มีความเข้าใจเช่นนี้และโปรแกรมเมอร์คนอื่น ๆ ที่ทำงานด้วยรหัสเดียวกันจะไม่ทราบว่าคุณวางเดิมพันบนบ้านด้วยความคาดหวังที่ผิดปกติของข้อมูลที่คุณ ' จะจัดการ ... สิ่งที่ดูเหมือนว่าการเปลี่ยนแปลงที่ปลอดภัยอย่างสมบูรณ์สำหรับพวกเขาอาจย้อนกลับมาได้เนื่องจาก "การเพิ่มประสิทธิภาพ" ของคุณ
มีอินพุตอะไรบ้างที่ไม่สามารถคูณหรือหารด้วยวิธีนี้ได้?
ใช่ ... ดังที่ได้กล่าวไปแล้วจำนวนลบมีพฤติกรรมการใช้งานที่กำหนดไว้เมื่อ "หาร" โดยการเลื่อนบิต