Pourquoi l'API Java utilise-t-elle int au lieu de short ou byte?

137

Pourquoi l'API Java utilise-t-elle int, quand shortou même byteserait-elle suffisante?

Exemple: le DAY_OF_WEEKchamp de la classe Calendarutilise int.

Si la différence est trop minime, alors pourquoi ces types de données ( short, int) existent-ils?

Willi Mentzel
la source

Réponses:

166

Certaines des raisons ont déjà été évoquées. Par exemple, le fait que "... (Presque) Toutes les opérations sur octet, short promouvra ces primitives en int" . Cependant, la prochaine question évidente serait: POURQUOI ces types sont-ils promus int?

Donc, pour aller un peu plus loin: la réponse peut simplement être liée au jeu d'instructions de la machine virtuelle Java. Comme résumé dans le tableau de la spécification de la machine virtuelle Java , toutes les opérations arithmétiques intégrales, telles que l'ajout, la division et autres, ne sont disponibles que pour le type intet le type long, et non pour les types plus petits.

(Un aparté: les types plus petits ( byteet short) sont essentiellement destinés uniquement aux tableaux . Un tableau comme new byte[1000]prendra 1000 octets, et un tableau comme new int[1000]prendra 4000 octets)

Maintenant, bien sûr, on pourrait dire que "... la prochaine question évidente serait: POURQUOI ces instructions sont-elles proposées uniquement pour int(et long)?" .

Une raison est mentionnée dans la spécification JVM mentionnée ci-dessus:

Si chaque instruction saisie prenait en charge tous les types de données d'exécution de la machine virtuelle Java, il y aurait plus d'instructions que ce qui pourrait être représenté dans un octet

De plus, la machine virtuelle Java peut être considérée comme une abstraction d'un processeur réel. Et l'introduction d'une unité logique arithmétique dédiée pour les types plus petits ne vaudrait pas la peine: elle aurait besoin de transistors supplémentaires, mais elle ne pourrait toujours exécuter qu'une seule addition en un cycle d'horloge. L'architecture dominante lors de la conception de la JVM était de 32 bits, juste pour un 32 bits int. (Les opérations qui impliquent une longvaleur de 64 bits sont implémentées comme cas particulier).

(Remarque: le dernier paragraphe est un peu simplifié, considérant la vectorisation possible, etc., mais devrait donner une idée de base sans plonger trop profondément dans les sujets de conception de processeur)


EDIT: Un petit addendum, se concentrant sur l'exemple de la question, mais dans un sens plus général: On pourrait également se demander s'il ne serait pas avantageux de stocker des champs en utilisant les types plus petits. Par exemple, on pourrait penser que la mémoire pourrait être sauvegardée en stockant Calendar.DAY_OF_WEEKsous forme de fichier byte. Mais ici, le format de fichier de classe Java entre en jeu: tous les champs d'un fichier de classe occupent au moins un "emplacement", qui a la taille d'un int(32 bits). (Les champs "larges", doubleet long, occupent deux emplacements). Donc, déclarer explicitement un champ comme shortou bytene sauverait pas non plus de mémoire.

