รหัสใดดีสำหรับการเพิ่มประสิทธิภาพการทำนายสาขา


10

การคาดการณ์ของสาขาและผลของการปรับแต่งคอมไพเลอร์รหัสใดมีแนวโน้มที่จะให้ประสิทธิภาพที่เหนือกว่า?

โปรดทราบว่า bRareExceptionPresent แสดงถึงเงื่อนไขที่ผิดปกติ มันไม่ใช่เส้นทางปกติของตรรกะ

/* MOST COMMON path must branch around IF clause */

bool SomeFunction(bool bRareExceptionPresent)
{
  // abort before function
  if(bRareExceptionPresent)
  {
     return false;
  }    
  .. function primary body ..    
  return true;
}

/* MOST COMMON path does NOT branch */

bool SomeFunction(bool bRareExceptionPresent)
{
  if(!bRareExceptionPresent)
  {
    .. function primary body ..
  }
  else
  {
    return false;
  }
  return true;
}

9
ฉันจะออกไปข้างนอกที่นี่และบอกว่าไม่มีความแตกต่างใด ๆ
Robert Harvey

7
สิ่งนี้อาจขึ้นอยู่กับ CPU เฉพาะที่คุณกำลังรวบรวมเนื่องจากมีสถาปัตยกรรมการวางท่อที่แตกต่างกัน (สล็อตล่าช้าและไม่มีสล็อตล่าช้า) เวลาที่คุณใช้คิดเกี่ยวกับสิ่งนี้น่าจะมากกว่าเวลาที่บันทึกไว้เมื่อใช้ - โปรไฟล์ก่อนจากนั้นจึงปรับให้เหมาะสม

2
มันเกือบจะแน่นอนการเพิ่มประสิทธิภาพขนาดเล็กก่อนวัยอันควร
Robert Harvey

2
@MichaelT อ๋อการทำโปรไฟล์เป็นวิธีเดียวที่เชื่อถือได้ในการทราบว่าเกิดอะไรขึ้นกับประสิทธิภาพของโค้ดบนเป้าหมายแพลตฟอร์มในบริบทของมัน อย่างไรก็ตามฉันสงสัยว่าเป็นที่ต้องการโดยทั่วไปหรือไม่
dyasta

1
@RobertHarvey: เป็นการเพิ่มประสิทธิภาพไมโครก่อนกำหนดยกเว้นในกรณีที่ตรงตามเงื่อนไขทั้งสอง : (1) การวนซ้ำเรียกว่าการพันล้านครั้ง (ไม่ใช่ล้าน) และ (2) แดกดันเมื่อลำตัวเล็ก ๆ ในแง่ของรหัสเครื่อง เงื่อนไข # 2 หมายความว่าสัดส่วนของเวลาที่ใช้ในค่าโสหุ้ยไม่น้อยเมื่อเทียบกับเวลาที่ใช้ในงานที่มีประโยชน์ ข่าวดีก็คือโดยปกติแล้วในสถานการณ์เช่นนี้ซึ่งทั้งสองเงื่อนไขตรงตามความต้องการ SIMD (vectorization) ซึ่งโดยธรรมชาติไม่มีสาขาจะแก้ปัญหาประสิทธิภาพการทำงานทั้งหมด

คำตอบ:


10

ในโลกทุกวันนี้มันไม่สำคัญมากถ้าเป็นเช่นนั้น

