Toute suggestion sur la façon d'améliorer les performances d'une conversion Java String en octet []?

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

Question

J'ai hérité d'un morceau de code qui fait un usage intensif de String - > byte [] conversions et vice versa pour certains codes de sérialisation développés en interne. Essentiellement, les objets Java savent convertir leurs parties constitutives en chaînes, qui sont ensuite converties en octet []. Ce tableau d'octets est ensuite transmis via JNI au code C ++ qui reconstitue l'octet [] en C ++ std :: strings et utilise ceux-ci pour amorcer les objets C ++ reflétant les objets Java. Il y a un peu plus, mais c'est une vue d'ensemble du fonctionnement de ce morceau de code; La communication fonctionne comme cela dans les deux sens, de sorte que le C ++ - > Transition Java est une image miroir de la Java - > C ++ transition mentionnée ci-dessus.

Une partie de ce code - la conversion réelle d'une chaîne en un octet [] - apparaît de manière inattendue dans le profileur en tant que gravure de beaucoup de ressources processeur. Certes, de nombreuses données sont en cours de transfert, mais il s’agit d’un goulet d’étranglement inattendu.

Le plan de base du code est le suivant:

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

Il y a un peu plus à la fonction mais pas beaucoup. La fonction ci-dessus est appelée une fois pour chaque objet String / Stringified et après que tous les constituants ont été écrits dans ByteArrayOutputStream, ByteArrayOutputStream est converti en octet []. Diviser ce qui précède en une version plus conviviale pour les profileurs en extrayant l'appel convert_me.getBytes () montre que plus de 90% du temps de cette fonction est passé dans l'appel getBytes ().

Existe-t-il un moyen d'améliorer les performances de l'appel getBytes () ou existe-t-il un autre moyen potentiellement plus rapide de réaliser la même conversion?

Le nombre d'objets en cours de conversion est assez élevé. Sur les cycles de profilage qui utilisent seulement un petit sous-ensemble des données de production, je vois environ 10 millions d'appels et plus à la fonction de conversion ci-dessus.

Etant donné que nous sommes sur le point de lancer le projet en production, il existe quelques solutions de contournement qui ne sont pas possibles pour le moment:

  • Réécrivez l'interface de sérialisation afin de simplement passer des objets String à travers la couche JNI. Pour moi, c’est une façon évidente d’améliorer la situation, mais il faudrait une réingénierie majeure de la couche de sérialisation. Compte tenu du fait que nous allons à l'UAT plus tôt cette semaine, il est beaucoup trop tard pour apporter ce genre de changement complexe. C'est la meilleure des choses à faire pour la prochaine version, donc ce sera fait. J’ai toutefois besoin d’une solution de contournement jusqu’alors, mais jusqu’à présent, le code fonctionne, est utilisé depuis des années et la plupart des problèmes ont été résolus. Eh bien, en dehors de la performance.
  • La modification de la machine virtuelle Java (actuellement 1,5) n’est pas non plus une option. Malheureusement, il s’agit de la machine virtuelle Java par défaut installée sur les machines du client et la mise à jour vers la version 1.6 (qui peut être ou ne pas être plus rapide dans ce cas) n’est malheureusement pas possible. Quiconque a travaillé dans de grandes organisations comprend probablement pourquoi ...
  • De plus, nous sommes déjà confrontés à des contraintes de mémoire. Tenter de mettre en cache au moins les chaînes les plus grandes et leur représentation en tableau d'octets, tout en étant une solution potentiellement élégante, risque de poser plus de problèmes qu'il n'en résoudra
Était-ce utile?

La solution

Je suppose qu’une partie du problème peut être qu’une chaîne Java est au format UTF-16, c’est-à-dire deux octets par caractère; Ainsi, getBytes () effectue un tas de travaux pour convertir chaque élément UTF-16 en un ou deux octets, en fonction de votre jeu de caractères actuel.

Avez-vous essayé d'utiliser CharsetEncoder - Cela devrait vous donner plus de contrôle sur le codage de chaîne et vous permettre d'éviter une partie de la surcharge liée à la mise en œuvre par défaut de getBytes .

Sinon, avez-vous essayé de spécifier explicitement le jeu de caractères avec getBytes et utilisez US-ASCII comme jeu de caractères?

Autres conseils

Je vois plusieurs options:

  • Si vous avez des chaînes Latin-1, vous pouvez simplement diviser l'octet supérieur des caractères de la chaîne (charset le fait aussi, je pense)
  • Vous pouvez également diviser le travail en plusieurs cœurs si vous en avez plusieurs (le framework de jonction à la fourche avait un backport de 1,5 fois)
  • Vous pouvez également créer les données dans un constructeur de chaînes et les convertir en tableau d'octets une fois à la fin.
  • Regardez votre utilisation de GC / mémoire. Une utilisation excessive de la mémoire peut ralentir vos algorithmes en raison d'interruptions fréquentes du CPG
  • Si ce sont les mêmes chaînes que vous convertissez toutes les fois, vous pouvez mettre le résultat en cache dans un WeakHashMap.

    Consultez également la méthode getBytes () (la source est disponible si vous installez le SDK) pour savoir ce qu’elle fait exactement.

    Le problème est que toutes les méthodes en Java, même aujourd'hui, allouent de la mémoire avec la production UTF-8. Pour obtenir un encodage performant, vous devez écrire un code personnalisé et réutiliser le tampon octet []. Colfer peut générer le code ou simplement copier son implémentation.

    scroll top