Чому у Java є примітиви для різних розмірів?


20

У Java є примітивні типи для byte, short, intі longі те ж саме для floatі double. Чому потрібно, щоб людина встановила, скільки байтів слід використовувати для примітивного значення? Не вдалося розмір просто визначити динамічно залежно від того, наскільки велике було передане число?

Я думаю про дві причини:

  1. Динамічне встановлення розміру даних означало б, що він повинен мати можливість також динамічно змінюватися. Це потенційно може спричинити проблеми з роботою?
  2. Можливо, програміст не хотів би, щоб хтось міг використовувати більшу кількість, ніж певний розмір, і це дозволяє їм обмежити це.

Я все ще думаю, що можна було б багато чого заробити простим використанням синглу intта floatтипу, чи була конкретна причина, чому Java вирішила не йти цим маршрутом?


4
До речей, я додам, що це питання пов'язане з питанням, на яке дослідники-компілятори прагнуть відповісти .
rwong

Отже, якщо ви додали до числа, ви думаєте, що тип слід динамічно змінювати? Я навіть хочу, щоб тип змінився? Якщо число ініціалізовано як intUnknown alpha = a + b; Ви розумієте, що було б трохи важко для компілятора. Чому це специфічно для java?
папараццо

@Paparazzi Існують існуючі мови програмування та середовища виконання (компілятори, інтерпретатори тощо), які зберігатимуть ціле число динамічної ширини залежно від того, наскільки велике фактичне значення (наприклад, результат операції додавання). Наслідки полягають у тому, що: код, який потрібно виконати на процесорі, ускладнюється; розмір цілого числа стає динамічним; зчитування динамічного цілого ширини з пам'яті може зажадати більше однієї поїздки; структури (об'єкти) та масиви, які містять цілі числа динамічної ширини всередині своїх полів / елементів, також можуть мати динамічний розмір.
rwong

1
@tofro я не розумію. Просто надішліть номер у будь-якому форматі, який вам подобається: десятковий, двійковий тощо. Серіалізація - це абсолютно ортогональне питання.
садок

1
@gardenhead Це ортогонально, так, але ... просто розгляньте випадок, коли ви хочете спілкуватися між сервером, написаним на Java, і клієнтом, написаним на C. Звичайно, це можна вирішити за допомогою спеціальної інфраструктури. Наприклад, є такі речі, як developers.google.com/protocol-buffers . Але це великий кувалд для маленької гайки передачі цілого числа по мережі. (Я знаю, це не є вагомим аргументом, але, можливо, слід враховувати - обговорення деталей виходить за рамки коментарів).
Marco13

Відповіді:


16

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

Альтернативи

Звичайно, можна (і досить просто) скласти мову програмування, яка має лише один тип натуральних чисел nat. Практично всі мови програмування, що використовуються для академічного вивчення (наприклад, PCF, System F), мають цей тип єдиного числа, який є більш елегантним рішенням, як ви думали. Але мовний дизайн на практиці не лише в елегантності; ми також повинні враховувати ефективність (ступінь, в якій розглядається продуктивність, залежить від передбачуваного застосування мови). Спектакль містить як часові, так і просторові обмеження.

Космічні обмеження

Дозволяючи програмісту вибирати кількість байтів наперед, це може заощадити місце в обмеженій пам'яті програмах. Якщо всі ваші числа будуть менше 256, то ви можете використовувати в 8 разів більше bytes, ніж longабо використовувати збережений сховище для складніших об'єктів. Стандартний розробник програм Java не повинен турбуватися про ці обмеження, але вони все-таки придумуються.

Ефективність

Навіть якщо ми ігноруємо простір, ми все ще обмежені процесором, який має лише інструкції, що працюють на фіксованій кількості байтів (8 байт на 64-бітній архітектурі). Це означає, що навіть надання єдиного 8-байтового longтипу зробило б реалізацію мови значно простішою, ніж наявність необмеженого типу натурального числа, маючи можливість відображати арифметичні операції безпосередньо в одній базовій інструкції процесора. Якщо ви дозволите програмісту використовувати довільно великі числа, то одна арифметична операція повинна бути відображена на послідовність складних інструкцій машини, що уповільнить програму. Це пункт (1), який ви виховували.

