Чи є приріст / втрата продуктивності при використанні цілих чисел без знака над цілими числами зі знаком?
Якщо так, чи це теж коротко і довго?
Чи є приріст / втрата продуктивності при використанні цілих чисел без знака над цілими числами зі знаком?
Якщо так, чи це теж коротко і довго?
Відповіді:
Ділення на потужності 2 швидше unsigned int
, оскільки його можна оптимізувати в одну зміну. З signed int
, як правило , вимагає більше машинних команд, тому що поділ раундів до нуля , але перехід до правих раундів вниз . Приклад:
int foo(int x, unsigned y)
{
x /= 8;
y /= 8;
return x + y;
}
Ось відповідна x
частина (підписаний розділ):
movl 8(%ebp), %eax
leal 7(%eax), %edx
testl %eax, %eax
cmovs %edx, %eax
sarl $3, %eax
І ось відповідна y
частина (без підпису):
movl 12(%ebp), %edx
shrl $3, %edx
shrl
повинен бути буквальним?
У C ++ (і C) переповнене ціле число зі знаком невизначене, тоді як переповнення беззнакового цілого числа визначено для обгортання. Зверніть увагу, що, наприклад, у gcc, ви можете використовувати прапор -fwrapv, щоб визначити підписане переповнення (для обгортання).
Невизначене підписане ціле число переповнення дозволяє компілятору припустити, що переповнення не відбувається, що може створити можливості оптимізації. Див., Наприклад, цю публікацію в блозі для обговорення.
unsigned
призводить до однакових або кращих показників, ніж signed
. Кілька прикладів:
signed
чисел; gcc робить це за допомогою 1 інструкції, як у unsigned
випадку)short
зазвичай призводить до тих самих або гірших показників, ніж int
(припускаючи sizeof(short) < sizeof(int)
). Погіршення продуктивності відбувається, коли ви присвоюєте результат арифметичної операції (яка, як правило int
, ніколи short
) змінній типу short
, яка зберігається в реєстрі процесора (який також є типом int
). Всі перетворення з short
до int
зайняти деякий час і дратує.
Примітка: деякі ЦСП мають інструкції щодо швидкого множення для signed short
типу; в цьому конкретному випадку short
швидше, ніж int
.
Щодо різниці між int
і long
, я можу лише здогадуватися (я не знайомий з 64-розрядними архітектурами). Звичайно, якщо int
і long
мають однакові розміри (на 32-розрядних платформах), їх продуктивність також однакова.
Дуже важливе доповнення, на яке звернули увагу кілька людей:
Що насправді важливо для більшості програм, це розмір пам'яті та використовувана пропускна здатність. Ви повинні використовувати найменші необхідні цілі числа ( short
, можливо, навіть signed/unsigned char
) для великих масивів.
Це дасть кращу продуктивність, але коефіцієнт посилення нелінійний (тобто не в 2 чи 4 рази) і дещо непередбачуваний - це залежить від розміру кешу та взаємозв'язку між обчисленнями та передачею пам'яті у вашому додатку.
short
відрізняється від вашого / будь-кого іншого)
short
сьогодні (а не кеш-пам’ять фактично нескінченна), і дуже вагомою причиною.
short
швидше, ніж int
коли пам'ять обмежена . З мого досвіду, вони мають однакову продуктивність на x86 і short
повільніше на ARM.
Це буде залежати від точної реалізації. У більшості випадків різниці не буде. Якщо вам дійсно цікаво, вам доведеться спробувати всі варіанти, які ви розглядаєте, і виміряти ефективність.
+1
для "якщо ви хочете знати, вам потрібно виміряти". Дуже прикро, що на це потрібно відповідати майже щотижня.
Це в значній мірі залежить від конкретного процесора.
У більшості процесорів є інструкції як для підписаної, так і безпідписаної арифметики, тож різниця між використанням підписаних та беззнакових цілих чисел зводиться до того, яку використовує компілятор.
Якщо будь-який із двох швидший, це повністю специфічний процесор, і, швидше за все, різниця незначна, якщо вона взагалі існує.
Різниця в продуктивності між цілими числами зі знаком та без знака насправді є загальнішою, ніж передбачає відповідь на прийняття. Ділення цілого числа без знака на будь-яку константу може бути здійснено швидше, ніж ділення цілого числа без знака на константу, незалежно від того, чи є константа степенем у два. Подивитися Http://ridiculousfish.com/blog/posts/labor-of-division-episode-iii.html
В кінці своєї посади він включає наступний розділ:
Закономірним є питання, чи може та сама оптимізація покращити підписаний підрозділ; на жаль, здається, що ні, з двох причин:
Приріст дивіденду повинен стати збільшенням величини, тобто збільшенням, якщо n> 0, зменшенням, якщо n <0. Це створює додаткові витрати.
Штраф за дільник, що не співпрацює, становить приблизно приблизно половину менше у підписаному відділі, залишаючи менше вікно для поліпшень.
Таким чином, схоже, що алгоритм округлення може бути зроблений для роботи у підписаному підрозділі, але він буде недостатньо ефективним для стандартного алгоритму округлення.
Не лише ділення на ступені 2 швидше з типом без знака, ділення на будь-які інші значення також швидше з типом без знака. Якщо ви подивитесь на таблиці інструкцій Агнера Фога то побачите, що непідписані підрозділи мають однакову або кращу продуктивність, ніж підписані версії
Наприклад, з AMD K7
Інструкція | Операнди | Опс | Латентність | Взаємна пропускна здатність |
---|---|---|---|---|
DIV | r8 / m8 | 32 | 24 | 23 |
DIV | r16 / m16 | 47 | 24 | 23 |
DIV | r32 / m32 | 79 | 40 | 40 |
IDIV | r8 | 41 | 17 | 17 |
IDIV | r16 | 56 | 25 | 25 |
IDIV | r32 | 88 | 41 | 41 |
IDIV | m8 | 42 | 17 | 17 |
IDIV | m16 | 57 | 25 | 25 |
IDIV | m32 | 89 | 41 | 41 |
Те саме стосується Intel Pentium
Інструкція | Операнди | Цикли годин |
---|---|---|
DIV | r8 / m8 | 17 |
DIV | r16 / m16 | 25 |
DIV | r32 / m32 | 41 |
IDIV | r8 / m8 | 22 |
IDIV | r16 / m16 | 30 |
IDIV | r32 / m32 | 46 |
Звичайно, це досить древні. Новіші архітектури з більшою кількістю транзисторів можуть зменшити прогалину, але застосовуються основні речі: як правило, потрібно більше мікрооперацій, більше логіки та більше затримок, щоб зробити підписаний поділ
Одним словом, не турбуйтеся перед фактом. Але не турбуйся після цього.
Якщо ви хочете мати продуктивність, вам доведеться використовувати оптимізацію продуктивності компілятора, яка може працювати проти здорового глузду. Запам’ятайте одне, що різні компілятори можуть компілювати код по-різному, і вони самі мають різні види оптимізації. Якщо ми говоримо про g++
компілятор і говоримо про максимізацію рівня його оптимізації за допомогою -Ofast
або, принаймні, -O3
прапора, на мій досвід він може компілювати long
тип у код з навіть кращою продуктивністю, ніж будь-який unsigned
тип, або навіть простоint
.
Це з мого власного досвіду, і я рекомендую вам спочатку написати свою повну програму і піклуватися про такі речі лише після цього, коли у вас є фактичний код, і ви можете скомпілювати його з оптимізаціями, щоб спробувати вибрати типи, які насправді виконуються найкраще. Це також хороша дуже загальна порада щодо оптимізації коду для продуктивності, спочатку пишіть швидко, спробуйте компілювати з оптимізаціями, налаштуйте речі, щоб побачити, що найкраще працює. Вам також слід спробувати використовувати різні компілятори для компіляції вашої програми та вибрати той, який виводить найбільш ефективний машинний код.
Оптимізована багатопотокова програма обчислень лінійної алгебри може легко мати різницю в продуктивності> 10 разів чітко оптимізовану та неоптимізовану. Тож це має значення.
Вихід оптимізатора суперечить логіці у багатьох випадках. Наприклад, у мене був випадок, коли різниця між a[x]+=b
і a[x]=b
змінювалась часом виконання програми майже вдвічі. І ні, a[x]=b
не швидший був.
Ось, наприклад, NVidia, яка заявляє, що для програмування своїх графічних процесорів:
Примітка: Як уже було рекомендовано найкращу практику, підписана арифметика повинна мати перевагу над непідписаною арифметикою, де це можливо, для кращої пропускної здатності на SMM. Стандарт мови C встановлює більше обмежень щодо поведінки переповнення для математики без підпису, обмежуючи можливості оптимізації компілятора.
IIRC, на x86 підписаний / непідписаний не повинен мати жодної різниці. З іншого боку, короткий / довгий - це інша історія, оскільки обсяг даних, який потрібно переміщати в / з оперативної пам’яті, на тривалий час більший (інші причини можуть включати операції приведення, такі як розширення короткого до довгого).
Підписані та беззнакові цілі числа завжди працюватимуть як інструкції з одним годинником і матимуть однакову продуктивність читання-запису, але, за словами д-ра Андрія Александреску, непідписаним надається перевага перед підписаним. Причиною цього є те, що ви можете вмістити подвійну кількість чисел в одній і тій самій кількості бітів, оскільки ви не витрачаєте знаковий біт, і ви будете використовувати менше інструкцій, перевіряючи наявність від’ємних чисел, що призводить до збільшення продуктивності від зменшеного ПЗУ. На моєму досвіді з VM Kabuki , який має надвисокопродуктивний сценарійВпровадження, рідко коли вам дійсно потрібен номер із підписом при роботі з пам'яттю. Я витрачаю майські роки на арифметику покажчиків із підписаними та беззнаковими числами, і я не знайшов вигоди для підписаного, коли не потрібен біт знаку.
Де підписом може бути кращим, коли використовується зсув бітів для виконання множення та ділення степенів 2, оскільки ви можете виконувати негативні степені 2 ділення із підписаними цілими числами доповнення 2. Будь ласка, перегляньте ще кілька відеороликів від Андрія для отримання додаткових методів оптимізації. Ви також можете знайти хорошу інформацію в моїй статті про найшвидший у світі алгоритм перетворення цілого числа в рядок .
Традиційно int
це власний цілочисельний формат цільової апаратної платформи. Будь-який інший цілий тип може спричинити штрафні санкції.
РЕДАГУВАТИ:
У сучасних системах справа дещо інша:
int
насправді може бути 32-розрядною в 64-розрядних системах з міркувань сумісності. Я вважаю, що це відбувається в системах Windows.
Сучасні компілятори можуть неявно використовувати int
під час обчислення для коротших типів в деяких випадках.
int
все ще має ширину 32 біти, але 64-бітні типи ( long
або long long
, залежно від ОС) повинні бути принаймні такими ж швидкими.
int
завжди має ширину 32 біти у всіх відомих мені системах (Windows, Linux, Mac OS X, незалежно від того, чи є процесор 64-розрядним чи ні). Це long
тип, який відрізняється: 32 біти в Windows, але одне слово в Linux та OS X.
int
не повинен бути завжди 32 біта.
Непідписане ціле число вигідне тим, що ви зберігаєте і обробляєте як потік бітів, я маю на увазі просто дані без знака, тому множення, розробка стає простішим (швидшим) за допомогою операцій зсуву бітів