Marco13
la source
Je suppose que la logique pour laquelle les opérandes sont promus en int est également liée à la logique utilisée en C et C ++
Shafik Yaghmour
@ Marco13 "Donc déclarer explicitement un champ comme court ou octet ne sauverait pas non plus de mémoire." Est-ce vrai? Je ne pense pas que ce soit correct.
ACV
@ACV À proprement parler, une implémentation pourrait choisir de stocker une forme plus compacte, mais le format qui est exposé «virtuellement» (c'est-à-dire par la machine virtuelle) traitera les valeurs comme ayant au moins la taille de int. Si vous avez une référence à une autre implémentation, je mettrais à jour la réponse et j'insérerais le lien en conséquence.
Marco13
40

(Presque) Toutes les opérations sur byte, shortles promouvront int, par exemple, vous ne pouvez pas écrire:

short x = 1;
short y = 2;

short z = x + y; //error

Les arithmétiques sont plus faciles et simples lors de l'utilisation int, pas besoin de lancer.

En termes d'espace, cela fait très peu de différence. byteet shortcompliquerait les choses, je ne pense pas que cette micro-optimisation en vaille la peine puisque nous parlons d'un nombre fixe de variables.

byteest pertinent et utile lorsque vous programmez pour des appareils intégrés ou que vous traitez des fichiers / réseaux. De plus, ces primitives sont limitées, et si les calculs pouvaient dépasser leurs limites à l'avenir? Essayez de penser à une extension pour la Calendarclasse qui pourrait faire évoluer de plus grands nombres.

A noter également que dans les processeurs 64 bits, les habitants seront enregistrés dans les registres et ne pas utiliser de ressources, donc l' utilisation int, shortet d' autres primitives ne fera aucune différence. De plus, de nombreuses implémentations Java alignent les variables * (et les objets).


* byte et shortoccupent le même espace que ints'il s'agissait de variables locales , de variables de classe ou même de variables d' instance . Pourquoi? Parce que dans (la plupart des) systèmes informatiques, les adresses des variables sont alignées , donc par exemple si vous utilisez un seul octet, vous vous retrouverez en fait avec deux octets - un pour la variable elle-même et un autre pour le remplissage.

D'un autre côté, dans les tableaux, byteprenez 1 octet, shortprenez 2 octets et intprenez quatre octets, car dans les tableaux, seuls le début et peut-être la fin doivent être alignés. Cela fera une différence au cas où vous voudriez utiliser, par exemple System.arraycopy(), alors vous remarquerez vraiment une différence de performances.

Maroun
la source
1
Fait amusant: si vous utilisez des modificateurs finaux pour les deux valeurs, cela fonctionnera. :)
alexander
7

Parce que les opérations arithmétiques sont plus faciles lors de l'utilisation d'entiers par rapport aux courts-circuits. Supposons que les constantes ont effectivement été modélisées par des shortvaleurs. Ensuite, vous devrez utiliser l'API de cette manière:

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

Remarquez le casting explicite. Les valeurs courtes sont implicitement promues en intvaleurs lorsqu'elles sont utilisées dans des opérations arithmétiques. (Sur la pile d'opérandes, les courts-circuits sont même exprimés en entiers.) Ce serait assez difficile à utiliser, c'est pourquoi les intvaleurs sont souvent préférées pour les constantes.

Par rapport à cela, le gain d'efficacité de stockage est minime car il n'existe qu'un nombre fixe de telles constantes. On parle de 40 constantes. Changer leur stockage de intà shortvous protégerait 40 * 16 bit = 80 byte. Voir cette réponse pour plus de références.

Rafael Winterhalter
la source
5

Si vous utilisiez la philosophie selon laquelle les constantes intégrales sont stockées dans le plus petit type dans lequel elles s'inscrivent, alors Java aurait un problème sérieux: chaque fois que les programmeurs écrivent du code en utilisant des constantes intégrales, ils doivent faire très attention à leur code pour vérifier si le type de les constantes comptent, et si c'est le cas, recherchez le type dans la documentation et / ou effectuez les conversions de type nécessaires.

Alors maintenant que nous avons décrit un problème grave, quels avantages pourriez-vous espérer obtenir avec cette philosophie? Je ne serais pas surpris si le seul effet observable à l'exécution de ce changement serait le type que vous obtenez lorsque vous regardez la constante par réflexion. (et, bien sûr, quelles que soient les erreurs introduites par des programmeurs paresseux / involontaires ne tenant pas correctement compte des types de constantes)

Peser le pour et le contre est très simple: c'est une mauvaise philosophie.


la source
4