การคาดคะเนสาขาแบบไดนามิก (สิ่งที่คิดมานานหลายทศวรรษ (ดูการวิเคราะห์แบบแผนการทำนายสาขาแบบไดนามิกในปริมาณงานของระบบที่เผยแพร่ในปี 1996) เป็นเรื่องธรรมดา

ตัวอย่างนี้สามารถพบได้ในหน่วยประมวลผล ARM จาก Arm Info Center เกี่ยวกับการทำนายสาขา

เพื่อปรับปรุงความแม่นยำในการทำนายสาขาการรวมกันของเทคนิคแบบคงที่และแบบไดนามิกจะใช้

คำถามคือ "การคาดคะเนสาขาแบบไดนามิกคืออะไรในโปรเซสเซอร์แขน?" การอ่านอย่างต่อเนื่องของการทำนายสาขาแบบไดนามิกแสดงให้เห็นว่ามันใช้รูปแบบการทำนาย 2 บิต (อธิบายไว้ในกระดาษ) สร้างข้อมูลเกี่ยวกับว่าสาขานั้นถูกยึดอย่างแรงหรืออ่อนแอหรือไม่

เมื่อเวลาผ่านไป (และตามเวลาที่ฉันหมายถึงไม่กี่ผ่านบล็อกที่) นี้สร้างขึ้นข้อมูลเป็นวิธีที่รหัสจะไป

สำหรับการคาดคะเนแบบคงที่มันจะดูวิธีที่รหัสดูตัวเองและวิธีที่สาขาทำในการทดสอบ - ไปยังคำสั่งก่อนหน้าหรืออีกหนึ่งในรหัส:

รูปแบบที่ใช้ในหน่วยประมวลผล ARM1136JF-S คาดการณ์ว่าสาขาที่มีเงื่อนไขไปข้างหน้าทั้งหมดจะไม่ได้รับการดำเนินการและสาขาที่ย้อนหลังทั้งหมดจะถูกนำมาใช้ ประมาณ 65% ของสาขาทั้งหมดจะถูกนำหน้าด้วยรอบที่ไม่ใช่สาขาเพียงพอที่จะคาดการณ์ได้อย่างสมบูรณ์

ดังที่ได้กล่าวไว้โดย Sparky สิ่งนี้มีพื้นฐานอยู่บนความเข้าใจที่วนซ้ำบ่อยกว่า ห่วงแยกไปข้างหลัง (มีกิ่งที่ปลายห่วงเพื่อรีสตาร์ทที่ด้านบน) - ปกติแล้วจะทำเช่นนี้

อันตรายจากการพยายามเดาตัวคอมไพเลอร์ครั้งที่สองคือคุณไม่รู้ว่ารหัสนั้นจะถูกรวบรวมได้อย่างไร (และปรับให้เหมาะสม) และส่วนใหญ่มันไม่สำคัญ ด้วยการทำนายแบบไดนามิกสองครั้งผ่านฟังก์ชั่นมันจะทำนายการข้ามคำสั่งการป้องกันเพื่อให้ได้ผลตอบแทนก่อนกำหนด หากประสิทธิภาพของท่อส่งน้ำสองท่อมีประสิทธิภาพที่สำคัญมีสิ่งอื่นที่คุณต้องกังวล

เวลาที่ใช้ในการอ่านสไตล์หนึ่งในอีกลักษณะหนึ่งนั้นมีความสำคัญมากกว่า - การทำให้โค้ดสะอาดเพื่อให้มนุษย์สามารถอ่านได้เพราะคอมไพเลอร์กำลังจะทำอะไรได้ดีไม่ว่าคุณจะยุ่งหรือเป็นอุดมคติในการเขียนโค้ด


7
คำถามที่ StackOverflow ที่มีชื่อเสียงแสดงให้เห็นว่าการทำนายสาขาไม่ว่าแม้วันนี้
Florian Margaine

3
@ FlorianMargaine ในขณะที่มันเป็นเรื่องสำคัญมันเริ่มตกอยู่ในสถานการณ์ที่มันไม่สำคัญว่าจะต้องมีความเข้าใจในสิ่งที่คุณกำลังรวบรวมและวิธีการทำงาน (arm vs x86 vs mips ... ) การเขียนรหัสที่พยายามใช้การเพิ่มประสิทธิภาพขนาดเล็กเมื่อเริ่มต้นนั้นมีแนวโน้มที่จะทำงานจากสถานที่ที่ผิดพลาดและไม่ได้ผลตามที่ต้องการ

แน่นอนว่าอย่าอ้าง DK แต่ฉันคิดว่าคำถามนี้ชัดเจนในแง่ของการปรับให้เหมาะสมเมื่อคุณผ่านขั้นตอนการทำโปรไฟล์ไปแล้ว :-)
Florian Margaine

2
@MichaelT คำตอบที่ดีและฉันเห็นด้วยอย่างมากกับข้อสรุปของคุณ การเพิ่มประสิทธิภาพ pre-profiling / นามธรรมชนิดนี้สามารถตอบโต้ได้อย่างแน่นอน มันจบลงด้วยการเป็นเกมที่คาดเดาทำให้คนหนึ่งตัดสินใจออกแบบด้วยเหตุผลที่ไม่สมเหตุสมผล ถึงกระนั้นฉันก็พบว่าตัวเองอยากรู้อยากเห็น o
dyasta


9

ความเข้าใจของฉันคือว่าครั้งแรกที่ CPU พบสาขามันจะทำนาย (ถ้าสนับสนุน) ว่าสาขาไปข้างหน้าจะไม่ได้รับสาขาและสาขาหลังเป็น เหตุผลสำหรับเรื่องนี้คือการวนรอบ (ซึ่งโดยทั่วไปแล้วจะแตกกิ่งก้านสาขา) จะถือว่าเป็นห่วง

ในโปรเซสเซอร์บางตัวคุณสามารถให้คำแนะนำในคำสั่งการประกอบว่าเส้นทางใดมีแนวโน้มมากขึ้น รายละเอียดของการหลบหนีฉันในขณะนี้

นอกจากนี้คอมไพเลอร์ C บางตัวยังสนับสนุนการทำนายสาขาแบบสแตติกเพื่อให้คุณสามารถบอกคอมไพเลอร์ว่ามีแนวโน้มว่าสาขาใด ในทางกลับกันมันอาจจัดระเบียบรหัสที่สร้างขึ้นใหม่หรือใช้คำแนะนำที่ปรับเปลี่ยนเพื่อใช้ประโยชน์จากข้อมูลนี้ (หรือแม้แต่ไม่สนใจมัน)

__builtin_expect((long)!!(x), 1L)  /* GNU C to indicate that <x> will likely be TRUE */
__builtin_expect((long)!!(x), 0L)  /* GNU C to indicate that <x> will likely be FALSE */

หวังว่านี่จะช่วยได้


3
"ความเข้าใจของฉันคือว่าครั้งแรกที่ CPU พบสาขามันจะทำนาย (ถ้าสนับสนุน) ว่าสาขาไปข้างหน้าจะไม่ได้รับสาขาและสาขาหลังเป็น" นี่เป็นความคิดที่น่าสนใจมาก คุณมีหลักฐานว่าสิ่งนี้ถูกนำไปใช้จริงในสถาปัตยกรรมทั่วไปหรือไม่?
blubb

5
ตรงจากปากม้า: กิ่งก้านไปข้างหน้าเป็นค่าเริ่มต้นที่ไม่ถ่าย ค่าเริ่มต้นสาขาย้อนกลับไปดำเนินการ และจากหน้าเดียวกัน: "คำนำหน้า 0x3E - ทำนายสาขาแบบคงที่"
MSalters

มี pragma แพลตฟอร์มที่ไม่เชื่อเรื่องพระเจ้าที่เท่าเทียมกับ__builtin_expect?
MarcusJ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.