Типи з плаваючою комою

Поки що обговорення стосувалося лише цілих чисел. Типи з плаваючою комою - складний звір, з надзвичайно тонкою семантикою та крайовими корпусами. Таким чином, навіть якщо ми могли б легко замінити int, long, shortі byteза допомогою одного natтипу, то не ясно , що тип чисел з плаваючою точкою , навіть є . Очевидно, вони не є реальними числами, оскільки реальні числа не можуть існувати в мові програмування. Вони теж не зовсім раціональні числа (хоча, за бажанням, створити раціональний тип прямолінійно). В основному, IEEE вирішив вирішити якоюсь мірою приблизні реальні цифри, і всі мови (і програмісти) були з ними до цього часу.

Нарешті:

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

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


2
справжній ключ до того, що у нас є плавці, - це те, що ми виділили для них обладнання
jk.

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

3
Енуми не рівноцінні цілим числам. Енуми - це лише режим використання типів сум. Те, що деякі мови прозоро кодують переліки як цілі числа, є мовним недоліком, а не функцією, що використовується.
садок

1
Я не знайомий з Адою. Чи можу я обмежити цілі числа будь-яким типом, наприклад type my_type = int (7, 2343)?
садок

1
Так. Синтаксис був би: тип my_type - діапазон 7..2343
Devsman

9

