มีคำใบ้คอมไพเลอร์สำหรับ GCC เพื่อบังคับให้การทำนายสาขาเป็นไปในทางที่แน่นอนหรือไม่?


118

สำหรับสถาปัตยกรรม Intel มีวิธีใดที่จะสั่งให้คอมไพเลอร์ GCC สร้างโค้ดที่บังคับให้การทำนายสาขาเป็นวิธีเฉพาะในโค้ดของฉันเสมอ ฮาร์ดแวร์ของ Intel รองรับสิ่งนี้หรือไม่ แล้วคอมไพเลอร์หรือฮาร์ดแวร์อื่น ๆ ล่ะ?

ฉันจะใช้สิ่งนี้ในรหัส C ++ ซึ่งฉันรู้ว่ากรณีที่ฉันต้องการทำงานอย่างรวดเร็วและไม่สนใจเกี่ยวกับการชะลอตัวเมื่อสาขาอื่นต้องดำเนินการแม้ว่าสาขานั้นจะเพิ่งเข้ามาเมื่อไม่นานมานี้

for (;;) {
  if (normal) { // How to tell compiler to always branch predict true value?
    doSomethingNormal();
  } else {
    exceptionalCase();
  }
}

ตามคำถามสำหรับ Evdzhan Mustafa คำใบ้สามารถระบุคำใบ้เป็นครั้งแรกที่โปรเซสเซอร์พบคำสั่งการทำนายสาขาที่ตามมาทั้งหมดทำงานได้ตามปกติหรือไม่


นอกจากนี้ยังอาจทำให้เกิดข้อยกเว้นหากมีสิ่งใดผิดปกติ (ซึ่งเป็นคอมไพเลอร์อิสระ)
Shep

คำตอบ:


9

ใน C ++ 20 แอตทริบิวต์ที่เป็นไปได้และไม่น่าเป็นไปได้ควรเป็นมาตรฐานและได้รับการสนับสนุนใน g ++ 9แล้ว ดังที่กล่าวไว้ที่นี่คุณสามารถเขียนได้

if (a>b) {
  /* code you expect to run often */
  [[likely]] /* last statement */
}

เช่นในรหัสต่อไปนี้บล็อกอื่นจะได้รับการอินไลน์เนื่องจาก[[unlikely]]ในบล็อก if

int oftendone( int a, int b );
int rarelydone( int a, int b );
int finaltrafo( int );

int divides( int number, int prime ) {
  int almostreturnvalue;
  if ( ( number % prime ) == 0 ) {
    auto k                         = rarelydone( number, prime );
    auto l                         = rarelydone( number, k );
    [[unlikely]] almostreturnvalue = rarelydone( k, l );
  } else {
    auto a            = oftendone( number, prime );
    almostreturnvalue = oftendone( a, a );
  }
  return finaltrafo( almostreturnvalue );
}

ลิงค์ godbolt เปรียบเทียบการมี / ไม่มีของแอตทริบิวต์


ทำไมต้องใช้[[unlikely]]ในifvs [[likely]]ในelse?
WilliamKF

ไม่มีเหตุผลเพิ่งมาอยู่ในกลุ่มดาวนี้หลังจากลองดูว่าแอตทริบิวต์ต้องไปที่ใด
pseyfert

ค่อนข้างเย็น น่าเสียดายที่วิธีนี้ใช้ไม่ได้กับ C ++ เวอร์ชันเก่า
Maxim Egorushkin

ลิงค์ Godbolt ที่ยอดเยี่ยม
Lewis Kelsey

87

GCC สนับสนุนฟังก์ชัน__builtin_expect(long exp, long c)เพื่อให้คุณลักษณะประเภทนี้ คุณสามารถตรวจสอบเอกสารที่นี่

expเงื่อนไขที่ใช้อยู่ที่ไหนและcเป็นค่าที่คาดหวัง ตัวอย่างเช่นในกรณีที่คุณต้องการ

if (__builtin_expect(normal, 1))

เนื่องจากไวยากรณ์ที่น่าอึดอัดจึงมักใช้โดยการกำหนดมาโครที่กำหนดเองสองแบบเช่น

