Dlaczego Java API używa int zamiast short lub byte?

137

Dlaczego Java API używa int, kiedy shortlub w ogóle bytebyłoby wystarczające?

Przykład: DAY_OF_WEEKpole w klasie Calendarużywa int.

Jeśli różnica jest zbyt minimalna, to dlaczego te typy danych ( short, int) w ogóle istnieją?

Willi Mentzel
źródło

Odpowiedzi:

166

Niektóre z powodów zostały już wskazane. Na przykład fakt, że „... (prawie) wszystkie operacje na bajcie, short będą promować te prymitywy do int” . Jednak oczywiste następne pytanie brzmiałoby : DLACZEGO te typy są promowane int?

A więc, aby przejść o jeden poziom głębiej: odpowiedź może być po prostu związana z zestawem instrukcji wirtualnej maszyny języka Java. Jak podsumowano w tabeli w specyfikacji wirtualnej maszyny języka Java , wszystkie integralne operacje arytmetyczne, takie jak dodawanie, dzielenie i inne, są dostępne tylko dla typu inti typu long, a nie dla mniejszych typów.

(Na marginesie: mniejsze typy ( bytei short) są w zasadzie przeznaczone tylko dla tablic . Tablica taka new byte[1000]zajmie 1000 bajtów, a tablica taka new int[1000]zajmie 4000 bajtów)

Teraz, oczywiście, można by powiedzieć, że „... oczywistym następnym pytaniem byłoby: DLACZEGO te instrukcje są oferowane tylko dla int(i long)?” .

Jeden powód jest wymieniony w specyfikacji JVM wspomnianej powyżej:

Gdyby każda wpisana instrukcja obsługiwała wszystkie typy danych czasu wykonywania wirtualnej maszyny języka Java, byłoby więcej instrukcji niż można by przedstawić w bajcie

Ponadto wirtualną maszynę Javy można uznać za abstrakcję prawdziwego procesora. Wprowadzenie dedykowanej arytmetycznej jednostki logicznej dla mniejszych typów nie byłoby warte wysiłku: wymagałoby to dodatkowych tranzystorów, ale nadal mogłoby wykonać tylko jeden dodatek w jednym cyklu zegara. Dominującą architekturą podczas projektowania maszyny JVM była architektura 32-bitowa, w sam raz dla wersji 32-bitowej int. (Operacje wymagające longwartości 64-bitowej są realizowane jako przypadek specjalny).

(Uwaga: ostatni akapit jest nieco uproszczony, biorąc pod uwagę możliwą wektoryzację itp., Ale powinien dać podstawowy pomysł bez zagłębiania się w tematy dotyczące projektowania procesorów)


EDYCJA: Krótki dodatek, skupiający się na przykładzie z pytania, ale w bardziej ogólnym sensie: można również zapytać, czy nie byłoby korzystne przechowywanie pól przy użyciu mniejszych typów. Na przykład można pomyśleć, że pamięć można zapisać, przechowując Calendar.DAY_OF_WEEKjako plik byte. Ale tutaj pojawia się format pliku klasy Java: wszystkie pola w pliku klasy zajmują co najmniej jeden „slot”, który ma rozmiar jeden int(32 bity). („Szerokie” pola doublei longzajmują dwa pola ). Tak jawne zadeklarowanie pola jako shortlub też bytenie zapisałoby żadnej pamięci.

Marco13
źródło
Wydaje mi się, że logika, dlaczego operandy są promowane do int, jest również związana z uzasadnieniem używanym w C i C ++
Shafik Yaghmour
@ Marco13 "Tak jawne zadeklarowanie pola jako krótkiego lub bajtowego również nie zapisałoby żadnej pamięci." czy to prawda? Nie sądzę, że to prawda.
ACV,
@ACV Ściśle mówiąc, implementacja mogłaby zdecydować się na przechowywanie bardziej zwartej formy, ale format, który jest ujawniany „wirtualnie” (tj. Przez maszynę wirtualną) będzie traktował wartości jako mające co najmniej rozmiar int. Jeśli masz odniesienie do innej implementacji, zaktualizuję odpowiedź i odpowiednio wstawię link.
Marco13
40

(Prawie) Wszystkie operacje na byte, shortbędą promować je int, np. Nie możesz napisać:

short x = 1;
short y = 2;

short z = x + y; //error

Arytmetyka jest łatwiejsza i prosta w użyciu int, nie ma potrzeby rzucania.

