Так, і вирівнювання, і розташування ваших даних можуть мати велику різницю в продуктивності, не лише на кілька відсотків, але на кілька-багато сотень відсотків.
Візьміть цю петлю, дві інструкції мають значення, якщо ви виконаєте достатньо циклів.
.globl ASMDELAY
ASMDELAY:
subs r0,r0,#1
bne ASMDELAY
bx lr
З кешем і без нього, а також з вирівнюванням з та без кешу, кидаючи передбачення гілок, і ви можете змінити виконання цих двох інструкцій на значну кількість (тикер таймера):
min max difference
00016DDE 003E025D 003C947F
Тест на працездатність, який ви дуже легко можете зробити самостійно. додайте або видаліть крапки навколо тестового коду і виконайте точну роботу з тимчасової роботи, перемістіть перевірені інструкції по досить широкому діапазону адрес, щоб торкнутися країв рядків кешу тощо.
Те ж саме з доступом до даних. Деякі архітектури скаржаться на нестандартний доступ (наприклад, виконуючи зчитування 32 біт за адресою 0x1001), надаючи помилку даних. Деякі з них ви можете відключити помилку і прийняти показник продуктивності. Інші, які дозволяють несогласувати доступ, ви просто отримуєте хіт продуктивності.
Іноді це "інструкції", але більшість часу це цикли годин / автобусів.
Подивіться на реалізацію memcpy в gcc для різних цілей. Скажімо, ви копіюєте структуру, що становить 0x43 байт, ви можете знайти реалізацію, яка копіює один байт, залишаючи 0x42, потім копіює 0x40 байт у великі ефективні фрагменти, а потім останній 0x2, який він може робити як два окремих байти, або як 16-бітну передачу. Вирівнювання та ціль приходять у гру, якщо адреси джерела та місця призначення однакові, наприклад, 0x1003 та 0x2003, то ви можете зробити один байт, потім 0x40 у великих фрагментах, то 0x2, але якщо один 0x1002, а інший 0x1003, то він отримує справжнє потворне і справжнє повільне.
Більшість часу це автобусні цикли. Або гірше кількість передач. Візьміть процесор із 64-бітовою шиною даних, як ARM, і виконайте передачу чотирьох слів (читання або запис, LDM або STM) за адресою 0x1004, тобто адреса, що відповідає слову, і цілком законна, але якщо шина 64 біт шириною, ймовірно, що одна інструкція перетвориться на три передачі, в цьому випадку 32-бітний на 0x1004, 64-бітний на 0x1008 та 32-бітний на 0x100A. Але якщо ви мали ту саму інструкцію, але за адресою 0x1008, вона могла б здійснити передачу чотирьох слів за адресою 0x1008. Кожна передача пов'язана з часом налаштування. Таким чином, різниця адрес від 0x1004 до 0x1008 сама по собі може бути в кілька разів швидшою, навіть / esp при використанні кешу, і всі це хіти кешу.
Якщо говорити, навіть якщо ви прочитаєте два слова, прочитані за адресою 0x1000 проти 0x0FFC, 0x0FFC з помилками кешу призведе до того, що два кеш-рядки зчитуються, де 0x1000 - це одна лінія кешу, ви маєте покарання у кеш-рядку, прочитаному в будь-якому випадку для випадкового доступу (читання більше даних, ніж використання), але потім подвоюється. Як вирівнюються ваші структури або ваші дані взагалі та частота доступу до цих даних тощо можуть спричинити обробку кешу.
Ви можете роздягнути свої дані таким чином, що, обробляючи дані, ви можете створювати виселення, ви можете отримати справжнє нещастя і в кінцевому підсумку, використовуючи лише частину свого кешу, і, переходячи через нього, наступна крапка даних стикається з попередньою крапкою . Змішуючи свої дані або переупорядковуючи функції у вихідному коді тощо, ви можете створювати або видаляти зіткнення, оскільки не всі кеші створюються рівними, компілятор не допоможе тобі саме тут. Навіть виявлення хітів або покращення ефективності залежить від вас.
Усі речі, які ми додали для покращення продуктивності, ширшої шини даних, трубопроводів, кеш-пам'ять, прогнозування гілок, декількох одиниць / шляхів виконання тощо. Найчастіше допоможуть, але всі вони мають слабкі місця, які можна використовувати навмисно або випадково. Компілятор або бібліотеки можуть зробити з цим дуже мало, якщо вас цікавить продуктивність, вам потрібно налаштувати один з найбільших факторів настройки - вирівнювання коду та даних, а не просто вирівнювання на 32, 64, 128, 256 бітові межі, але також там, де речі відносно один одного, ви хочете, щоб широко використовувані цикли або повторно використовувані дані не переходили в той самий спосіб кешу, кожен з них хоче власне. Компілятори можуть допомогти, наприклад, впорядкувати вказівки для супер скалярної архітектури, переупорядкувати інструкції, що відносно один одного не мають значення,
Найбільший нагляд - припущення, що процесор є вузьким місцем. Це не було правдою протягом десяти років і більше, подача процесора - це проблема, і саме тут виникають такі проблеми, як хіти вирівнювання продуктивності, обробка кешу тощо. Трохи працюючи навіть на рівні вихідного коду, переупорядкування даних у структурі, упорядкування змінних / структурних декларацій, упорядкування функцій у вихідному коді та трохи додаткового коду для вирівнювання даних, можуть покращити продуктивність у кілька разів за або більше.