#define likely(x)    __builtin_expect (!!(x), 1)
#define unlikely(x)  __builtin_expect (!!(x), 0)

เพียงเพื่อให้งานง่ายขึ้น

โปรดทราบว่า:

  1. นี่ไม่ใช่มาตรฐาน
  2. ตัวทำนายสาขาคอมไพเลอร์ / ซีพียูมีแนวโน้มที่จะมีความชำนาญมากกว่าคุณในการตัดสินใจเรื่องดังกล่าวดังนั้นนี่อาจเป็นการเพิ่มประสิทธิภาพไมโครก่อนกำหนด

3
มีเหตุผลไหมที่คุณแสดงมาโครไม่ใช่constexprฟังก์ชัน
Columbo

22
@ Columbo: ฉันไม่คิดว่าconstexprฟังก์ชันสามารถแทนที่มาโครนี้ได้ มันต้องอยู่ในifแถลงการณ์โดยตรงที่ฉันเชื่อ เหตุผลเดียวกันassertไม่สามารถเป็นconstexprฟังก์ชันได้
หมูปิ้ง

1
@MooingDuck ผมเห็นด้วยแม้ว่าจะมีเหตุผลมากขึ้นสำหรับการยืนยัน
Shafik Yaghmour

7
@Columbo เหตุผลหนึ่งที่ต้องใช้มาโครอาจเป็นเพราะนี่เป็นหนึ่งในไม่กี่แห่งใน C หรือ C ++ ที่มาโครมีความถูกต้องมากกว่าฟังก์ชัน ฟังก์ชั่นจะปรากฏเฉพาะในการทำงานเนื่องจากการเพิ่มประสิทธิภาพ (มันคือเพิ่มประสิทธิภาพ: constexprเพียงพูดคุยเกี่ยวกับความหมายค่าไม่ inlining ของการดำเนินการเฉพาะการชุมนุม); การตีความรหัสอย่างตรงไปตรงมา (ไม่มีอินไลน์) ไม่มีความหมาย ไม่มีเหตุผลเลยที่จะใช้ฟังก์ชันนี้
Leushenko

2
@Leushenko พิจารณาว่า__builtin_expectตัวเองเป็นคำใบ้ในการเพิ่มประสิทธิภาพดังนั้นการเถียงว่าวิธีที่ทำให้การใช้งานง่ายขึ้นนั้นขึ้นอยู่กับการปรับให้เหมาะสมคือ ... ไม่น่าเชื่อ นอกจากนี้ฉันไม่ได้เพิ่มตัวconstexprระบุเพื่อให้ใช้งานได้ตั้งแต่แรก แต่เพื่อให้ทำงานในนิพจน์คงที่ และใช่มีเหตุผลในการใช้ฟังก์ชัน ตัวอย่างเช่นฉันไม่ต้องการสร้างมลพิษให้เนมสเปซทั้งหมดของฉันด้วยชื่อเล็ก ๆ น่ารัก ๆ เช่นlikely. ฉันต้องใช้เช่นLIKELYเพื่อเน้นว่ามันเป็นมาโครและหลีกเลี่ยงการชนกัน แต่มันก็น่าเกลียด
Columbo

46

gcc มี__builtin_expect ยาว (long exp, long c) ( เน้นของฉัน ):

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

ค่าที่ส่งคืนคือค่าของ exp ซึ่งควรเป็นนิพจน์อินทิกรัล ความหมายของบิวท์อินนั้นคาดว่า exp == c. ตัวอย่างเช่น:

if (__builtin_expect (x, 0))
   foo ();

บ่งชี้ว่าเราไม่คาดหวังว่าจะเรียก foo เนื่องจากเราคาดว่า x จะเป็นศูนย์ เนื่องจากคุณถูก จำกัด เฉพาะนิพจน์อินทิกรัลสำหรับ exp คุณควรใช้โครงสร้างเช่น

if (__builtin_expect (ptr != NULL, 1))
   foo (*ptr);

เมื่อทดสอบตัวชี้หรือค่าทศนิยม

