Посібники з оптимізації Agner Fog чудові. У нього є путівники, таблиці термінів інструкцій та документи з мікроархітектури всіх останніх процесорів x86 процесора (починаючи від Intel Pentium). Дивіться також деякі інші ресурси, пов’язані з /programming//tags/x86/info
Для задоволення я відповім на деякі запитання (номери з останніх процесорів Intel). Вибір опції не є головним фактором оптимізації коду (якщо ви не зможете уникнути поділу.)
Чи одне множення повільніше у процесорі, ніж додавання?
Так (якщо це не сила 2). (3–4 рази затримка, лише одна на пропускну здатність в Intel.) Не виходьте далеко з шляху, щоб уникнути цього, оскільки це так швидко, як 2 або 3 додає.
Які саме швидкісні характеристики основних математичних та контрольних протоколів потоку?
Дивіться таблиці інструкцій Agner Fog та посібник з мікроархітектури, якщо ви хочете точно знати : P. Будьте обережні при умовних стрибках. Безумовні стрибки (як виклики функцій) мають невеликі накладні витрати, але не дуже.
Якщо два опкоди мають однакову кількість циклів для виконання, то обидва можна використовувати взаємозамінно без будь-якого посилення / втрати продуктивності?
Ні, вони можуть змагатися за той самий порт виконання, що і щось інше, а можуть і ні. Це залежить від того, над якими ланцюгами залежностей CPU може працювати паралельно. (На практиці зазвичай не може бути прийнято жодного корисного рішення. Іноді з'являється, що ви можете використовувати зсув вектора або перетасування векторів, які працюють у різних портах процесорів Intel. Але зміна байтів всього реєстру ( PSLLDQ
і т. д.) працює в блоці переміщення.)
Вдячні за будь-які інші технічні деталі, якими ви можете поділитися щодо продуктивності процесора x86
Документи мікроарха Agner Fog описують конвеєри процесорів Intel та AMD досить докладно, щоб точно визначити, скільки циклів повинно пройти цикл за ітерацію та чи є вузьке місце загальною пропускною здатністю, ланцюгом залежності або суперечкою для одного порту виконання. Дивіться деякі мої відповіді на StackOverflow, на кшталт цієї чи цієї .
Крім того, http://www.realworldtech.com/haswell-cpu/ (і подібні до попередніх конструкцій) цікаво читати, якщо вам подобається дизайн процесора.
Ось ваш список, відсортований за процесором Haswell, на основі моїх найкращих оцінок. Це насправді не корисний спосіб роздуму над речами ні для чого, окрім налаштування циклу зору. Ефекти прогнозування кешу / гілки зазвичай домінують, тому пишіть свій код, щоб мати гарні зразки. Числа дуже ручні, і намагайтеся врахувати високу затримку, навіть якщо пропускна здатність не є проблемою, або для отримання більшої кількості Uops, які засмічують трубу, щоб інші речі паралельно відбувалися. Esp номери кешу / гілки дуже складні. Затримка має значення для петельних залежностей, пропускна здатність має значення, коли кожна ітерація є незалежною.
TL: DR: ці цифри складаються з урахуванням того, що я малюю для "типового" випадку використання, що стосується компромісів між затримками, вузькими місцями у виконанні портів та пропускною спроможністю (або стійлами для речей, таких як пропуски відділення ). Будь ласка, не використовуйте ці номери для будь-якого серйозного аналізу персоналу .
- 0,5 - 1 побітове / додавання цілих чисел / віднімання /
зсув та обертання (кількість компіляцій часу / компіляції) /
векторні версії всіх цих (1 до 4 за пропускну здатність циклу, 1 затримка циклу)
- 1 вектор хв, макс, порівняння-рівне, порівняння-більший (для створення маски)
- 1,5 переміщення вектор. У Haswell і новіших є лише один порт перетасовки, і мені здається, що зазвичай потрібно багато перетасувати, якщо вам це потрібно, тому я зважую його трохи вище, щоб заохотити думати про використання меншої кількості перетасовок. Вони не вільні, особливо. якщо вам потрібна маска керування pshufb з пам'яті.
- 1,5 завантаження / зберігання (потрапляння кешу L1. Пропускна здатність краща за затримку)
- 1.75 Множення цілого числа (затримка 3c / один на 1c tput в Intel, 4c lat на AMD і лише один на 2c tput). Невеликі константи навіть дешевші за допомогою LEA та / або ADD / SUB / shift . Але звичайно константи часу компіляції завжди хороші і часто можуть оптимізуватися в інші речі. (А множення в циклі часто компілятором може бути зменшено міцність,
tmp += 7
а не циклічно tmp = i*7
)
- 1.75 деякі перемикання 256b у векторному режимі (додаткова затримка в інтернах, які можуть переміщувати дані між 128b смугами AVX-вектора). (Або від 3 до 7 на Ryzen, де перетасовка смуги, що перетинає смугу руху, потребує набагато більше Uops)
- 2 fp add / sub (та векторні версії того самого) (1 або 2 за цикл, затримка 3 - 5 циклів). Може бути повільним, якщо ви обмежуєте затримку, наприклад, підсумовуючи масив лише з 1
sum
змінною. (Я міг би зважити це і fp mul на рівні від 1 або до 5, залежно від випадку використання).
- 2 векторні fp mul або FMA. (x * y + z коштує так само дешево, як або mul або додавання, якщо ви компілюєте з увімкненою підтримкою FMA).
- 2 вставлення / вилучення регістрів загального призначення у векторні елементи (
_mm_insert_epi8
тощо)
- 2,25 векторного int mul (16-бітні елементи або pmaddubsw роблять 8 * 8 -> 16-бітові). Дешевше на Skylake, з кращою пропускною здатністю, ніж скалярний мул
- 2,25 зсув / обертання за змінним підрахунком (2c затримка, один на 2c пропускної здатності в Intel, швидше в AMD або з BMI2)
- 2.5 Порівняння без розгалуження (
y = x ? a : b
або y = x >= 0
) ( test / setcc
або cmov
)
- 3 int-> float перетворення
- 3 ідеально передбачуваний потік управління (передбачувана гілка, виклик, повернення).
- 4 векторні int mul (32-бітні елементи) (2 Uops, 10c затримка на Haswell)
- 4 ціле ділення або
%
константа часу компіляції (не-потужність 2).
- 7 векторних горизонтальних опцій (наприклад,
PHADD
додавання значень у вектор)
- 11 (вектор) FP-відділення (затримка 10-13c, одна на пропускну здатність 7c або гірше). (Може бути дешевим, якщо використовується рідко, але пропускна здатність на 6 - 40 разів гірша, ніж FP муль)
- 13? Контрольний потік (погано прогнозована гілка, можливо 75% передбачувана)
- 13 int поділ ( так дійсно , він повільніше, ніж поділ FP, і не може векторизувати). (зауважте, що компілятори діляться на постійну, використовуючи mul / shift / add з магічною константою , а div / mod потужностями 2 дуже дешево.)
- 16 (вектор) FP sqrt
- 25? завантаження (хіт L3 кеша). (магазини кеш-міс коштують дешевше, ніж вантажі.)
- 50? FP trig / exp / log. Якщо вам потрібно багато exp / log і не потрібна повна точність, ви можете торгувати точністю для швидкості за допомогою коротшого полінома та / або таблиці. Можна також SIMD-векторизацію.
- 50-80? завжди - передбачувана галузь, вартістю 15-20 циклів
- 200-400? завантажувати / зберігати (пропустити кеш)
- 3000 ??? читати сторінку з файлу (хіт кешу диска ОС) (тут складаються номери)
- 20000 ??? сторінка для читання диска (пропуск кеш-диска ОС, швидкий SSD) (повністю складений номер)
Я цілком склав це на основі здогадок . Якщо щось виглядає не так, це тому, що я думав про інший випадок використання, або помилку редагування.
Відносна вартість речей на процесорних процесорах AMD буде аналогічною, за винятком того, що вони мають швидші цілочисельні перемикачі, коли кількість змін змінюється. Процесори сімейства AMD Bulldozer, звичайно, повільніші для більшості кодів з різних причин. (Ryzen досить гарна у великій кількості речей).
Майте на увазі, що звести речі до одновимірної вартості дійсно неможливо . Окрім кеш-пропусків та помилок гілок, вузьким місцем у блоці коду може бути затримка, загальна пропускна здатність (frontend) або пропускна здатність певного порту (порт виконання).
"Повільна" операція, як поділ FP, може бути дуже дешевою, якщо навколишній код заважає процесора займатися іншою роботою . (вектор FP div або sqrt - це 1 взагалі кожен, у них просто погана затримка та пропускна здатність. Вони блокують лише одиницю поділу, а не весь порт виконання, на якому він працює. Integer div - це кілька упп.) Отже, якщо у вас є лише один поділ FP на кожні ~ 20 мкм і додайте, і для роботи процесора є інша робота (наприклад, незалежна ітерація циклу), тоді "вартість" FP div може бути приблизно такою самою, як муль FP. Це, мабуть, найкращий приклад чогось із низькою пропускною здатністю, коли це все ви робите, але дуже добре поєднується з іншим кодом (коли затримка не є фактором) через низьку загальну кількість Uops.
Зауважте, що ціле ділення не є настільки сприятливим для оточуючого коду: у Haswell це 9 уп, один на пропускну здатність 8-11c та затримку 22-29c. (64-бітний поділ набагато повільніше, навіть у Skylake.) Отже, затримка та пропускна здатність дещо схожі на FP div, але FP div - лише один загалом.
Для прикладів аналізу короткої послідовності інсів для пропускної здатності, затримки та загальної величини UPS, див. Деякі мої відповіді:
- Розділ "Аналіз ефективності" цієї відповіді резюмує речі. Решта відповіді стосується оптимізації циклу, який відбувається
sum += x[i] * y[i]
шляхом розкручування кількома векторними акумуляторами, щоб приховати затримку FMA. Це досить технічно і на низькому рівні, але показує, який вигляд мови збірки ви хочете, щоб ваш компілятор зробив, і чому це має значення.
- Чому цей код C ++ швидший, ніж моя рукописна збірка для тестування гіпотези Collatz? : ця популярна відповідь, яку я написав, пояснює, як перенести компілятор на створення кращої якості, коли це можливо. Також деякі деталі оптимізації ASM, які дозволяють перемогти компілятор для невеликих функцій / циклів у цьому випадку. IDK, чому в ньому стільки більше відгуків, ніж будь-які інші мої відповіді.
- Який ефективний спосіб підрахунку встановлених бітів у позиції чи нижче? : Перф-аналіз послідовності з 6-ти інн для цікавої проблеми, коли деяке тримання рук у джерелі С призвело до того, що gcc вдосконалив код. Деякі з моїх інших відповідей стосуються ще коротших послідовностей інструкцій.
- Найшвидший калькулятор абсолютної величини за допомогою SSE
- Проблеми з ADC / SBB та INC / DEC в щільних петлях на деяких процесорах
- Швидкий векторизований rsqrt і зворотний з SSE / AVX залежно від точності
- Сортування 64-бітних структур за допомогою AVX?
- /programming//search?q=user%3A224132+throughput+latency+cycles
IDK, якщо інші люди пишуть відповіді ТА, включаючи подібний аналіз. Мені набагато простіше шукати свій власний час, бо я знаю, що часто впадаю в цю деталь, і я пам'ятаю, що я написав.