La complexité de conception d'une machine virtuelle est fonction du nombre de types d'opérations qu'elle peut effectuer. Il est plus facile d'avoir quatre implémentations d'une instruction comme "multiplier" - une pour chaque entier 32 bits, entier 64 bits, virgule flottante 32 bits et virgule flottante 64 bits - que d'avoir, en plus à ce qui précède, des versions pour les types numériques plus petits également. Une question de conception plus intéressante est de savoir pourquoi il devrait y avoir quatre types, plutôt que moins (effectuer tous les calculs d'entiers avec des entiers 64 bits et / ou faire tous les calculs à virgule flottante avec des valeurs à virgule flottante 64 bits). La raison de l'utilisation d'entiers 32 bits est que Java était censé fonctionner sur de nombreuses plates-formes où les types 32 bits pourraient être utilisés aussi rapidement que les types 16 bits ou 8 bits, mais les opérations sur les types 64 bits seraient notables. Ralentissez.ayant uniquement des types 32 bits.

En ce qui concerne les calculs en virgule flottante sur des valeurs 32 bits, les avantages sont un peu moins évidents. Il existe des plates-formes où un calcul commefloat a=b+c+d;pourrait être effectuée plus rapidement en convertissant tous les opérandes en un type de plus haute précision, en les ajoutant, puis en reconvertissant le résultat en un nombre à virgule flottante de 32 bits pour le stockage. Il existe d'autres plates-formes où il serait plus efficace d'effectuer tous les calculs en utilisant des valeurs à virgule flottante 32 bits. Les créateurs de Java ont décidé que toutes les plates-formes devraient être obligées de faire les choses de la même manière, et qu'ils devraient favoriser les plates-formes matérielles pour lesquelles les calculs en virgule flottante 32 bits sont plus rapides que les plus longs, même si ce PC gravement dégradé à la fois la vitesse et la précision des calculs en virgule flottante sur un PC classique, ainsi que sur de nombreuses machines sans unités à virgule flottante. Notez, btw, qu'en fonction des valeurs de b, c et d, utiliser des calculs intermédiaires de plus haute précision lors du calcul d'expressions comme celles mentionnées ci-dessusfloat a=b+c+d;produira parfois des résultats nettement plus précis que ceux obtenus avec tous les opérandes intermédiaires calculés avec floatprécision, mais produira parfois une valeur un tout petit peu moins précise. Dans tous les cas, Sun a décidé que tout devait être fait de la même manière et a opté pour l'utilisation de floatvaleurs de précision minimale .

Notez que les principaux avantages des types de données plus petits deviennent évidents lorsqu'un grand nombre d'entre eux sont stockés ensemble dans un tableau; même s'il n'y avait aucun avantage à avoir des variables individuelles de types inférieurs à 64 bits, il vaut la peine d'avoir des tableaux qui peuvent stocker des valeurs plus petites de manière plus compacte; avoir une variable locale être a byteplutôt que an longéconomise sept octets; avoir un tableau de 1000000 de nombres contient chaque nombre comme un byteplutôt qu'unlongondes 7 000 000 octets. Étant donné que chaque type de tableau n'a besoin de prendre en charge que quelques opérations (notamment lire un élément, stocker un élément, copier une plage d'éléments dans un tableau ou copier une plage d'éléments d'un tableau à un autre), la complexité supplémentaire d'avoir plus les types de tableaux n'est pas aussi grave que la complexité d'avoir plus de types de valeurs numériques discrètes directement utilisables.

supercat
la source
2

En fait, il y aurait un petit avantage. Si tu as un

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

puis sur une JVM typique, il a besoin d'autant d'espace qu'une classe contenant un seul int. La consommation de mémoire est arrondie à un multiple suivant de 8 ou 16 octets (IIRC, c'est configurable), de sorte que les cas où il y a une réelle sauvegarde sont plutôt rares.

Cette classe serait légèrement plus facile à utiliser si les Calendarméthodes correspondantes renvoyaient un fichier byte. Mais il n'y a pas de telles Calendarméthodes, seulement get(int)qui doit renvoyer un à intcause d'autres champs. Chaque opération sur des types plus petits favorise int, donc vous avez besoin de beaucoup de casting.

Très probablement, vous abandonnerez et passerez à un intou écrivez des setters comme

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

Alors le type de DAY_OF_WEEKn'importe pas, de toute façon.

maaartinus
la source