Причина дуже проста: ефективність . Багаторазово.

  1. Рідні типи даних: Чим ближче типи даних мови відповідають основним типам даних апаратного забезпечення, тим ефективнішою вважається мова. (Не в тому сенсі, що ваші програми обов'язково будуть ефективними, але в тому сенсі, що ви можете, якщо ви дійсно знаєте, чим займаєтесь, написати код, який буде працювати так само ефективно, як і апаратне забезпечення може.) Java відповідають байтам, словам, подвійним словам і чотирьом словам найпопулярніших апаратних засобів там. Це найефективніший шлях.

  2. Необгрунтована накладні витрати на 32-розрядні системи: Якби було прийнято рішення зіставити все на 64-бітну фіксовану величину, це призвело б до величезного штрафу для 32-бітових архітектур, яким потрібно значно більше тактових годин, щоб виконати 64- бітова операція, ніж 32-бітна операція.

  3. Марнотратство пам’яті: Є багато обладнання, яке не надто прискіпливе до вирівнювання пам’яті (приклади цього є архітектури Intel x86 та x64), тому масив із 100 байтів на цьому апараті може займати лише 100 байт пам’яті. Однак якщо у вас більше немає байта, і вам доведеться використовувати довгий замість цього, той же масив займе на порядок більше пам’яті. І байтові масиви дуже поширені.

  4. Розрахунок розмірів чисел: Ваше уявлення про динамічне визначення розміру цілого числа залежно від того, наскільки велике число було передано, є надто спрощеним; немає єдиного пункту "передачі" числа; обчислення того, наскільки потрібно велике число, потрібно виконувати під час виконання кожної операції, яка може потребувати результату більшого розміру: кожен раз, коли ви збільшуєте число, кожен раз, коли ви додаєте два числа, кожен раз, коли ви множите два числа тощо.

  5. Операції з номерами різного розміру: Згодом, коли числа, що потенційно різного розміру плавають навколо пам’яті, ускладнюватимуть усі операції: Навіть для простого порівняння двох чисел час виконання спочатку повинен перевірити, чи обидва числа, які слід порівнювати, однакові розмір, а якщо ні, розмір меншого розміру відповідає розміру більшого.

  6. Операції, що вимагають конкретних розмірів операндів. Окремі бітові операції покладаються на ціле число, що має певний розмір. Не маючи заздалегідь визначеного конкретного розміру, ці операції повинні бути емульовані.

  7. Напруженість поліморфізму: Зміна розміру числа під час виконання по суті означає, що воно повинно бути поліморфним. Це в свою чергу означає, що він не може бути примітивом фіксованого розміру, виділеним на стеку, він повинен бути об'єктом, виділеним на купі. Це жахливо неефективно. (Перечитайте вище №1.)


6

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

З точки зору дизайну мови

  • Безумовно, можна розробити та впровадити мову програмування та його середовище виконання, яке автоматично розмістить результати цілочисельних операцій, що не відповідають ширині машини.
  • Вибір дизайнера мови, чи робити такі цілі числа динамічної ширини типовим цілим числом для цієї мови.
  • Однак дизайнер мови повинен враховувати наступні недоліки:
    • Процесору доведеться виконати більше коду, що потребує більше часу. Однак можна оптимізувати найчастіший випадок, коли ціле число вписується в одне машинне слово. Див. Представлення тегів указівниками .
    • Розмір цього цілого числа стає динамічним.
    • Читання цілого числа динамічної ширини з пам'яті може зажадати більше однієї поїздки.
    • Структури (об'єкти) та масиви, які містять динамічні цілі ширини у своїх полях / елементах, матимуть також загальний (зайнятий) розмір, який також є динамічним.

Історичні причини

Про це вже йдеться у статті Вікіпедії про історію Яви, а також коротко обговорюється у відповіді Marco13 .

Я зазначу, що:

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

Причини ефективності

Коли значення має ефективність?

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

Ефективність зберігання (в пам'яті або на диску)

  • Колись пам'ять комп’ютера була дефіцитним ресурсом. У ті старі часи розмір даних додатків, які могли оброблятися комп'ютером, був обмежений обсягом пам'яті комп'ютера, хоча це, безсумнівно, можна було б обробити за допомогою розумного програмування (що коштуватиме дорожче).

Ефективність виконання (всередині процесора або між процесором і пам'яттю)

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

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

  • Ці потреби часто виникають при розробці бібліотек програмного забезпечення, включаючи власні стандартні бібліотеки мови. Нижче наведено кілька таких випадків.

Взаємодія

  • Найчастіше мови програмування вищого рівня потребують взаємодії з операційною системою або фрагментами програмного забезпечення (бібліотеками), написаними іншими мовами нижчого рівня. Ці мови нижчого рівня часто спілкуються за допомогою "структури" , що є жорсткою специфікацією компонування пам'яті запису, що складається з полів різних типів.
  • Наприклад, мові вищого рівня може знадобитися вказати, що певна зовнішня функція приймає charмасив розміром 256. (Приклад.)
  • Деякі абстракції, які використовуються операційними системами та файловими системами, вимагають використання потоків байтів.
  • Деякі мови програмування вирішують надати функції утиліти (наприклад BitConverter) для впакування та розпакування вузьких цілих чисел у бітові потоки та байтові потоки.
  • У цих випадках більш вузькі цілі типи не повинні бути примітивним типом, вбудованим у мову. Натомість вони можуть надаватися у вигляді бібліотеки.

Обробка струн

  • Є програми, основна мета дизайну яких - маніпулювати струнами. Таким чином, ефективність обробки рядків важлива для таких типів додатків.

Обробка файлів у форматі

  • Дуже багато форматів файлів були розроблені з C-подібним мисленням. Як таке, поширене використання полів вузької ширини.

Бажаність, якість програмного забезпечення та відповідальність програміста

  • Для багатьох типів програм автоматичне розширення цілих чисел насправді не є бажаною особливістю. Ні насичення, ні обертання (модуль).
  • Багато типів програм виграють від явного конкретизації програміста найбільших дозволених значень у різних критичних точках програмного забезпечення, наприклад, на рівні API.

Розглянемо наступний сценарій.

  • API програмного забезпечення приймає запит JSON. Запит містить масив дочірніх запитів. Весь запит JSON може бути стислий за допомогою алгоритму Deflate.
  • Зловмисний користувач створює запит JSON, що містить мільярд запитів дітей. Усі запити дітей однакові; шкідливий користувач має намір систему спалювати деякі цикли процесора, виконуючи марну роботу. Завдяки стисненню ці однакові запити дітей стискаються до дуже малого загального розміру.
  • Очевидно, що попередньо визначений ліміт на стислий розмір даних недостатній. Натомість API потрібно ввести заздалегідь визначений ліміт на кількість дочірніх запитів, які можуть міститися в ньому, та / або заздалегідь визначений ліміт на спущений розмір даних.

Часто для цього потрібно розробляти програмне забезпечення, яке може безпечно масштабувати багато порядків, зі збільшенням складності. Це не відбувається автоматично, навіть якщо проблема цілого переповнення усунена. Це доходить до повного кола, що відповідає мовній мовній перспективі: часто програмне забезпечення, яке відмовляється виконувати роботу, коли відбувається ненавмисне ціле переповнення (шляхом помилки чи винятку), є кращим, ніж програмне забезпечення, яке автоматично відповідає астрономічно великим операціям.

Це означає перспективу ОП,

Чому потрібно, щоб людина встановила, скільки байтів слід використовувати для примітивного значення?

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


2

Все це відбувається з обладнання.

Байт - це найменша адресна пам'ять на більшості апаратних засобів.

Кожен згаданих вами типів складається з декількох байтів.

Байт - 8 біт. За допомогою цього ви можете виразити 8 булевих, але не можете шукати лише один за одним. Ви адресу 1, ви звертаєтесь до всіх 8.

І раніше це було так просто, але тоді ми перейшли з 8-бітної шини до 16, 32, а тепер 64-бітної шини.

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

Зіткнувшись з цим обладнанням, мовні дизайнери вирішили дозволити нам вибирати типи, які дозволяли нам вибирати типи, що відповідають обладнанням.

Ви можете стверджувати, що таку деталь можна і потрібно абстрагувати, особливо мовою, яка спрямована на будь-яке обладнання. Це може мати приховані проблеми щодо продуктивності, але ви можете мати рацію. Просто так не сталося.

Java насправді намагається це зробити. Байти автоматично перетворюються на Інти. Факт, який призведе до того, що ви змусите вас вперше, коли ви спробуєте виконати будь-яку серйозну роботу, що змінює біт.

То чому б це не спрацювало добре?

Велика продажна точка Java в той день, коли ви могли сісти за відомим хорошим алгоритмом С, наберіть його на Java і з незначними налаштуваннями він буде працювати. А C дуже близький до обладнання.

Збереження цього і зменшення розміру від цілісних типів просто не працювало разом.

Так вони могли мати. Вони просто ні.

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

Це дійсне мислення. Існують методи для цього. Функція затиску для одного. Мова може зайти так далеко, щоб вивести довільні межі у їхні види. І коли ці межі відомі під час компіляції, це дозволило б оптимізувати спосіб зберігання цих чисел.

Java просто не така мова.


" Мова може зайти так далеко, щоб перетворити довільні межі у їхні типи ". І справді Паскаль має форму цього з підрядними типами.
Пітер Тейлор

1

Мабуть, одна важлива причина, чому ці види існують на Java, - це проста і неприємна неприємність:

Ц і С ++ також мали ці типи!

Хоча важко надати доказ того, що це причина, є принаймні деякі вагомі докази: Специфікація мови дуба (версія 0.2) містить такий уривок:

3.1 Цілі типи

Цілі цілі в мові Дуба схожі з тими на C і C ++, за двома винятками: всі цілі типи не залежать від машин, а деякі традиційні визначення були змінені, щоб відобразити зміни у світі з моменту введення C. Чотири цілі типи мають ширину 8, 16, 32 та 64 біт і підписуються, якщо не встановлено префіксацію unsignedмодифікатором.

Тож питання може зводитися до:

Чому короткі, int і давно винайдені в C?

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

Найголовніші причини, про які я можу подумати, - це

  • Байт - це найменший адресний блок пам'яті (як уже згадувалося в CandiedOrange). A byte- це елементарний будівельний блок даних, який можна прочитати з файлу або по мережі. Деяке явне уявлення про це повинно існувати (і воно існує в більшості мов, навіть коли воно іноді маскується).

  • Це правда, що на практиці було б доцільно представляти всі поля та локальні змінні, використовуючи один тип, і називати цей тип int. Існує пов'язане питання щодо цього в stackoverflow: Чому Java API використовує int замість короткого або байтового? . Як я вже згадував у своїй відповіді, одне обґрунтування наявності менших типів ( byteі short) полягає в тому, що ви можете створювати масиви таких типів: Java має представлення масивів, які все ще досить "близькі до апаратних засобів". На відміну від інших мов (і на відміну від масивів об'єктів, як Integer[n]масив), int[n]масив не є сукупністю посилань, де значення розкидані по всій купі. Натомість будена практиці представляти собою послідовний блок n*4байтів - один шматок пам'яті з відомим розміром і макетом даних. Коли у вас є вибір для зберігання 1000 байтів у колекції об'єктів цілочисельних значень довільного розміру або в byte[1000](які займають 1000 байт), останні можуть дійсно зберегти деяку кількість пам'яті. (Деякі інші переваги цього можуть бути більш тонкими і стають очевидними лише при взаємодії Java з рідними бібліотеками)


Щодо питань, про які ви спеціально запитали:

Не вдалося розмір просто визначити динамічно залежно від того, наскільки велике було передане число?

Динамічне встановлення розміру даних означало б, що він повинен мати можливість також динамічно змінюватися. Це потенційно може спричинити проблеми з роботою?

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


Бічні нотатки:

  • Можливо, ви задумалися про unsignedмодифікатор, про який згадували в специфікації Дуба. Насправді він також містить зауваження: " unsignedще не реалізовано; це може бути ніколи". . І вони мали рацію.

  • Крім того, що вам цікаво, чому C / C ++ взагалі мали ці цілі типи, ви можете задатися питанням, чому вони так збентежили їх, що ніколи не знаєте, скільки біт intмає. Виправдання для цього, як правило, пов'язані з продуктивністю, і їх можна шукати в іншому місці.


0

Це, безумовно, показує, що ви ще не вчили про продуктивність та архітектуру.

  • По-перше, не кожен процесор може впоратися з великими типами, тому вам потрібно знати обмеження та працювати з цим.
  • По-друге, менші типи означають більшу продуктивність при виконанні операцій.
  • Також розмір має значення, якщо вам потрібно зберігати дані у файлі чи базі даних, розмір вплине як на продуктивність, так і на кінцевий розмір усіх даних, наприклад, скажімо, у вас є таблиця з 15 стовпцями, і ви закінчуєте декілька мільйони записів. Різниця між обраним невеликим необхідним розміром для кожного стовпця або обраним просто найбільшим типом, це буде різниця можливих Гіг даних та часу у виконанні операцій.
  • Крім того, він застосовується в складних обчисленнях, де розмір оброблюваних даних матиме великий вплив, як, наприклад, в іграх.

Ігноруючи важливість розміру даних завжди вражає ефективність, ви повинні використовувати стільки ресурсів, скільки необхідно, але не більше, завжди!

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


0

Є кілька вагомих причин

(1) хоча зберігання одного байтового змінного вірша один довгий є незначним, зберігання мільйонів у масиві є дуже значним.

(2) Арифметика «нативного обладнання» на основі конкретних цілих розмірів може бути набагато ефективнішою, а для деяких алгоритмів на деяких платформах це може бути важливо.

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