Pod względem przestrzeni robi to bardzo małą różnicę. bytei shortskomplikowałoby sprawę, nie sądzę, aby ta mikro optymalizacja była tego warta, ponieważ mówimy o stałej liczbie zmiennych.

bytejest przydatna i przydatna podczas programowania urządzeń wbudowanych lub korzystania z plików / sieci. Również te prymitywy są ograniczone, a co, jeśli obliczenia mogą przekroczyć ich granice w przyszłości? Spróbuj pomyśleć o rozszerzeniu Calendarklasy, które mogłoby spowodować powstanie większej liczby.

Należy również pamiętać, że w ciągu 64-bitowych procesorów, mieszkańcy zostaną zapisane w rejestrach i nie będzie wykorzystywać żadnych zasobów, więc przy użyciu int, shorta inne prymitywy nie będzie żadnej różnicy w ogóle. Co więcej, wiele implementacji Java dopasowuje zmienne * (i obiekty).


* byte i shortzajmują taką samą przestrzeń, jak intgdyby były zmiennymi lokalnymi, zmiennymi klas , a nawet zmiennymi instancji . Czemu? Ponieważ w (większości) systemach komputerowych adresy zmiennych są wyrównane , więc na przykład jeśli używasz jednego bajtu, w rzeczywistości otrzymasz dwa bajty - jeden na samą zmienną, a drugi na wypełnienie.

Z drugiej strony, w tablicach byteweź 1 bajt, shortweź 2 bajty i intweź 4 bajty, ponieważ w tablicach tylko początek i być może koniec muszą być wyrównane. Będzie to miało znaczenie, jeśli chcesz na przykład użyć System.arraycopy(), wtedy naprawdę zauważysz różnicę w wydajności.

Maroun
źródło
1
Ciekawostka: jeśli użyjesz końcowych modyfikatorów dla obu wartości, zadziała. :)
alexander
7

Ponieważ operacje arytmetyczne są łatwiejsze w przypadku używania liczb całkowitych w porównaniu do krótkich. Załóżmy, że stałe były rzeczywiście modelowane przez shortwartości. Wtedy musiałbyś użyć API w ten sposób:

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

Zwróć uwagę na jawne odlewanie. Krótkie wartości są niejawnie promowane do intwartości, gdy są używane w operacjach arytmetycznych. (Na stosie operandów krótkie są nawet wyrażane jako liczby całkowite). Byłoby to dość kłopotliwe w użyciu, dlatego intczęsto preferowane są wartości dla stałych.

W porównaniu z tym wzrost wydajności pamięci jest minimalny, ponieważ istnieje tylko ustalona liczba takich stałych. Mówimy o 40 stałych. Zmiana ich przechowywania z intna shortbyłaby bezpieczna 40 * 16 bit = 80 byte. Zobacz tę odpowiedź w celu uzyskania dalszych informacji.

Rafael Winterhalter
źródło
5

Gdybyś zastosował filozofię, w której stałe całkowe są przechowywane w najmniejszym typie, do którego pasują, Java miałby poważny problem: ilekroć programiści piszą kod przy użyciu stałych całkowitych, muszą zwracać szczególną uwagę na swój kod, aby sprawdzić, czy typ Stałe mają znaczenie, a jeśli tak, wyszukaj typ w dokumentacji i / lub wykonaj dowolne konwersje typów.

Więc teraz, kiedy nakreśliliśmy poważny problem, jakie korzyści możesz osiągnąć dzięki tej filozofii? Nie zdziwiłbym się, gdyby jedynym efektem tej zmiany, który można było zaobserwować w czasie wykonywania, byłby typ, jaki uzyskuje się, patrząc na stałą poprzez refleksję. (i oczywiście wszelkie błędy wprowadzane przez leniwych / nieświadomych programistów niepoprawnie uwzględniających typy stałych)

Ważenie za i przeciw jest bardzo łatwe: to zła filozofia.


źródło
4

