Irgendwelche Vorschläge, wie die Leistung einer Java -Zeichenfolge zu Byte [] umwandelt werden kann?

StackOverflow https://stackoverflow.com/questions/1023701

Frage

Ich habe einen Code geerbt, der die intensive Verwendung von String -> Byte [] Conversions und umgekehrt für einen hausgemachten Serialisierungscode macht. Im Wesentlichen wissen die Java -Objekte, wie sie ihre Bestandteile in Zeichenfolgen umwandeln können, die dann in ein Byte umgewandelt werden []. Das Byte -Array wird dann durch JNI in C ++ - Code übergeben, das das Byte [] in C ++ std :: Zeichenfolgen rekonstituiert und diese zum Bootstrap C ++ - Objekte verwendet, die die Java -Objekte widerspiegeln. Es steckt ein bisschen mehr, aber dies ist eine hohe Ansicht, wie dieses Stück Code funktioniert. Die Kommunikation funktioniert in beiden Richtungen so, so dass der C ++ -> Java -Übergang ein Spiegelbild des Java -> C ++ -Übergangs, den ich oben erwähnt habe.

Ein Teil dieses Codes - die tatsächliche Konvertierung einer Zeichenfolge in ein Byte [] - zeigt sich unerwartet im Profiler als Verbrennung einer Menge CPU. Zugegeben, es werden viele Daten übertragen, aber dies ist ein unerwarteter Engpass.

Der grundlegende Umriss des Codes lautet wie folgt:

public void convertToByteArray(String convert_me, ByteArrayOutputStream stream)
{
  stream.write(convert_me.getBytes());
}

Die Funktion hat etwas mehr, aber nicht viel. Die obige Funktion wird einmal für jedes Zeichenfolgen/Zeichenfolge/String -Objekt aufgerufen und nachdem alle Bestandteile in den BytearrayoutputStream geschrieben wurden, wird der BytearrayoutputStream in ein Byte [] konvertiert. Das Obige in eine profilierfreundlichere Version durchbrechen, indem Sie die extrahieren convert_me.getBytes() Aufruf zeigt, dass in über 90% der Fälle in dieser Funktion im Anruf von GetByTes () ausgegeben wird.

Gibt es eine Möglichkeit, die Leistung des GetBytes () -Verrufs zu verbessern, oder gibt es eine andere, möglicherweise schnellere Möglichkeit, dieselbe Konvertierung zu erreichen?

Die Anzahl der umgewandelten Objekte ist ziemlich groß. Bei den Profilerstellung, die nur eine kleine Teilmenge der Produktionsdaten verwenden, sehe ich ungefähr 10 Millionen Plus -Aufrufe der obigen Konvertierungsfunktion.

Aufgrund der Tatsache, dass wir das Projekt in der Produktion sehr verschenken, gibt es einige Problemumgehungen, die zu diesem Zeitpunkt nicht möglich sind:

  • Schreiben Sie die Serialisierungsschnittstelle neu, um String -Objekte über die JNI -Ebene zu übergeben. Dies ist die offensichtliche (für mich) Art, die Situation zu verbessern, aber es würde ein wesentliches Neugineering der Serialisierungsschicht erfordern. Angesichts der Tatsache, dass wir Anfang dieser Woche in die UAT gehen, ist es viel zu spät, um diese Art von komplexer Veränderung vorzunehmen. Es ist mein Top -Todo für die nächste Veröffentlichung, also wird es getan. Ich brauche jedoch bis dahin eine Problemumgehung, aber bisher funktioniert der Code seit Jahren und hat die meisten Knicke ausgearbeitet. Abgesehen von der Leistung.
  • Das Ändern des JVM (derzeit 1.5) ist ebenfalls keine Option. Leider ist dies die Standard -JVM, die auf den Maschinen des Kunden installiert ist und die Aktualisierung auf 1.6 (was in diesem Fall möglicherweise nicht schneller ist), leider nicht möglich. Jeder, der in großen Organisationen gearbeitet hat, versteht wahrscheinlich, warum ...
  • Darüber hinaus stoßen wir bereits auf Speicherbeschränkungen ein und versuchen, mindestens die größeren Saiten und ihre Byte -Array -Darstellung zu zwischenstrahlen, während es eine potenziell elegante Lösung ist, wahrscheinlich mehr Probleme verursachen, als es lösen wird
War es hilfreich?

Lösung

Ich vermute, ein Teil des Problems könnte sein, dass sich eine Java -Zeichenfolge im UTF -16 -Format befindet - dh zwei Bytes pro Charakter; Also getBytes() Erledigt eine Reihe von Arbeiten, um jedes UTF-16-Element in ein oder zwei Bytes umzuwandeln, abhängig von Ihrem aktuellen Zeichensatz.

Haben Sie versucht zu verwenden CharsetEncoder - Dies sollte Ihnen mehr Kontrolle über die String -Codierung geben und es Ihnen ermöglichen, einen Teil des Overheads im Standard zu überspringen getBytes Implementierung.

Alternativ haben Sie versucht, den Charset explizit anzulegen getBytes, und verwenden US-Ascii Wie der Charakter setzte?

Andere Tipps

Ich sehe mehrere Optionen:

  • Wenn Sie lateinische 1 Saiten haben, können Sie einfach das höhere Byte der Zeichen in der Saite teilen (Charset tut dies auch, denke ich)
  • Sie können die Arbeit auch unter mehreren Kernen teilen, wenn Sie mehr haben (das Fork-Join-Framework hatte einmal 1,5 auf 1,5).
  • Sie können die Daten auch in einen StringBuilder erstellen und sie am Ende nur einmal in Byte -Array konvertieren.
  • Schauen Sie sich Ihre GC/Speicherverwendung an. Zu viel Speicherauslastung kann Ihre Algorithmen aufgrund häufiger GC -Unterbrechungen verlangsamen
  • Wenn es sich um die gleichen Saiten handelt, die Sie alle Zeiten konvertieren, können Sie das Ergebnis zu einer schwachen HashMap zwischenspeichern.

    Schauen Sie sich auch die Methode von GetByTes () an (die Quelle ist verfügbar, wenn Sie das SDK installieren), um zu sehen, was genau sie tut.

    Das Problem ist, dass alle Methoden in Java auch heute noch den Gedächtnis mit UTF-8-Produktion zuweisen. Um den Codierungs -Performanten zu erhalten, müssten Sie benutzerdefinierten Code schreiben und den Byte [] -Puffer wiederverwenden. Colfer kann den Code generieren oder einfach nur seine Implementierung kopieren.

    https://github.com/pascaldekloe/colfer/blob/4c6f022c5183c0aeBb8bc73e8137f976d31b1083/java/gen/o.java#l414

    Lizenziert unter: CC-BY-SA mit Zuschreibung
    Nicht verbunden mit StackOverflow
    scroll top