Um einen UTF8-String über einen Stream zu schicken, will ich diesen en- und decodieren.
Laut Protokoll muss dem codierten String die Länge vorangestellt werden. Deshalb muss ich die characters erst in ein Array speichern, denn erst dann weiss ich, wieviel Platz der UTF8 codierte String einnehmen wird.
Auf meinem Board mit embedded Linux habe ich nun rausgefunden, dass die String codierung sehr viel Zeit braucht.
Ich denke, dass diese Array Kopierung nicht gerade schön ist. Auch die allokation von 255 Elementen ist nicht so schön. (Siehe Code unten.) Setze ich einen kleineren Wert ein, kann ich sehen, dass das Programm einiges schneller läuft, aber die 255 brauche ich als maximalen Wert.
Bei der Decodierung (siehe Code unten) bin ich mir nicht sicher, ob der Weg mit dem StringBuilder gut ist.
Bin auf eure Anregungen gespannt.
Encodierung:
char[] chars = new char[255];
int i=0;
for(int n=0; n 1byte
if (c 2byte
else if((c\>127) && (c\>6)|192);
chars[i++] = (char)((c&63)|128);
}
// 2048 bis 66536 =\> 3byte
else {
chars[i++] = (char)((c\>\>12)|224);
chars[i++] = (char)(((c\>\>6)&63)|128);
chars[i++] = (char)((c&63)|128);
}
}
outputStream.write(i);
for (int j=0; j
_ **Decodierung:**
final int len = inputStream.read();
char[] chars = null;
StringBuffer plaintext = new StringBuffer();
chars = new char[len];
int i=0;
while (i191) && (chars[i]_
Kannst du java.io.DataOutputStream benutzen ? Da gibts so eine praktische, verdammt schnelle Methode writeUTF. Solange du keine kranken Sonderzeichen drin hast kommt ab normales UTF8 raus. Wenn man aber ein japanisches Zeichen drin hat…
Oder gleich:
byte[] data = String.getBytes(„UTF8“)
OutputStream.write(data.length);
OutputStream.write(data);
Bei der Decodieren kann man auch ohne for-schliefen tun. Ist deutlich schneller.
Kannst du java.io.DataOutputStream benutzen ? Da gibts so eine
praktische, verdammt schnelle Methode writeUTF. Solange du
keine kranken Sonderzeichen drin hast kommt ab normales UTF8
raus. Wenn man aber ein japanisches Zeichen drin hat…
Nein, muss leider die OutputStream Klasse benutzen.
Oder gleich:
byte[] data = String.getBytes(„UTF8“)
OutputStream.write(data.length);
OutputStream.write(data);
Ich habe rausgefunden, dass dieses String.getBytes(„UTF8“) halb so schnell läuft wie meine for Schleife. Auf dem PC kein Problem, aber auf dem embedded Board ist das performancemässig sehr schlecht.
Bei der Decodieren kann man auch ohne for-schliefen tun. Ist
deutlich schneller.
Wie meinst du das? Ich muss ja über das ganze byte Array traversieren, um die characters zu erhalten.
Ich habe rausgefunden, dass dieses String.getBytes(„UTF8“)
halb so schnell läuft wie meine for Schleife.
hmm… Welche JME-version ist das ? personal edition oder MIDP ? (Ich grab an einem änlichen Problem, allerdings für MIDP 1.0. getBytes ist auf dem Gerät deutlich schneller als mein Umsetzer)
In den meisten J2ME-KVMs arbeitet String intern mit einem char[]. getBytes(„UTF8“) tut also genau das was dein Code tut, nur meistens native und nicht in ByteCode.
Vergleich mal den Speed von 1x String.toCharArray() zu getCharAt(). Dann „char[] chars = new char[255];“ in byte[] umwandeln (und mit (byte) anstelle von (char) „behandeln“). Vorteil: man kann outputStream (byte[],int offset, int length) benutzen.
Und versuch rauszufinden ob überhaupt Werte > 127 vorkommen.
Bei der Decodieren kann man auch ohne for-schliefen tun. Ist
deutlich schneller.
Wie meinst du das? Ich muss ja über das ganze byte Array
traversieren, um die characters zu erhalten.
Du liest die Länge aus, erstellst ein byte[] dieser Länge. Dann dieses mit InputStream.read(array) füllen (Rückgabewert beachten, bei den KVM’s weiss man nie wieviele Bytes tatsächlich gelesen wurden). Das Array in „new String (array, „UTF8“)“ stopfen. Kleinerer Code, evtl. native ausgeführte Umwandelung, keine Probleme mit der synchronisation von StringBuffer (Stringbuffer-methoden sind synchronized und synchronized ist langsam), kein doppeltes Umwandeln in Char.
Ich habe rausgefunden, dass dieses String.getBytes(„UTF8“)
halb so schnell läuft wie meine for Schleife.
hmm… Welche JME-version ist das ? personal edition oder MIDP
? (Ich grab an einem änlichen Problem, allerdings für MIDP
1.0. getBytes ist auf dem Gerät deutlich schneller als mein
Umsetzer)
Ist die J9-VM von IBM.
In den meisten J2ME-KVMs arbeitet String intern mit einem
char[]. getBytes(„UTF8“) tut also genau das was dein Code tut,
nur meistens native und nicht in ByteCode.
Ne, hab das mal debugged und da ist nichts native programmiert.
Vergleich mal den Speed von 1x String.toCharArray() zu
getCharAt(). Dann „char[] chars = new char[255];“ in byte[]
umwandeln (und mit (byte) anstelle von (char) „behandeln“).
Vorteil: man kann outputStream (byte[],int offset, int length)
benutzen.
char[] nach byte[] umwandeln ist ja auch nicht perfomanzfördernd
Und versuch rauszufinden ob überhaupt Werte > 127
vorkommen.
natürlich
Bei der Decodieren kann man auch ohne for-schliefen tun. Ist
deutlich schneller.
Wie meinst du das? Ich muss ja über das ganze byte Array
traversieren, um die characters zu erhalten.
Du liest die Länge aus, erstellst ein byte[] dieser
Länge. Dann dieses mit InputStream.read(array) füllen
(Rückgabewert beachten, bei den KVM’s weiss man nie wieviele
Bytes tatsächlich gelesen wurden). Das Array in „new String
(array, „UTF8“)“ stopfen. Kleinerer Code, evtl. native
ausgeführte Umwandelung, keine Probleme mit der
synchronisation von StringBuffer (Stringbuffer-methoden sind
synchronized und synchronized ist langsam), kein doppeltes
Umwandeln in Char.
Nein, damit habe ich es vorher versucht. Meine Implementation ist doppelt so schnell wie new String(array, „UTF8“)
Ne, hab das mal debugged und da ist nichts native
programmiert.
Oh…
char[] nach byte[] umwandeln ist ja auch nicht
perfomanzfördernd
Und was glaubst du das du da machst:
for (int j=0; j
_"outputStream.write(char c)" gibts nicht, da wandelt der compiler automatisch zu byte (oder int).
Also ich meinte das so:
for(int n=0; n
Du rufst da n mal eine Methode auf. Das sollte langsamer sein als:
char[] chars = myString.toCharArray()
for(int n=0; n
weil da nur eine Methode einmal im Spiel ist. In dem array sind int-Werte zwischen 8 und 24 Bit Länge gespeichert. Das kann man den Streams nicht einfachso verkaufen. Deshalb deine Umwandelung hintendran. Aber nicht in ein array des Typs char, sondern des Typs byte[]:
byte[] temp = new byte[255];
char[] chars = myString.toCharArray()
int i=0;
for(int n=0; n 1byte
if (c 2byte
else if((c\>127) && (c\>6)|192);
temp[i++] = (byte)((c&63)|128);
}
// 2048 bis 66536 =\> 3byte
else {
temp[i++] = (byte)((c\>\>12)|224);
temp[i++] = (byte)(((c\>\>6)&63)|128);
temp[i++] = (byte)((c&63)|128);
}
}
outputStream.write(temp.length);
outputStream.write(temp);
Da sind dann auch nur 2 Streamoperationen. Die Streamops sind in den KVMs nicht gebuffert. D.h. jeder Aufruf schlägt komplett bis zum Endpunkt durch.
> > Du liest die Länge aus, erstellst ein **byte[]** dieser
> > Länge. Dann dieses mit InputStream.read(array) füllen
> > (Rückgabewert beachten, bei den KVM's weiss man nie wieviele
> > Bytes tatsächlich gelesen wurden). Das Array in "new String
> > (array, "UTF8")" stopfen. Kleinerer Code, evtl. native
> > ausgeführte Umwandelung, keine Probleme mit der
> > synchronisation von StringBuffer (Stringbuffer-methoden sind
> > synchronized und synchronized ist langsam), kein doppeltes
> > Umwandeln in Char.
>
>
> Nein, damit habe ich es vorher versucht. Meine Implementation
> ist doppelt so schnell wie **new String(array, "UTF8")**
byte oder char-array ? Das ist nämlich ein grosser Unterschied.
Test das mal:
final int len = inputStream.read();
char[] chars = new char[len];
int i=0;
int j =0;
while (i191) && (b
Wenn du dir über das zusätzliche Array Sorgen machst: StringBuffer verbraucht mehr RAM.
cu_
weil da nur eine Methode einmal im Spiel ist. In dem array
sind int-Werte zwischen 8 und 24 Bit Länge gespeichert. Das
kann man den Streams nicht einfachso verkaufen. Deshalb deine
Umwandelung hintendran. Aber nicht in ein array des Typs char,
sondern des Typs byte[]:
byte[] temp = new byte[255];
char[] chars = myString.toCharArray()
int i=0;
for(int n=0; n 1byte
if (c 2byte
else if((c>127) && (c>6)|192);
temp[i++] = (byte)((c&63)|128);
}
Den Konstruktor
new String (chars,0,j,„UTF8“)
habe ich nicht gefunden.
Verdammt, das kommt davon wenn man den Code nicht testweise
kompiliert. Ich meinte:
new String (chars,0,j)
Vielen herzlichen Dank! Durch deine Tipps konnte ich die Laufzeit meines Codes um 20% verringern.
Das ist das erste Mal, dass ich auf Performance achten musste und ich habe auf jeden Fall ne Menge gelernt.
Das ist das erste Mal, dass ich auf Performance achten musste
Wenn du mehr rausholen willst: schmeiss die 2 array (char und byte) nicht jedesmal weg. Speicher sie einfach in der Instanz für nächste mal. OK, das kostet Heap-Space, ABER: KVMs fragmentieren manchmal den RAM. D.h. wenn man viele Objecte erzeugt und wieder entsorgt wurden können keine grossen Objecte mehr angelegt werden. Ob J9 drunter fällt weiss ich nicht. Es ist schade um die 256 + 4x256 Bytes, aber wenns schnell gehen soll ist das der richtige Weg.
Für 1.1 geschreiben, gilt bei den meisten KVMs immernoch:
„This can be considerably faster, but if you have short arrays, it’s less optimal since throwing and catching a new Exception takes 9,500ns and doing a >= int compare takes about 250ns (on a 200MHz UltraSPARC), so the breakpoint is about 40 elements.“
Beim schreiben für KVMs geht es eh nicht mehr um die normalen OO-Prinzipien, sondern nur einzig und alleine um Speed und Heap-grösse. Bei einem java-prog für PC würd ich’s auch nicht anwenden. Aber wenn die letzte ms rausgeholt werden soll, wieso nicht ?
wenn’s wirklich um Performance gehen muss, dann würde ich auch die letzten OO-Prinzipien über Bord werfen und wie eine (Entschuldigung) Drecksau programmieren. Das gilt nicht nur für KVMs, sondern auch für PCs und Server (da ganz besonders).
wenn’s wirklich um Performance gehen muss, dann würde ich auch
die letzten OO-Prinzipien über Bord werfen und wie eine
(Entschuldigung) Drecksau programmieren.