Чому Java API використовує int замість короткого чи байтового?


137

Чому Java API використовує int, коли shortабо навіть byteбуде достатньо?

Приклад: DAY_OF_WEEKполе в класі Calendarвикористовує int.

Якщо різниця занадто мінімальна, то навіщо ці типи даних ( short, int) існують взагалі?

Відповіді:


166

Деякі причини вже вказані. Наприклад, той факт, що "... (Майже) всі операції над байтом, короткими будуть сприяти цим примітивам до int" . Однак очевидним наступним питанням було б: ЧОМУ рекламуються ці типи int?

Отже, щоб піти на один рівень глибше: відповідь може бути просто пов'язана з набором інструкцій Java-віртуальної машини. Як узагальнено в таблиці в специфікації Java Virtual Machine , всі інтегральні арифметичні операції, такі як додавання, ділення та інші, доступні лише для типу intта типу long, а не для менших типів.

(Вбік: менші типи ( byteі short) в основному призначені лише для масивів . Масив на зразок new byte[1000]займе 1000 байт, а масив на зразок new int[1000]займе 4000 байт)

Тепер, звичайно, можна сказати, що "... очевидним наступним питанням буде: ЧОМУ ці інструкції пропонуються лише для intlong)?" .

Одна з причин згадується у згаданому вище специфіці JVM:

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

Крім того, віртуальну машину Java можна розглядати як абстракцію реального процесора. І введення спеціальної арифметичної логічної одиниці для менших типів не варто було б докладати зусиль: їй знадобляться додаткові транзистори, але вона все одно може виконати лише одне додавання за один тактовий цикл. Домінуюча архітектура, коли проектувався JVM, була 32-бітною, саме 32-бітовою int. (Операції, що передбачають 64-бітове longзначення, реалізуються як окремий випадок).

(Примітка. Останній абзац трохи спрощений, враховуючи можливу векторизацію тощо), але повинен дати основну думку, не заглиблюючись занадто глибоко в теми дизайну процесора)


EDIT: Короткий додаток, орієнтований на приклад із запитання, але в більш загальному сенсі: Можна також запитати, чи не вигідно буде зберігати поля, використовуючи менші типи. Наприклад, можна подумати, що пам'ять можна зберегти, зберігаючи Calendar.DAY_OF_WEEKяк byte. Але тут вступає в дію Формат файлів класу Java: Усі поля у файлі класу займають принаймні один «слот», який має розмір одного int(32 біта). ("Широкі" поля, doubleі long, займають два слоти). Тож явно оголошуючи поле таким чином, як shortі byteне збережеться жодна пам'ять.


Я б здогадався, що логіка того, чому операнди просуваються до int, також пов'язана з обґрунтуванням, що використовується в C і C ++
Шафік Ягмур

@ Marco13 "Так явно оголошення поля коротким чи байтовим також не збереже пам'яті." це правда? Я не думаю, що це правильно.
ACV

@ACV Строго кажучи, реалізація може обрати для зберігання більш компактну форму, але формат, який піддається «практично» (тобто віртуальній машині), буде вважати значення як мінімум розмірами int. Якщо у вас є посилання на іншу реалізацію, я би оновив відповідь і відповідно вставив посилання.
Marco13

40

(Майже) Усі операції на byte, наприклад short, сприятимуть їх int, наприклад, не можна писати:

short x = 1;
short y = 2;

short z = x + y; //error

Арифметика простіша і простіша при використанні int, не потрібно робити кидання.

Що стосується простору, це робить дуже незначну різницю. byteі shortце ускладнить речі, я не вважаю, що ця мікрооптимізація того варта, оскільки ми говоримо про фіксовану кількість змінних.

byteє релевантним та корисним при програмуванні вбудованих пристроїв або роботі з файлами / мережами. Крім того, ці примітиви обмежені, що робити, якщо розрахунки в майбутньому можуть перевищити їх межі? Спробуйте подумати про розширення для Calendarкласу, яке могло б розвиватися в більшій кількості.

Також відзначимо , що протягом 64-розрядних процесорів, місцеві жителі будуть збережені в регістрах і не буде використовувати будь - які ресурси, тому використання int, shortі інші примітиви не буде ніякої різниці взагалі. Більше того, багато реалізацій Java вирівнюють змінні * (та об'єкти).


* byte і shortзаймають таке ж простір, як intякщо б вони були локальними змінними, змінними класів або навіть змінними екземплярів . Чому? Оскільки в (більшості) комп'ютерних системах адреси змінних вирівнюються , тому, наприклад, якщо ви використовуєте один байт, ви фактично закінчитеся двома байтами - один для самої змінної, а інший для заміни.

З іншого боку, в масивах byteвізьміть 1 байт, shortвізьміть 2 байти і intвізьміть чотири байти, тому що в масивах потрібно вирівняти лише початок і, можливо, кінець його. Це змінить випадок, якщо ви хочете використовувати, наприклад System.arraycopy(), тоді ви дійсно відзначите різницю в продуктивності.


1
Факт забави: якщо ви використовуєте остаточні модифікатори для обох значень, це спрацює. :)
Олександр

