web-dev-qa-db-fra.com

Sérialisation haute performance: Java vs Google Protocol Buffers vs ...?

Pour une mise en cache que je pense faire pour un projet à venir, j'ai pensé à Java sérialisation. À savoir, devrait-il être utilisé?

Maintenant, j'ai déjà écrit la sérialisation et la désérialisation personnalisées (externalisables) pour diverses raisons au cours des années passées. De nos jours, l'interopérabilité est devenue encore plus un problème et je peux prévoir le besoin d'interagir avec les applications .Net, j'ai donc pensé à utiliser une solution indépendante de la plate-forme.

Quelqu'un a-t-il une expérience de l'utilisation hautes performances du GPB? Comment se compare-t-il en termes de vitesse et d'efficacité avec la sérialisation native de Java? Sinon, existe-t-il d'autres régimes qui méritent d'être envisagés?

52
cletus

Je n'ai pas comparé les tampons de protocole avec la sérialisation native de Java en termes de vitesse, mais pour l'interopérabilité, la sérialisation native de Java est un sérieux non. Il ne sera également pas aussi efficace en termes d'espace que les tampons de protocole dans la plupart des cas. Bien sûr, il est un peu plus flexible en termes de ce qu'il peut stocker, et en termes de références, etc. (et d'autres choses).

J'ai récemment publié un cadre d'analyse comparative des tampons de protocole dans Java et .NET. La Java est dans le projet principal de Google (dans le répertoire benchmarks ), la version .NET est dans mon projet de port C # . Si vous voulez comparer la vitesse PB avec Java Si vous êtes intéressé par l'interopérabilité, je ne voudrais vraiment pas donner une deuxième réflexion à la sérialisation native Java (ou sérialisation binaire native .NET)).

Il existe d'autres options pour la sérialisation interopérable en plus des tampons de protocole - Thrift , JSON et YAML me vient à l'esprit, et il y en a sans doute d'autres.

EDIT: D'accord, l'interopérabilité n'étant pas si importante, cela vaut la peine d'essayer d'énumérer les différentes qualités que vous voulez d'un cadre de sérialisation. Une chose à laquelle vous devriez penser est le versioning - c'est une autre chose que PB est conçu pour bien gérer, à la fois en arrière et en avant (pour que les nouveaux logiciels puissent lire les anciennes données et vice versa) - lorsque vous vous en tenez aux règles suggérées, bien sûr :)

Ayant essayé d'être prudent sur les Java vs sérialisation native, je ne serais vraiment pas surpris de constater que PB était de toute façon plus rapide. Si vous en avez l'occasion, utilisez le serveur vm - mon récent les repères ont montré que le serveur VM pour être deux fois plus rapide à sérialiser et désérialiser les données d'échantillon. Je pense que le PB le code convient très bien au JIT de la VM du serveur :)

Tout comme des exemples de performances, la sérialisation et la désérialisation de deux messages (un 228 octets, un 84750 octets), j'ai obtenu ces résultats sur mon ordinateur portable à l'aide de la machine virtuelle du serveur:

 Benchmarking benchmarks.GoogleSize $ SizeMessage1 avec le fichier google_message1.dat 
 Sérialisation en chaîne d'octets: 2581851 itérations en 30,16 s; 18,613789 Mo/s 
 Sérialisation en tableau d'octets: 2583547 itérations en 29,842 s; 18,824497 Mo/s 
 Sérialisation vers le flux mémoire: 2210320 itérations en 30,125 s; 15,953759 Mo/s 
 Désérialiser à partir d'une chaîne d'octets: 3356517 itérations en 30,088 s; 24,256632 Mo/s 
 Désérialisation du tableau d'octets: 3356517 itérations en 29,958 s; 24,361889 Mo/s 
 Désérialisation du flux mémoire: 2618821 itérations en 29,821s; 19.094952MB/s 
 
 Benchmarking benchmarks.GoogleSpeed ​​$ SpeedMessage1 avec le fichier google_message1.dat 
 Sérialiser en chaîne d'octets: 17068518 itérations en 29.978s; 123.802124 Mo/s 
 Sérialisation en tableau d'octets: 17520066 itérations en 30,043 s; 126.802376MB/s 
 Sérialisation en flux mémoire: 7736665 itérations en 30.076s; 55,93307 Mo/s 
 Désérialisation de la chaîne d'octets: 16123669 itérations en 30,073 s; 116,57947 Mo/s 
 Désérialisation du tableau d'octets: 16082453 itérations en 30,109 s; 116.14243MB/s 
 Désérialisation du flux mémoire: 7496968 itérations en 30.03s; 54,283176 Mo/s 
 
 Benchmarking benchmarks.GoogleSize $ SizeMessage2 avec le fichier google_message2.dat 
 Sérialiser en chaîne d'octets: 6266 itérations dans 30.034s; 16,826494 Mo/s 
 Sérialisation en tableau d'octets: 6246 itérations en 30,027 s; 16,776697 Mo/s 
 Sérialisation vers le flux mémoire: 6042 itérations en 29,916 s; 16,288969 Mo/s 
 Désérialisation de la chaîne d'octets: 4675 itérations en 29,819 s; 12,644595 Mo/s 
 Désérialisation du tableau d'octets: 4694 itérations en 30,093 s; 12,580387 Mo/s 
 Désérialisation du flux mémoire: 4544 itérations en 29,579s; 12,389998 Mo/s 
 
 Benchmarking benchmarks.GoogleSpeed ​​$ SpeedMessage2 avec le fichier google_message2.dat 
 Sérialiser en chaîne d'octets: 39562 itérations dans 30.055s; 106.16416 Mo/s 
 Sérialisation en tableau d'octets: 39715 itérations en 30,178 s; 106.14035 Mo/s 
 Sérialisation vers le flux mémoire: 34161 itérations en 30,032 s; 91,74085 Mo/s 
 Désérialisation de la chaîne d'octets: 36934 itérations en 29,794 s; 99,98019 Mo/s 
 Désérialisation du tableau d'octets: 37191 itérations en 29,915 s; 100,26867 Mo/s 
 Désérialisation du flux mémoire: 36237 itérations en 29,846s; 97,92251 Mo/s 

La "vitesse" par rapport à la "taille" est de savoir si le code généré est optimisé pour la vitesse ou la taille du code. (Les données sérialisées sont les mêmes dans les deux cas. La version "taille" est fournie pour le cas où vous avez beaucoup de messages définis et ne voulez pas prendre beaucoup de mémoire pour le code.)

Comme vous pouvez le voir, pour le plus petit message, il peut être très rapide - plus de 500 petits messages sérialisés ou désérialisés par milliseconde . Même avec le message 87K, cela prend moins d'une milliseconde par message.

60
Jon Skeet

Encore un point de données: ce projet:

http://code.google.com/p/thrift-protobuf-compare/

donne une idée des performances attendues pour les petits objets, y compris Java sérialisation sur PB.

Les résultats varient beaucoup en fonction de votre plateforme, mais il existe des tendances générales.

15
StaxMan

Vous pouvez également consulter FST , un remplacement direct pour la sérialisation JDK intégrée qui devrait être plus rapide et avoir une sortie plus petite.

estimations brutes sur les analyses comparatives fréquentes que j'ai faites ces dernières années:

100% = approches binaires/structurées (par exemple SBE, fst-structs)

  • incommode
  • le post-traitement (créer des "vrais" objets côté récepteur) peut gruger les avantages de performance et n'est jamais inclus dans les tests de performances

~ 10% -35% protobuf et dérivés

~ 10% à 30% de sérialiseurs rapides tels que FST et KRYO

  • les objets désérialisés pratiques peuvent être utilisés le plus souvent directement sans code de traduction manuel supplémentaire.
  • peut être optimisé pour les performances (annotations, enregistrement de classe)
  • conserver les liens dans le graphe d'objets (aucun objet sérialisé deux fois)
  • peut gérer des structures cycliques
  • solution générique, FST est entièrement compatible avec la sérialisation JDK

~ 2% -15% de sérialisation JDK

~ 1% -15% JSon rapide (par exemple Jackson)

  • ne peut gérer aucun graphique d'objet, mais seulement un petit sous-ensemble de structures de données Java
  • pas de ref restauration

0,001-1% graphique complet JSon/XML (par exemple JSON.io)

Ces chiffres sont censés donner une impression d'ordre de grandeur très approximative. Notez que les performances dépendent BEAUCOUP des structures de données en cours de sérialisation/de référence. Les benchmarks simples et simples de classe sont pour la plupart inutiles (mais populaires: par exemple, ignorer unicode, pas de collections, ..).

voir également

http://Java-is-the-new-c.blogspot.de/2014/12/a-persistent-keyvalue-server-in-40.html

http://Java-is-the-new-c.blogspot.de/2013/10/still-using-externalizable-to-get.html

8
R.Moeller

Si vous confondez PB et natif Java sérialisation sur la vitesse et l'efficacité, optez simplement pour PB.

  • PB a été conçu pour atteindre de tels facteurs. Voir http://code.google.com/apis/protocolbuffers/docs/overview.html
  • Les données PB sont très petites tandis que Java tend à répliquer un objet entier, y compris sa signature. Pourquoi j'obtiens toujours mon nom de classe, nom de champ ... sérialisé, même si je le connais à l'envers au récepteur?
  • Pensez au développement du langage. Cela devient difficile si un côté utilise Java, un côté utilise C++ ...

Certains développeurs suggèrent Thrift, mais j'utiliserais Google PB parce que "je crois en google" :-) .. Quoi qu'il en soit, ça vaut le coup d'oeil: http://stuartsierra.com/2008/07/10/thrift -vs-protocol-buffers

6
instcode

Qu'entendez-vous par haute performance? Si vous voulez une sérialisation en millisecondes, je vous suggère d'utiliser l'approche de sérialisation qui est la plus simple. Si vous voulez moins de milli-seconde, vous aurez probablement besoin d'un format binaire. Si vous voulez beaucoup moins de 10 micro-secondes, vous aurez probablement besoin d'une sérialisation personnalisée.

Je n'ai pas vu beaucoup de références pour la sérialisation/désérialisation, mais peu prennent en charge moins de 200 micro-secondes pour la sérialisation/désérialisation.

Les formats indépendants de la plate-forme ont un coût (en termes d'effort de votre part et de latence), vous devrez peut-être décider si vous souhaitez des performances ou l'indépendance de la plate-forme. Cependant, il n'y a aucune raison pour laquelle vous ne pouvez pas avoir les deux comme option de configuration parmi lesquelles vous pouvez basculer selon les besoins.

5
Peter Lawrey

Voici la suggestion décalée du jour :-) (vous venez de peaufiner quelque chose dans ma tête que je veux maintenant essayer) ...

Si vous pouvez opter pour l'ensemble de la solution de mise en cache via cela, cela pourrait fonctionner: Project Darkstar . Il est conçu comme un serveur de jeu à très hautes performances, spécialement pour que les lectures soient rapides (donc bon pour un cache). Il a Java et API C donc je crois (pensais que ça faisait longtemps que je ne l'avais pas regardé, et je n'y pensais pas alors) que vous pouviez enregistrer des objets avec Java et les relire en C et vice versa.

Si rien d'autre ne vous donne quelque chose à lire aujourd'hui :-)

1
TofuBeer

Pour une sérialisation conviviale, envisagez d'utiliser l'interface externalisable. Utilisé intelligemment, vous aurez une connaissance intime pour décider de la façon optimale de marshaller et de démarshaller des champs spécifiques. Cela dit, vous devrez gérer correctement la gestion des versions de chaque objet - facile à désassembler, mais rechaper un objet V2 lorsque votre code prend en charge V1 cassera, perdra des informations ou, pire, des données corrompues d'une manière que vos applications ne sont pas en mesure de traiter correctement. Si vous cherchez un chemin optimal, sachez qu'aucune bibliothèque ne résoudra votre problème sans compromis. Généralement, les bibliothèques s'adapteront à la plupart des cas d'utilisation et auront l'avantage supplémentaire qu'elles s'adapteront et s'amélioreront au fil du temps sans votre contribution, si vous avez opté pour un projet open source actif. Et ils pourraient ajouter des problèmes de performances, introduire des bogues et même corriger des bogues qui ne vous ont pas encore affecté!

0
user4992332