เป็นบันทึกเอกสารที่คุณควรจะชอบที่จะใช้ข้อเสนอแนะรายละเอียดที่เกิดขึ้นจริงและบทความนี้แสดงให้เห็นตัวอย่างการปฏิบัตินี้__builtin_expectและวิธีการในกรณีของพวกเขาที่ปลายน้อยลงด้วยการปรับตัวดีขึ้นมากกว่าการใช้ ดูวิธีใช้การเพิ่มประสิทธิภาพที่แนะนำโปรไฟล์ใน g ++ ได้อย่างไร .

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

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

สังเกตว่า!!ใช้ในมาโครเราสามารถหาคำอธิบายได้ในWhy use !! (condition) แทน (condition)? .

เพียงเพราะเทคนิคนี้ใช้ในเคอร์เนลของ Linux ไม่ได้หมายความว่าจะใช้มันเสมอไป เราสามารถเห็นได้จากคำถามนี้ฉันเพิ่งตอบความแตกต่างระหว่างประสิทธิภาพของฟังก์ชันเมื่อส่งผ่านพารามิเตอร์เป็นค่าคงที่เวลาคอมไพล์หรือตัวแปรที่เทคนิคการเพิ่มประสิทธิภาพแบบรีดด้วยมือจำนวนมากไม่ได้ผลในกรณีทั่วไป เราจำเป็นต้องสร้างรหัสโปรไฟล์อย่างรอบคอบเพื่อให้เข้าใจว่าเทคนิคมีประสิทธิภาพหรือไม่ เทคนิคเก่า ๆ หลายอย่างอาจไม่เกี่ยวข้องกับการเพิ่มประสิทธิภาพคอมไพเลอร์สมัยใหม่ด้วยซ้ำ

หมายเหตุแม้ว่า builtins ไม่ได้พกพาเสียงดังกราวยังสนับสนุน __builtin_expect

นอกจากนี้ในบางสถาปัตยกรรมมันอาจจะไม่สร้างความแตกต่าง


อะไรที่ดีพอสำหรับเคอร์เนล Linux ไม่เพียงพอสำหรับ C ++ 11
Maxim Egorushkin

@MaximEgorushkin หมายเหตุฉันไม่แนะนำให้ใช้จริง ๆ ในความเป็นจริงเอกสาร gcc ที่ฉันอ้างถึงซึ่งเป็นการอ้างอิงครั้งแรกของฉันไม่ได้ใช้เทคนิคนั้นด้วยซ้ำ ฉันจะบอกว่าแรงผลักดันหลักของคำตอบของฉันคือการพิจารณาทางเลือกอื่นอย่างรอบคอบก่อนที่จะไปตามเส้นทางนี้
Shafik Yaghmour

44

ไม่มีไม่มี (อย่างน้อยก็ในโปรเซสเซอร์ x86 ที่ทันสมัย)

__builtin_expectที่กล่าวถึงในคำตอบอื่น ๆ มีผลต่อวิธีที่ gcc จัดเรียงรหัสแอสเซมบลี ไม่มีผลโดยตรงต่อตัวทำนายสาขาของ CPU แน่นอนว่าจะมีผลทางอ้อมต่อการทำนายสาขาที่เกิดจากการเรียงลำดับรหัสใหม่ แต่ในโปรเซสเซอร์ x86 สมัยใหม่ไม่มีคำสั่งใดที่บอกให้ CPU "สมมติว่าสาขานี้เป็น / ไม่ได้ใช้"

ดูคำถามนี้สำหรับรายละเอียดเพิ่มเติม: Intel x86 0x2E / 0x3E Prefix Branch Prediction ใช้จริงหรือไม่?

เพื่อความชัดเจน__builtin_expectและ / หรือการใช้-fprofile-arcs สามารถปรับปรุงประสิทธิภาพของโค้ดของคุณได้ทั้งโดยการให้คำแนะนำกับตัวทำนายสาขาผ่านเค้าโครงโค้ด (ดูการเพิ่มประสิทธิภาพการทำงานของแอสเซมบลี x86-64 - การจัดแนวและการคาดคะเนสาขา ) และการปรับปรุงลักษณะการทำงานของแคช โดยรักษารหัสที่ "ไม่น่าจะเป็นไปได้" ให้ห่างจากโค้ด "น่าจะเป็น"