7

Тому що арифметичні операції легші при використанні цілих чисел порівняно з шортами. Припустимо, що константи справді змодельовані shortзначеннями. Тоді вам доведеться використовувати API таким чином:

short month = Calendar.JUNE;
month = month + (short) 1; // is july

Помітьте чітке кастинг. Короткі значення неявно піднімаються до intзначень, коли вони використовуються в арифметичних операціях. (На стеку операндів шорти навіть виражаються як ints.) Це було б досить громіздко, тому intзначення часто віддають перевагу константам.

Порівняно з цим, посилення ефективності зберігання є мінімальним, оскільки існує лише фіксована кількість таких констант. Ми говоримо про 40 констант. Зміна сховища на intна shortзахистить вас 40 * 16 bit = 80 byte. Дивіться цю відповідь для подальшого ознайомлення.


5

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

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

Зважити плюси і мінуси дуже просто: це погана філософія.


4

Складність проектування віртуальної машини - це залежність від кількості видів операцій, які вона може виконувати. Простіше мати чотири реалізації інструкції типу "помножити" - по одній для 32-бітових цілих чисел, 64-розрядних цілих чисел, 32-бітної плаваючої точки і 64-бітної плаваючої точки - ніж до того ж мати вище, версії і для менших числових типів. Більш цікавим дизайнерським питанням є те, чому повинно бути чотири типи, а не менше (виконувати всі цілі обчислення з 64-бітовими цілими числами та / або робити всі обчислення з плаваючою комою з 64-бітовими значеннями з плаваючою комою). Причина використання 32-бітних цілих чисел полягає в тому, що, як очікувалося, Java буде працювати на багатьох платформах, де 32-бітні типи можна буде діяти так само швидко, як 16-бітні або 8-бітні типи, але операції з 64-бітовими типами будуть помітні повільніше.лише 32-розрядні типи.

Що стосується проведення обчислень з плаваючою комою на 32-бітних значеннях, переваги трохи менш зрозумілі. Є деякі платформи, на яких подобається обчисленняfloat a=b+c+d;можна було б виконати найшвидше, перетворивши всі операнди у тип більш високої точності, додавши їх, а потім перетворив результат на 32-бітове число з плаваючою комою для зберігання. Є й інші платформи, де було б ефективніше виконувати всі обчислення, використовуючи 32-бітні значення з плаваючою комою. Творці Java вирішили, що від усіх платформ потрібно робити так само, і вони повинні надавати перевагу апаратним платформам, для яких 32-розрядні обчислення з плаваючою комою швидші, ніж довші, хоча цей ПК сильно погіршив швидкість ПК точність математики з плаваючою комою на типовому ПК, а також на багатьох машинах без одиниць з плаваючою комою. Зауважте, btw, що залежно від значень b, c і d, використовуючи проміжні обчислення більш високої точності при обчисленні виразів, подібних до вищезгаданихfloat a=b+c+d;іноді дасть результати, які є значно точнішими, ніж це було б досягнуто для всіх проміжних операндів, які були обчислені з floatточністю, але іноді дадуть значення, яке є мінімальним менш точним. У будь-якому випадку, Sun вирішила, що все слід робити так само, і вони вирішили використовувати значення мінімальної точності float.

Зауважте, що основні переваги менших типів даних стають очевидними, коли велика кількість їх зберігається разом у масиві; навіть якщо не було переваги мати індивідуальні змінні типи менше 64 біт, варто мати масиви, які можуть зберігати менші значення більш компактно; наявність локальної змінної byteшвидше, ніж longекономить сім байтів; маючи масив в 1 000 000 чисел, утримуйте кожне число, byteа не якlongхвилі 7 000 000 байт. Оскільки для кожного типу масиву потрібно підтримувати лише кілька операцій (найголовніше - прочитати один елемент, зберегти один елемент, скопіювати діапазон елементів у масив або скопіювати діапазон елементів з одного масиву в інший), додаткова складність мати більше Типи масивів не такі серйозні, як складність наявності більше типів дискретних числових значень, що безпосередньо використовуються.


2

Насправді, була б невелика перевага. Якщо у вас є

class MyTimeAndDayOfWeek {
    byte dayOfWeek;
    byte hour;
    byte minute;
    byte second;
}

то для типового JVM йому потрібно стільки ж місця, скільки класу, що містить одиницю int. Споживання пам'яті округляється до наступного кратного 8 або 16 байт (IIRC, це можна настроювати), тому випадки, коли є реальна економія, досить рідкісні.

Цей клас було б легше використовувати, якби відповідні Calendarметоди повернули a byte. Але таких Calendarметодів немає , лише get(int)вони повинні повертати intчерез інші поля. Кожна операція на менших типах просувається до int, тому вам потрібно багато кастингу.

Швидше за все, ви або відмовитесь і перейдете на intабо записуєте такі сетери

void setDayOfWeek(int dayOfWeek) {
    this.dayOfWeek = checkedCastToByte(dayOfWeek);
}

Тоді тип DAY_OF_WEEKне має значення.

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