Złożoność projektu maszyny wirtualnej jest funkcją tego, ile rodzajów operacji może ona wykonywać. Łatwiej jest mieć cztery implementacje instrukcji, takich jak „mnożenie” - po jednej dla 32-bitowej liczby całkowitej, 64-bitowej liczby całkowitej, 32-bitowej liczby zmiennoprzecinkowej i 64-bitowej liczby zmiennoprzecinkowej - niż dodatkowo do powyższego, wersje dla mniejszych typów numerycznych. Bardziej interesującym pytaniem projektowym jest, dlaczego powinny istnieć cztery typy, a nie mniej (wykonywanie wszystkich obliczeń całkowitych z 64-bitowymi liczbami całkowitymi i / lub wykonywanie wszystkich obliczeń zmiennoprzecinkowych z 64-bitowymi wartościami zmiennoprzecinkowymi). Powodem używania 32-bitowych liczb całkowitych jest to, że oczekiwano, że Java będzie działać na wielu platformach, na których 32-bitowe typy mogą być obsługiwane równie szybko jak 16-bitowe lub 8-bitowe, ale operacje na typach 64-bitowych byłyby zauważalne wolniej.tylko z typami 32-bitowymi.

Jeśli chodzi o wykonywanie obliczeń zmiennoprzecinkowych na wartościach 32-bitowych, zalety są nieco mniej oczywiste. Istnieje kilka platform, na których obliczenia takie jakfloat a=b+c+d;można wykonać najszybciej, konwertując wszystkie operandy na typ o większej precyzji, dodając je, a następnie konwertując wynik z powrotem na 32-bitową liczbę zmiennoprzecinkową w celu przechowywania. Istnieją inne platformy, na których wykonywanie wszystkich obliczeń przy użyciu 32-bitowych wartości zmiennoprzecinkowych byłoby bardziej wydajne. Twórcy Javy zdecydowali, że wszystkie platformy powinny działać w ten sam sposób i że powinni faworyzować platformy sprzętowe, dla których 32-bitowe obliczenia zmiennoprzecinkowe są szybsze niż dłuższe, mimo że ten poważnie zdegradowany komputer PC zarówno pod względem szybkości oraz precyzja obliczeń zmiennoprzecinkowych na typowym komputerze PC, a także na wielu komputerach bez jednostek zmiennoprzecinkowych. Zwróć uwagę, że w zależności od wartości b, c i d, używając obliczeń pośrednich o wyższej precyzji podczas obliczania wyrażeń, takich jak wyżej wymienionefloat a=b+c+d;czasami daje wyniki, które są znacznie dokładniejsze niż w przypadku wszystkich pośrednich operandów obliczonych z floatprecyzją, ale czasami daje wartość, która jest odrobinę mniej dokładna. W każdym razie Sun zdecydował, że wszystko należy zrobić w ten sam sposób i zdecydował się na użycie floatwartości o minimalnej precyzji .

Zwróć uwagę, że podstawowe zalety mniejszych typów danych stają się widoczne, gdy duża ich liczba jest przechowywana razem w tablicy; nawet jeśli indywidualne zmienne o typach mniejszych niż 64-bitowe nie przynosiłyby korzyści, warto mieć tablice, które mogą przechowywać mniejsze wartości w bardziej zwarty sposób; posiadanie zmiennej lokalnej jako a bytezamiast an longoszczędza siedem bajtów; posiadanie tablicy 1 000 000 liczb oznacza każdą liczbę jako bytezamiast alongfale 7 000 000 bajtów. Ponieważ każdy typ tablicy musi obsługiwać tylko kilka operacji (w szczególności odczytywanie jednego elementu, przechowywanie jednego elementu, kopiowanie zakresu elementów w tablicy lub kopiowanie zakresu elementów z jednej tablicy do drugiej), dodatkowa złożoność posiadania więcej typy tablic nie są tak poważne, jak złożoność posiadania większej liczby typów dyskretnych wartości liczbowych, które można bezpośrednio wykorzystać.

superkat
źródło
2

Właściwie byłaby niewielka przewaga. Jeśli masz

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

wtedy w typowej maszynie JVM zajmuje tyle samo miejsca, co klasa zawierająca pojedynczy plik int. Zużycie pamięci jest zaokrąglane do kolejnej wielokrotności 8 lub 16 bajtów (IIRC, to konfigurowalne), więc przypadki, w których są rzeczywiste oszczędności, są raczej rzadkie.

Ta klasa byłaby nieco łatwiejsza w użyciu, gdyby odpowiednie Calendarmetody zwracały plik byte. Ale nie ma takich Calendarmetod, get(int)które tylko muszą zwrócić z intpowodu innych pól. Każda operacja na mniejszych typach awansuje do int, więc potrzebujesz dużo rzucania.

Najprawdopodobniej zrezygnujesz i przełączysz się na intsetery, takie jak

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

Wtedy i tak DAY_OF_WEEKnie ma znaczenia.

maaartinus
źródło