9
สิ่งนี้ไม่ถูกต้อง ในเวอร์ชันใหม่ทั้งหมดของ x86 อัลกอริธึมการคาดคะเนเริ่มต้นคือการทำนายว่าสาขาข้างหน้าจะไม่ถูกนำมาใช้และสาขาย้อนหลังคือ (ดูsoftware.intel.com/en-us/articles/… ) ดังนั้นการจัดเรียงโค้ดของคุณใหม่คุณสามารถให้คำใบ้กับ CPU ได้อย่างมีประสิทธิภาพ ตรงนี้เป็นสิ่ง GCC __builtin_expectไม่เมื่อคุณใช้
Nemo

6
@ นีโมคุณอ่านประโยคแรกของคำตอบของฉันผ่านไปแล้วหรือยัง? ทุกสิ่งที่คุณพูดครอบคลุมโดยคำตอบของฉันหรือในลิงค์ที่ให้มา คำถามถามว่าคุณสามารถ "บังคับให้การทำนายสาขาเป็นไปในทางใดทางหนึ่งเสมอ" ซึ่งคำตอบคือ "ไม่" และฉันไม่รู้สึกว่าคำตอบอื่น ๆ ชัดเจนเพียงพอเกี่ยวกับเรื่องนี้
Artelius

4
โอเคฉันควรอ่านอย่างละเอียดมากกว่านี้ มันดูเหมือนว่าฉันคำตอบนี้เป็นเทคนิคที่ถูกต้อง __builtin_expectแต่ไร้ประโยชน์เนื่องจากผู้ถามจะเห็นได้ชัดมองหา ดังนั้นนี่ควรเป็นเพียงความคิดเห็น แต่ก็ไม่เป็นเท็จดังนั้นฉันจึงลบการโหวตลงคะแนน
Nemo

IMO ไม่ใช่เรื่องไร้ประโยชน์ เป็นการชี้แจงที่เป็นประโยชน์ว่าซีพียูและคอมไพเลอร์ทำงานอย่างไรซึ่งอาจเกี่ยวข้องกับการวิเคราะห์ประสิทธิภาพโดยมี / ไม่มีตัวเลือกเหล่านี้ เช่นคุณมักไม่สามารถใช้__builtin_expectสร้างกรณีทดสอบเล็กน้อยที่คุณสามารถวัดได้perf statซึ่งจะมีอัตราการทำนายผิดสาขาที่สูงมาก มันก็ส่งผลกระทบต่อสาขารูปแบบ และ BTW, Intel ตั้งแต่ Sandybridge หรืออย่างน้อย Haswell ก็ไม่ได้ใช้การทำนายแบบคงที่มากนัก BHT มีการคาดคะเนอยู่เสมอไม่ว่าจะเป็นนามแฝงเก่าหรือไม่ก็ตาม xania.org/201602/bpu-part-two
Peter Cordes

24

วิธีที่ถูกต้องในการกำหนดมาโครที่เป็นไปได้ / ไม่น่าเป็นไปได้ใน C ++ 11 มีดังต่อไปนี้:

#define LIKELY(condition) __builtin_expect(static_cast<bool>(condition), 1)
#define UNLIKELY(condition) __builtin_expect(static_cast<bool>(condition), 0)

วิธีนี้เป็นวิธีที่เข้ากันได้กับทุกรุ่น C ++ ซึ่งแตกต่างจากแต่อาศัยส่วนขยายที่ไม่ได้มาตรฐาน[[likely]]__builtin_expect


เมื่อมาโครเหล่านี้กำหนดด้วยวิธีนี้:

#define LIKELY(condition) __builtin_expect(!!(condition), 1)

นั่นอาจเปลี่ยนความหมายของifคำสั่งและทำลายรหัส พิจารณารหัสต่อไปนี้:

#include <iostream>

struct A
{
    explicit operator bool() const { return true; }
    operator int() const { return 0; }
};

#define LIKELY(condition) __builtin_expect((condition), 1)

