JVM um weitere Befehle erweitern


#1

Mir fehlt bei Java die Möglichkeit aus einem byte[] array an einer bestimmten Stelle direkt einen primitiven Datentyp auszulesen.
Bisher wird das mit viel Rechenaufwand erreicht. Zum Beispiel so:
ByteBuffer.wrap(new byte[]{0,0,0,42}).getInt();
Hier werden letztendlich einige dutzend Methoden aufgerufen um den Integer aus den vier Bytes auszurechnen.

Bei der JVM werden die Befehle mit bytes kodiert. Es gibt insgesamt 201 Befehle. Also könnte man 54 weitere implementieren.

So könnte man einige Befehle einbauen um aus einem byte[] array all die primitiven typen (int, float, short, char, long, double) in einem Schritt auslesen zu können. Das wäre sehr nützlich und
wäre z.B. bei Datenbank-Anwendungen vorteilhaft.


#2

Erstes kurzes Statement: Nutzlos.

Ewas länger: Man wird immer mehr Rechenaufwand für die gestellte Aufgabe benötigen, als nur einen Opcode, deswegen wird man es wohl so lassen. Darüber hinaus wäre es ja nichts anderes, als 1,2,4 oder 8 Bytes ab einer bestimmten Stelle im Speicher zu lesen, in die richtige Reihenfolge (BigEndian, LittleEndian) zu bringen und als entsprechenden Datentypen zurück zu geben und solche Opcodes existieren bereits.

Auf dem Assembler-Level kennt Java ja keine Datentypen, sondern nur Datenstrukturen, was aber nichts Neues für Prozessoren ist. Was du hier als Opcodes haben willst, klingt schon sehr nach dem, was man eigentlich vermeiden will, nämlich danach, dass der Prozessor auch noch andere Dinge neben dem Speicher manipulieren soll - z.B. auch Dateien auf einer Festplatte oder Pixel auf einem Bildschirm. Das wäre nur wenig Vorteilhaft für Anwendungen aber unheimlich unvorteilhaft für Prozessor- und Controller-Architekturen.

Ein Tipp: Wenn dich öfters solche Geschichten plagen, dann solltest du in deiner Programmstuktur eher Buffer als Arrays verwenden. Das ist zwar gewöhnungsbedürftig, aber, wenn man den Bogen erst mal raus hat, recht komfortabel. Naja… es besteht dann auch die “Gefahr”, dass man unheimlich schnell nur noch mit DirectBuffer arbeitet, um den JavaHeap nicht zu belasten, aber soweit ich das überblicke, würde ich da nicht unbedingt von “Gefahr” sprechen, es sei denn, ich übersehe da etwas.


#3

Mit Zeigern ist es möglich einfach innerhalb so eines byte[] arrays zu zeigen und schon interpretiert der Prozessor dort einen int. Also kaum Rechenaufwand. JVM ist ja selbst meist in C Programmiert, also ginge das.

Es gibt allerdings JIT, vllt wird das eh optimiert. Aber nichtsdestotrotz, ich frage mich warum das nicht implementiert ist.

Ich schreibe später wie viele bytecode-Befehle man gespart hätte. Ich schätze einige Dutzend.

Nun man könnte natürlich auch eine native Bibliothek nutzen, es wäre aber auch nicht performanter, weil man eine Methode aufrufen müsste…


#4

Ich habe mal eine Frage. Greifst Du auf Native Sachen zu? Mich interessiert der Kontext warum Du überhaupt mit ByteBuffer aus Bytes einen Int machen musst ?

Abgesehen davon ist Java nun mal keine Sprache um Assembler und/oder Maschinennah zu programmieren…da gibt es besser alternativen Go, C, C++…?


#5

Ich schreibe zur (oder zu?) Übungszwecken eine JVM mit Java. Mittlerweile kann sie sich selbst ausführen. Bei byte[] code interpretation muss die JVM bei vielen Befehlen zahlen aus mehreren bytes konstruieren. Was mit java sehr langsam geht. Aber das muss ja nicht sein. Ich werde einfach mal als Experiment solche Befehle Einbauen in die JVM.


#6

Hi, sieht extrem interessant aus…

Die Frage im Zusammenhang mit “Was mit java sehr langsam geht”. Wie Du das gemessen hast ? Und die Frage ist dann was als “langsam” zu definieren ist? Da wäre ja ein Vergleich mit einer aktuellen JVM angesagt ?


#7

Der Code
ByteBuffer.wrap(new byte[]{0,0,0,42}).getInt();
führt 217 bytecode Anweisungen aus (die Erzeugung des Arrays ausgenommen). Einige dieser Anweisungen sind Aufrufe von Methoden - die sind besonderes teuer. Mit solchen zusätzlichen Anweisungen wäre die JVM deutlich schneller. Zumindest meine Implementation.
D.h. man hätte 217 Anweisungen durch eine ersetzt. Und solche Operationen führt man sehr oft aus. Z.B. wenn man einen int über Netzwerk überträgt. Den muss man ja wieder zusammenbauen.

Das beste was ich erreicht habe sind 28 Anwesungen mit:
int i=(((a[3] ) << 24) | ((a[2] & 0xff) << 16) | ((a[1] & 0xff) << 8) | ((a[0] & 0xff) ));


#8

Nun, ich trau’ mich kaum, das zu erwähnen, aber grundsätzlich kommt man mit Unsafe schon recht nah an das gewünschte ran - nämlich Daten (aus einem byte[]) recht “roh” umzuinterpretieren in einen int.

Aber ich bin kein Fan von Unsafe. Bevor man das benutzt, sollte man IMHO

  1. Absolut sicher sein, dass das signifikant schneller ist
  2. Einen Fallback haben. D.h. es sollte auch eine Alternativimplementierung ohne Unsafe geben, auf die man leicht umschalten kann.

Ansonten denke ich, dass es ein paar Hilfsmethoden schon tun sollten, wie es z.B. auch in https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/io/Bits.java gemacht wird.