Pentium 4 (він же мікроархітектура Netburst) мав підказки для передбачення гілок як префікси до інструкцій jcc, але лише P4 ніколи з ними нічого не робив. Див. Http://ref.x86asm.net/geek32.html . І
Розділ 3.5 чудового посібника для вибору асоціації Agner Fog з http://www.agner.org/optimize/ . У нього також є посібник з оптимізації на C ++.
Раніше та пізніші процесори x86 мовчки ігнорують ці префікс-байти. Чи є результати тесту продуктивності для використання ймовірних / малоймовірних підказок? згадує, що PowerPC має деякі інструкції щодо переходу, які мають підказку про передбачення гілок як частину кодування. Це досить рідкісна архітектурна особливість. Статично передбачити гілки під час компіляції дуже важко зробити точно, тому зазвичай краще залишити це на апаратному забезпеченні, щоб це зрозуміти.
Офіційно опубліковано не так багато інформації про те, як саме поводяться провісники гілок та буферні цільові буфери в останніх процесорах Intel і AMD. Посібники з оптимізації (які легко знайти на веб-сайтах AMD та Intel) дають деякі поради, але не документують конкретної поведінки. Деякі люди проводили тести, щоб спробувати зрозуміти реалізацію, наприклад, скільки записів BTB має Core2 ... У будь-якому випадку, ідея явного натякання на предиктор відмовлена (на даний момент).
Документовано, наприклад, що Core2 має буфер історії гілок, який дозволяє уникнути неправильного прогнозування циклу-виходу, якщо цикл завжди виконує постійну коротку кількість ітерацій, <8 або 16 IIRC. Але не поспішайте розгортатись, тому що цикл, який вміщується в 64 байти (або 19uops на Penryn), не матиме вузьких місць для вибору інструкцій, оскільки він повторюється з буфера ... ідіть, читайте pdfs Агнера Фога, вони чудові .
Див. Також Чому за ці роки Intel змінила механізм прогнозування статичних гілок? : Intel, оскільки Sandybridge взагалі не використовує статичне прогнозування, наскільки ми можемо зрозуміти з експериментів з продуктивністю, які намагаються здійснити зворотне проектування того, що роблять центральні процесори. (Багато старих центральних процесорів мають статичне передбачення як запасний варіант, коли динамічне передбачення пропускає. Звичайним статичним прогнозуванням є прямі гілки, які не беруться, а зворотні гілки беруться (тому що зворотні гілки часто є гілками циклу).)
Ефект likely()
/ unlikely()
макросів із використанням GNU C __builtin_expect
(як згадується відповідь Дракоші) не вводить безпосередньо підказки BP в asm . (Можливо, це можна зробити з gcc -march=pentium4
, але не під час компіляції для чогось іншого).
Фактичним ефектом є викладення коду, щоб швидкий шлях мав менше взятих гілок і, можливо, менше загальних інструкцій. Це допоможе передбаченню розгалужень у випадках, коли вступає в дію статичне передбачення (наприклад, динамічні провісники холодні, на процесорах, які повертаються до статичного передбачення, замість того, щоб просто дозволяти псевдоніми гілок один одному в кешах провісників.)
Див. Яка перевага __builtin_expect GCC у твердженнях if else? для конкретного прикладу code-gen.
Взяті гілки коштують трохи дорожче, ніж не взяті гілки, навіть якщо це передбачено ідеально. Коли центральний процесор отримує код шматками по 16 байт для паралельного декодування, прийнята гілка означає, що пізніші інструкції в цьому блоці отримання не є частиною потоку команд для виконання. Це створює бульбашки в інтерфейсі, що може стати вузьким місцем у високопродуктивному коді (який не зупиняється у фоновому режимі при помилках кешу та має високий паралелізм на рівні інструкцій).
Стрибки між різними блоками також потенційно можуть торкнутися більше рядків кешу коду , збільшуючи розмір кешу L1i і, можливо, спричиняючи більше помилок кешу інструкцій, якщо було холодно. (І потенційно слід із загальним кешем). Отже, це ще одна перевага, якщо швидкий шлях буде коротким і лінійним.
Оптимізація за профілем GCC зазвичай робить імовірні / малоймовірні макроси непотрібними. Компілятор збирає дані про час виконання, яким шляхом кожна гілка йшла для прийняття рішень щодо розміщення коду, а також для ідентифікації гарячих та холодних блоків / функцій. (наприклад, це буде розгортати петлі в гарячих, але не в холодних функціях.) Див. -fprofile-generate
та -fprofile-use
в посібнику GCC . Як використовувати оптимізовані керовані профілем в g ++?
В іншому випадку GCC повинен вгадати, використовуючи різні евристики, якщо ви не використовували ймовірні / малоймовірні макроси та не використовували PGO. -fguess-branch-probability
увімкнено за замовчуванням на -O1
і вище.
https://www.phoronix.com/scan.php?page=article&item=gcc-82-pgo&num=1 містить результати порівняльних показників для PGO порівняно зі звичайними з gcc8.2 на процесорі Xeon Scalable Server. (Skylake-AVX512). Кожен бенчмарк отримав хоча б невеликий пришвидшення, а деякі отримали вигоду на ~ 10%. (Більшість з них, ймовірно, відбувається з розгортання циклу в гарячих циклах, але, мабуть, це пов’язано з кращим розташуванням гілок та іншими ефектами.)