int main() {
    A a;
    if(a)
        std::cout << "if(a) is true\n";
    if(LIKELY(a))
        std::cout << "if(LIKELY(a)) is true\n";
    else
        std::cout << "if(LIKELY(a)) is false\n";
}

และผลลัพธ์:

if(a) is true
if(LIKELY(a)) is false

ที่คุณสามารถดูความหมายของน่าจะใช้!!เป็นโยนไปแบ่งความหมายของboolif

ประเด็นที่นี่ไม่ได้เป็นเช่นนั้นoperator int()และoperator bool()ควรจะเกี่ยวข้องกัน ซึ่งเป็นแนวทางปฏิบัติที่ดี

แต่ที่ใช้!!(x)แทนการstatic_cast<bool>(x)สูญเสียบริบทสำหรับC ++ 11 แปลงตามบริบท


โปรดทราบว่าการแปลงตามบริบทเกิดขึ้นจากข้อบกพร่องในปี 2012และแม้กระทั่งในช่วงปลายปี 2014 ก็ยังมีความแตกต่างของการนำไปใช้ อันที่จริงดูเหมือนว่ากรณีที่ฉันเชื่อมโยงยังใช้ไม่ได้กับ gcc
Shafik Yaghmour

@ShafikYaghmour นั่นเป็นข้อสังเกตที่น่าสนใจเกี่ยวกับการแปลงตามบริบทที่เกี่ยวข้องswitchขอบคุณ การแปลงตามบริบทที่เกี่ยวข้องที่นี่เป็นส่วนหนึ่งที่จะพิมพ์boolและบริบทเฉพาะห้ารายการที่แสดงอยู่ในนั้นซึ่งไม่รวมswitchบริบท
Maxim Egorushkin

สิ่งนี้มีผลกับ C ++ เท่านั้นใช่ไหม? ดังนั้นจึงไม่มีเหตุผลที่จะไปเปลี่ยนโปรเจ็กต์ C ที่มีอยู่เพื่อใช้(_Bool)(condition)เนื่องจาก C ไม่มีตัวดำเนินการมากเกินไป
Peter Cordes

2
ในตัวอย่างของคุณคุณใช้เพียงไม่(condition) !!(condition)ทั้งสองtrueหลังจากเปลี่ยนสิ่งนั้นแล้ว (ทดสอบด้วย g ++ 7.1) คุณสามารถสร้างตัวอย่างที่แสดงให้เห็นถึงปัญหาที่คุณกำลังพูดถึงเมื่อใช้!!บูลีนได้หรือไม่?
Peter Cordes

3
ดังที่ Peter Cordes ชี้ให้เห็นคุณพูดว่า "เมื่อมาโครเหล่านี้ [ถูก] กำหนดด้วยวิธีนี้:" จากนั้นแสดงมาโครโดยใช้ "!!" อาจเปลี่ยนความหมายของคำสั่ง if และทำลายโค้ดลองพิจารณาโค้ดต่อไปนี้: " ... แล้วคุณแสดงรหัสที่ไม่ใช้ "!!" เลย - ซึ่งเป็นที่รู้กันว่าเสียก่อน C ++ 11 โปรดเปลี่ยนคำตอบเพื่อแสดงตัวอย่างที่มาโครที่ระบุ (โดยใช้ !!) ผิดพลาด
Carlo Wood

18

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

ตามบรรทัดที่คล้ายกัน แต่ยังไม่ได้กล่าวถึงเป็นวิธีเฉพาะของ GCC ในการบังคับให้คอมไพเลอร์สร้างโค้ดบนเส้นทาง "เย็น" สิ่งนี้เกี่ยวข้องกับการใช้noinlineและcoldแอตทริบิวต์ซึ่งทำในสิ่งที่ดูเหมือนว่าพวกเขาทำ แอตทริบิวต์เหล่านี้สามารถใช้ได้กับฟังก์ชันเท่านั้น แต่ด้วย C ++ 11 คุณสามารถประกาศฟังก์ชันแลมบ์ดาแบบอินไลน์ได้และคุณลักษณะทั้งสองนี้ยังสามารถใช้กับฟังก์ชันแลมบ์ดาได้

