Чому швидкі цілі типи швидше, ніж інші цілі типи?


107

У ISO / IEC 9899: 2018 (C18) зазначено під 7.20.1.3:

7.20.1.3 Найшвидші цілі числа мінімальної ширини

1 Кожен з наступних типів позначає цілий тип, який, як правило, найшвидший 268) для роботи з усіма цілими типами, які мають принаймні задану ширину.

2 Назва typedef int_fastN_tпозначає найшвидший підписаний цілий тип шириною не менше N. Ім'я typedef uint_fastN_tпозначає найшвидший неподписаний цілий тип з шириною принаймні N.

3 Необхідні наступні типи:

int_fast8_t, int_fast16_t, int_fast32_t, int_fast64_t, uint_fast8_t, uint_fast16_t, uint_fast32_t,uint_fast64_t

Усі інші типи цієї форми необов’язкові.


268) Гарантований тип не гарантується швидкістю для всіх цілей; якщо реалізація не має чітких підстав для вибору одного типу над іншим, вона просто вибере якийсь цілий тип, що відповідає вимогам підпису та ширини.


Але не вказано, чому ці "швидкі" цілі типи швидші.

  • Чому ці швидкі цілі типи швидше, ніж інші цілі типи?

Я позначив це питання C ++, оскільки швидкі цілі типи також доступні в C ++ 17 у файлі заголовка cstdint. На жаль, в ISO / IEC 14882: 2017 (C ++ 17) немає такого розділу про їх пояснення; Я втілив цей розділ інакше в тілі питання.


Інформація: В C вони оголошені у файлі заголовка stdint.h.


24
Ключовим моментом тут є те, що ці цілі типи не є окремими, магічно швидшими типами. Вони просто псевдоніми до того, що звичайний існуючий тип є найшвидшим на цій машині для цієї операції.
mtraceur

3
Компілятор випускає операційні коди процесора для завантаження, зберігання, маскування та зміни місць розташування пам'яті та регістрів конкретних розмірів; це все, що бачить процесор. Операційна система нічого спільного з цим не має. Це все, що робить компілятор, точно так, як якщо б ви самі вказали даний типdef. (Я припускаю, що компілятору дозволено внутрішньо обробляти його інакше - можливо, більш ефективно, якщо це можливо - ніж користувач typedef, доки немає видимих ​​різниць у поведінці.)
Пітер - Відновіть Моніку

1
@ RobertS-ReinstateMonica Якщо бути точним, ці "псевдоніми" - це лише typedefтвердження. Так зазвичай це робиться на стандартному рівні бібліотеки. Зрозуміло, C Стандарт не накладає реальне обмеження на те , що вони typedef- значить , наприклад , типова реалізація зробити з на 32-бітну системі, але гіпотетичний компілятор може , наприклад , реалізувати властивий тип і обіцяє зробити деякі фантазії оптимізація для вибору найшвидшого типу машин у кожному конкретному випадку для змінних цього типу, і тоді бібліотека могла саме до цього. int_fast32_ttypedefint__int_fasttypedef
mtraceur

1
@ RobertS-ReinstateMonica Так, правда. Ви отримуєте програми максимальної продуктивності зі специфічними архітектурними прапорами компіляції, що робить бінарне менш портативним.
Пітер - Відновіть Моніку

1
@ Робертс ReinstateMonica Це буде найбільш ефективним на платформі він був складений для , не обов'язково на .
HABO

Відповіді:


152

Уявіть процесор, який виконує лише 64-бітні арифметичні операції. Тепер уявіть, як ви реалізували б підписаний 8-бітний додаток на такому процесорі. Для отримання потрібного результату обов'язково потрібно задіяти більше однієї операції. У такому процесорі 64-бітні операції швидше, ніж операції на інших цілих ширинах. У цій ситуації все, Xint_fastY_tможливо, може бути псевдонімом 64-бітного типу.

Якщо процесор підтримує швидкі операції для вузьких цілих типів і, отже, ширший тип не швидший, ніж вужчий, то Xint_fastY_tне буде псевдонімом більш широкого типу, ніж це необхідно для представлення всіх бітів Y.

