2010-06-25 6 views
11

Nous avons un v.large Dictionary<long,uint> (plusieurs millions d'entrées) dans le cadre d'une application C# haute performance. Lorsque l'application se ferme, nous sérialisons le dictionnaire sur disque en utilisant BinaryFormatter et MemoryStream.ToArray(). La sérialisation revient dans environ 30 secondes et produit un fichier d'environ 200 Mo. Lorsque nous essayons alors de désérialiser le dictionnaire en utilisant le code suivant:Sérialiser et désérialiser le dictionnaire V.Large en C#

BinaryFormatter bin = new BinaryFormatter(); 
Stream stream = File.Open("filePathName", FileMode.Open); 
Dictionary<long, uint> allPreviousResults = 
    (Dictionary<long, uint>)bin.Deserialize(stream); 
stream.Close(); 

Il faut environ 15 minutes pour revenir. Nous avons essayé des alternatives et la partie lente est définitivement bin.Derserialize(stream), c'est-à-dire que les octets sont lus à partir du disque dur (SSD haute performance) en moins d'une seconde.

Quelqu'un peut-il signaler ce que nous faisons de mal, car nous voulons que le temps de chargement sur le même ordre que le temps de gagner.

Cordialement, Marc

+0

Quel est le type de dictionnaire? C'EST À DIRE. Est-ce comme: Dictionnaire ? – CodingGorilla

+0

Je viens de réaliser que j'ai effectivement ajouté cela à l'article original mais il était à l'intérieur des parenthèses triangulaires, donc je ne me suis pas présenté. Le dictionnaire est (long, uint). – MarcF

+0

Hmm. Intéressant. J'aurais juré qu'il y aurait eu des ficelles - beaucoup d'allocations de chaînes sur le tas. –

Répondre

11

Vous pouvez vérifier protobuf-net ou simplement le sérialiser vous-même, ce qui sera probablement le plus rapide que vous pouvez obtenir.

class Program 
{ 
    public static void Main() 
    { 
     var dico = new Dictionary<long, uint>(); 
     for (long i = 0; i < 7500000; i++) 
     { 
      dico.Add(i, (uint)i); 
     } 

     using (var stream = File.OpenWrite("data.dat")) 
     using (var writer = new BinaryWriter(stream)) 
     { 
      foreach (var key in dico.Keys) 
      { 
       writer.Write(key); 
       writer.Write(dico[key]); 
      } 
     } 

     dico.Clear(); 
     using (var stream = File.OpenRead("data.dat")) 
     using (var reader = new BinaryReader(stream)) 
     { 
      while (stream.Position < stream.Length) 
      { 
       var key = reader.ReadInt64(); 
       var value = reader.ReadUInt32(); 
       dico.Add(key, value); 
      } 
     } 
    } 
} 

taille du fichier résultant => 90M octets (85,8MB).

+0

Juste couru ce code en utilisant un dictionnaire avec des paires de valeur-clé 20M, produisant un fichier de 234 Mo de taille. Performance sur un i7 (4GHz) - 8Go DDR3 Ram - Vertex 2 SSD Disque dur: Construire le dictionnaire et écrire à l'heure du fichier - 2.17secs Dictionnaire lu à partir du fichier et le temps de reconstruction - 15.39secs Si nous pouvons maintenir ce genre de performance, il devrait fonctionner bien. – MarcF

+0

+1: solution merveilleuse :) – Juliet

+0

Je viens de terminer l'implémentation de cette solution dans notre application actuelle et les résultats étaient similaires aux performances affichées précédemment par moi (c'est-à-dire Excellent). J'étais un peu inquiet d'avoir des clés non consécutives qui pourraient causer un problème mais c'était injustifié (cela ne semble pas faire de différence). Encore merci beaucoup !! – MarcF

2

Vous pouvez utiliser un profileur pour voir si, dans les coulisses, le désérialiseur effectue un tas de sur la volée réflexion. Pour l'instant, si vous ne souhaitez pas utiliser de base de données, essayez de stocker vos objets sous forme de fichier plat dans un format personnalisé. Par exemple, la première ligne du fichier donne le nombre total d'entrées dans le dictionnaire, ce qui vous permet d'instancier un dictionnaire avec une taille prédéterminée. Disposez les lignes restantes comme une série de paires clé-valeur de largeur fixe représentant toutes les entrées de votre dictionnaire.

Avec votre nouveau format de fichier, utilisez un StreamReader pour lire votre fichier ligne par ligne ou dans des blocs fixes, voyez si cela vous permet de lire votre dictionnaire plus rapidement.

+0

Bon point sur la taille du dictionnaire avant d'ajouter les entrées. Lorsque j'étudie cette approche, je suggère d'utiliser un lecteur binaire comme lisant des millions de chaînes, créant des millions de chaînes, puis analysant des millions de longs et ulongs à partir de ces chaînes auront leurs propres problèmes de performance. –

+0

Voir l'exemple de @ Darin. –

1

Il existe plusieurs solutions NoSQL Key-Value rapides, pourquoi ne pas les essayer? À titre d'exemple ESENT, quelqu'un l'a posté ici à SO. managedesent

4

Juste pour montrer sérialisation similaire (la réponse acceptée) par l'intermédiaire protobuf-net:

using System.Collections.Generic; 
using ProtoBuf; 
using System.IO; 

[ProtoContract] 
class Test 
{ 
    [ProtoMember(1)] 
    public Dictionary<long, uint> Data {get;set;} 
} 

class Program 
{ 
    public static void Main() 
    { 
     Serializer.PrepareSerializer<Test>(); 
     var dico = new Dictionary<long, uint>(); 
     for (long i = 0; i < 7500000; i++) 
     { 
      dico.Add(i, (uint)i); 
     } 
     var data = new Test { Data = dico }; 
     using (var stream = File.OpenWrite("data.dat")) 
     { 
      Serializer.Serialize(stream, data); 
     } 
     dico.Clear(); 
     using (var stream = File.OpenRead("data.dat")) 
     { 
      Serializer.Merge<Test>(stream, data); 
     } 
    } 
} 

Taille: 83meg - mais surtout, vous ne l'avez pas eu à tout faire à la main, l'introduction bogues. Rapide aussi (sera encore plus rapide dans "v2").

Questions connexes