__builtin_expectอย่างไรก็ตามเรื่องนี้ยังคงตกอยู่ในประเภททั่วไปของไมโครเพิ่มประสิทธิภาพและทำให้คำแนะนำมาตรฐานใช้ทดสอบไม่ได้เดาฉันรู้สึกเหมือนมันเป็นมากขึ้นโดยทั่วไปที่มีประโยชน์กว่า แทบจะไม่มีรุ่นใด ๆ ของโปรเซสเซอร์ x86 ที่ใช้คำแนะนำการทำนายสาขา ( ข้อมูลอ้างอิง ) ดังนั้นสิ่งเดียวที่คุณจะสามารถส่งผลต่อได้คือลำดับของรหัสแอสเซมบลี เนื่องจากคุณรู้ว่าอะไรคือการจัดการข้อผิดพลาดหรือโค้ด "edge case" คุณสามารถใช้คำอธิบายประกอบนี้เพื่อให้แน่ใจว่าคอมไพลเลอร์จะไม่คาดเดาสาขาไปยังส่วนนั้นและจะเชื่อมโยงออกจากโค้ด "hot" เมื่อปรับขนาดให้เหมาะสม

ตัวอย่างการใช้งาน:

void FooTheBar(void* pFoo)
{
    if (pFoo == nullptr)
    {
        // Oh no! A null pointer is an error, but maybe this is a public-facing
        // function, so we have to be prepared for anything. Yet, we don't want
        // the error-handling code to fill up the instruction cache, so we will
        // force it out-of-line and onto a "cold" path.
        [&]() __attribute__((noinline,cold)) {
            HandleError(...);
        }();
    }

    // Do normal stuff
    
}

ยิ่งไปกว่านั้น GCC จะเพิกเฉยต่อสิ่งนี้โดยอัตโนมัติเพื่อสนับสนุนการตอบกลับของโปรไฟล์เมื่อพร้อมใช้งาน (เช่นเมื่อรวบรวมด้วย-fprofile-use)

ดูเอกสารอย่างเป็นทางการที่นี่: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes


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

CPU สมัยใหม่ของ Intel ไม่ใช้การทำนายสาขาแบบคงที่ อัลกอริทึมที่คุณอธิบาย @Nemo ซึ่งทำนายสาขาย้อนหลังและสาขาไปข้างหน้าจะถูกคาดการณ์ว่าไม่มีการใช้ในโปรเซสเซอร์รุ่นก่อนหน้านี้และผ่าน Pentium M หรือมากกว่านั้น แต่การออกแบบที่ทันสมัยเพียงแค่เดาแบบสุ่มโดยทั่วไปแล้วจัดทำดัชนีลงในสาขาของตน ตารางที่คาดว่าจะพบข้อมูลเกี่ยวกับสาขานั้นและใช้ข้อมูลใด ๆ ที่มีอยู่ (แม้ว่าจะเป็นขยะเป็นหลักก็ตาม) ดังนั้นคำแนะนำในการทำนายสาขาจะมีประโยชน์ในทางทฤษฎี แต่อาจไม่ใช่ในทางปฏิบัติซึ่งเป็นสาเหตุที่ Intel ลบออก
Cody Grey

เพื่อความชัดเจนการใช้การทำนายสาขามีความซับซ้อนมากและข้อ จำกัด ของพื้นที่ในการแสดงความคิดเห็นบังคับให้ฉันต้องทำให้ง่ายขึ้นอย่างมาก นี่จะเป็นคำตอบทั้งหมดในตัวของมันเอง อาจยังคงมีร่องรอยของการทำนายสาขาแบบคงที่ในสถาปัตยกรรมจุลภาคสมัยใหม่เช่น Haswell แต่ก็ไม่ง่ายอย่างที่เคยเป็นมา
Cody Grey

คุณมีข้อมูลอ้างอิงสำหรับ "ซีพียู Intel สมัยใหม่ไม่ใช้การทำนายสาขาแบบคงที่" หรือไม่ บทความของ Intel เอง ( software.intel.com/en-us/articles/… ) บอกว่าเป็นอย่างอื่น ... แต่นั่นคือตั้งแต่ปี 2011
Nemo