З цікавості я перевірив розміри конкретної реалізації (GNU, Linux) для деяких архітектур. Вони не однакові для всіх реалізацій в одній архітектурі:

┌────╥───────────────────────────────────────────────────────────┐
 Y     sizeof(Xint_fastY_t) * CHAR_BIT                         
    ╟────────┬─────┬───────┬─────┬────────┬──────┬────────┬─────┤
     x86-64  x86  ARM64  ARM  MIPS64  MIPS  MSP430  AVR 
╞════╬════════╪═════╪═══════╪═════╪════════╪══════╪════════╪═════╡
 8   8       8    8      32   8       8     16      8   
 16  64      32   64     32   64      32    16      16  
 32  64      32   64     32   64      32    32      32  
 64  64      64   64     64   64      64    64      64  
└────╨────────┴─────┴───────┴─────┴────────┴──────┴────────┴─────┘

Зауважте, що хоча операції над більшими типами можуть бути швидшими, такі типи також займають більше місця в кеші, і, таким чином, їх використання не обов'язково дає кращі показники. Крім того, не завжди можна вірити, що впровадження зробила правильний вибір в першу чергу. Як завжди, вимірювання потрібно для досягнення оптимальних результатів.


Скріншот таблиці для користувачів Android:

Знімок екрана вище таблиці

(У Android немає символів для малювання коробки в моно шрифті - ref )


Коментарі не для розширеного обговорення; ця розмова була переміщена до чату .
Самуель Liew

@RobertSsupportsMonicaCellio Ні. "Не однакове для всіх архітектур" також вірно, але це відразу видно з показаних даних, тому я б не вважав за необхідне заявляти очевидне. Я показав лише значення однієї реалізації, і справді інші мають різні варіанти. Перевірте, наприклад, x86-64 на Windows. Ви знайдете різні розміри порівняно з показаними тут.
eerorika

@RobertSsupportsMonicaCellio На мою думку, ці коментарі стосуються відповіді та є доречними тут. Я дозволю модератору перемістити їх, якщо вони відчують потребу в цьому.
eerorika

11

Вони, принаймні, не надійно.

Швидкі типи - це просто typedefs для звичайних типів, однак, залежно від реалізації, як їх визначити. Вони повинні бути як мінімум потрібного розміру, але вони можуть бути і більшими.

Це правда, що в деяких архітектурах деякі цілі типи мають кращу ефективність, ніж інші. Наприклад, ранні реалізації ARM мали інструкції з доступу до пам'яті для 32-розрядних слів і для непідписаних байтів, але вони не мали інструкцій для півслов або підписаних байтів. Інструкції з півсловом та підписаними байтами були додані пізніше, але вони все ще мають менш гнучкі параметри адресації, оскільки їх потрібно було вкласти в запасний простір кодування. Крім того, усі фактичні вказівки щодо обробки даних щодо ARM працюють над словами, тому в деяких випадках для обгрунтування результатів може знадобитися замаскувати менші значення для отримання правильних результатів.

Однак існує також суперечливий тиск кешу, навіть якщо потрібно більше інструкцій щодо завантаження / зберігання / обробки меншого значення. Менше значення все ж може працювати краще, якщо зменшить кількість пропусків кешу.

Визначення типів на багатьох загальних платформах не здається продуманим. Зокрема, сучасні 64-розрядні платформи, як правило, мають гарну підтримку 32-бітових цілих чисел, проте "швидкі" типи часто непотрібно 64-бітні на цих платформах.

Крім того, типи C стають частиною ABI платформи. Тож навіть якщо постачальник платформи виявить, що вони зробили німий вибір, згодом важко змінити ці німі рішення.

Ігноруйте "швидкі" типи. Якщо вас справді хвилює ціла ефективність, порівняйте свій код з усіма доступними розмірами.


7

Швидкі типи не швидші за всі інші цілі типи - насправді вони ідентичні деякому "звичайному" цілому типу (вони є лише псевдонімом цього типу) - той тип, який буде найшвидшим для отримання значення принаймні, що багато біт.

Це просто залежить від платформи , яка цілий типу кожного швидкого типу є псевдонімом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.