ไม่มีข้อมูลอ้างอิงอย่างเป็นทางการ @Nemo Intel มีความเข้าใจอย่างแน่นหนาเกี่ยวกับอัลกอริทึมการทำนายสาขาที่ใช้ในชิปโดยถือว่าเป็นความลับทางการค้า สิ่งที่รู้ส่วนใหญ่คิดได้จากการทดสอบเชิงประจักษ์ เช่นเคยวัสดุของ Agner Fogเป็นทรัพยากรที่ดีที่สุด แต่ถึงอย่างนั้นเขาก็บอกว่า: "เครื่องทำนายสาขาดูเหมือนจะได้รับการออกแบบใหม่ใน Haswell แต่ไม่ค่อยมีใครรู้เรื่องการก่อสร้าง" ฉันจำไม่ได้ว่าครั้งแรกที่ฉันเห็นการวัดประสิทธิภาพที่แสดงให้เห็นถึงความดันคงที่ไม่ได้ใช้อีกต่อไปโชคไม่ดี
Cody Grey

5

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

if (__builtin_expect (x == 0, 0)) ++count;
if (__builtin_expect (y == 0, 0)) ++count;
if (__builtin_expect (z == 0, 0)) ++count;

คอมไพเลอร์จะสร้างรหัสเช่น

if (x == 0) goto if1;
back1: if (y == 0) goto if2;
back2: if (z == 0) goto if3;
back3: ;
...
if1: ++count; goto back1;
if2: ++count; goto back2;
if3: ++count; goto back3;

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

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


2

ในส่วนที่เกี่ยวกับ OP ไม่มีไม่มีวิธีใดใน GCC ที่จะบอกโปรเซสเซอร์ให้ถือว่าสาขานั้นเป็นหรือไม่ได้รับเสมอ สิ่งที่คุณมีคือ __builtin_expect ซึ่งทำในสิ่งที่คนอื่นพูด นอกจากนี้ฉันคิดว่าคุณไม่ต้องการบอกโปรเซสเซอร์ว่าสาขานั้นถูกยึดหรือไม่เสมอไป โปรเซสเซอร์ในปัจจุบันเช่นสถาปัตยกรรม Intel สามารถจดจำรูปแบบที่ค่อนข้างซับซ้อนและปรับเปลี่ยนได้อย่างมีประสิทธิภาพ

อย่างไรก็ตามมีหลายครั้งที่คุณต้องการควบคุมว่าโดยค่าเริ่มต้นมีการทำนายสาขาหรือไม่: เมื่อคุณทราบรหัสจะเรียกว่า "เย็น" ตามสถิติการแยกสาขา

ตัวอย่างที่เป็นรูปธรรมอย่างหนึ่ง: รหัสการจัดการข้อยกเว้น ตามความหมายแล้วรหัสการจัดการจะเกิดขึ้นเป็นพิเศษ แต่บางทีเมื่อต้องการประสิทธิภาพสูงสุด (อาจมีข้อผิดพลาดร้ายแรงที่ต้องดำเนินการโดยเร็วที่สุด) ดังนั้นคุณอาจต้องการควบคุมการคาดการณ์เริ่มต้น

อีกตัวอย่างหนึ่ง: คุณสามารถจัดประเภทข้อมูลที่คุณป้อนและข้ามไปยังรหัสที่จัดการผลลัพธ์ของการจัดประเภทของคุณ หากมีการจำแนกประเภทจำนวนมากโปรเซสเซอร์อาจรวบรวมสถิติ แต่สูญเสียเนื่องจากการจัดประเภทเดียวกันไม่เกิดขึ้นเร็วพอและทรัพยากรการคาดคะเนจะอุทิศให้กับรหัสที่เรียกเมื่อเร็ว ๆ นี้ ฉันหวังว่าจะมีพื้นฐานที่จะบอกโปรเซสเซอร์ "โปรดอย่าทุ่มเททรัพยากรการคาดการณ์ให้กับโค้ดนี้" แบบที่บางครั้งคุณสามารถพูดว่า "ไม่แคชนี้"

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