2010-10-26 19 views
17

J'écris une application C# qui doit lire environ 130 000 paires (String, Int32) au démarrage d'un dictionnaire. Les paires sont stockées dans un fichier .txt, et sont donc facilement modifiables par n'importe qui, ce qui est dangereux dans le contexte. Je voudrais demander s'il existe un moyen de sauvegarder ce dictionnaire afin que les informations puissent être stockées raisonnablement en toute sécurité, sans perdre de performance au démarrage. J'ai essayé d'utiliser BinaryFormatter, mais le problème est que pendant que le programme original prend entre 125ms et 250ms au démarrage pour lire les informations du txt et construire le dictionnaire, désérialiser les fichiers binaires résultants prend jusqu'à 2s, ce qui n'est pas trop par lui-même, mais par rapport à la performance d'origine est une diminution de 8-16x de la vitesse.Enregistrement d'un dictionnaire <String, Int32> en C# - Sérialisation?

Note: Le chiffrement est important, mais le plus important devrait être un moyen d'enregistrer et de lire le dictionnaire à partir du disque - peut-être à partir d'un fichier binaire - sans avoir à utiliser Convert.ToInt32 sur chaque ligne, améliorant ainsi les performances .

+0

8-16x plus lent comparé tp quoi, qu'est-ce que vous utilisez actuellement? Je serais surpris si BinaryFormatter n'est pas le plus rapide. – Aliostad

+0

Je lis les chaînes du fichier .txt en utilisant la méthode ReadLine() de StreamReader et en utilisant Convert.ToInt32 pour les entiers. Chacune de ces deux opérations est effectuée environ 131 000 fois au démarrage. – Miguel

Répondre

26

question intéressante. Je l'ai fait quelques tests rapides et vous avez raison - BinaryFormatter est étonnamment lent:

  • sérialisation 130.000 entrées du dictionnaire: 547ms
  • deserialize 130000 Les entrées du dictionnaire: 1046ms

Quand je codé il avec un StreamReader/StreamWriter avec des valeurs séparées par des virgules j'ai obtenu:

  • sérialisation 130 000 entrées du dictionnaire: 121ms
  • deserialize 130000 Les entrées du dictionnaire: 111ms

Mais j'ai essayé en utilisant simplement un BinaryWriter/BinaryReader:

  • sérialisation 130.000 entrées du dictionnaire: 22ms
  • Désertiquez 130 000 entrées de dictionnaire: 36ms

Le code qui ressemble à ceci:

public void Serialize(Dictionary<string, int> dictionary, Stream stream) 
{ 
    BinaryWriter writer = new BinaryWriter(stream); 
    writer.Write(dictionary.Count); 
    foreach (var kvp in dictionary) 
    { 
     writer.Write(kvp.Key); 
     writer.Write(kvp.Value); 
    } 
    writer.Flush(); 
} 

public Dictionary<string, int> Deserialize(Stream stream) 
{ 
    BinaryReader reader = new BinaryReader(stream); 
    int count = reader.ReadInt32(); 
    var dictionary = new Dictionary<string,int>(count); 
    for (int n = 0; n < count; n++) 
    { 
     var key = reader.ReadString(); 
     var value = reader.ReadInt32(); 
     dictionary.Add(key, value); 
    } 
    return dictionary;     
} 

Comme d'autres ont dit que, si vous êtes préoccupé par les utilisateurs Toute modification du fichier, le cryptage, plutôt que la mise en forme binaire est la voie à suivre.

+0

Merci beaucoup pour votre suggestion! – Miguel

+0

Comment avez-vous obtenu une telle différence en utilisant BinaryReader/BinaryWriter? Je reçois environ les mêmes temps en utilisant FileReader/FileWriter et BinaryReader/BinaryWriter ... – Miguel

+1

@Miguel - voici mon fichier de test unitaire: http://pastie.org/1249910 - il se peut que mon code StreamReader/StreamWriter ne soit pas aussi efficace que le vôtre –

1

Eh bien, en utilisant un BinaryFormatter est pas vraiment un moyen sûr de stocker les paires, comme vous pouvez écrire un programme très simple désérialiser (après, disons, réflecteur en cours d'exécution sur votre code pour obtenir le type)

Que diriez-vous de crypter le txt? Par exemple avec this? (pour des performances maximales, essayez sans compression)

+0

Merci beaucoup pour votre suggestion. Quel est l'impact sur les performances de l'utilisation du chiffrement? Et, si je comprends bien, c'est aussi dangereux car tout utilisateur peut le décompresser, changer le fichier .txt et le compresser à nouveau, n'est-ce pas? – Miguel

+1

Je n'ai aucune idée, vous devriez probablement tester votre cas. Notez également la réponse de Pieter, peut-être une meilleure idée pour le chiffrement (je ai lié à une bibliothèque de compression, qui peut également crypter) –

+0

@Miguel - Notez cependant qu'il y a de fortes chances que votre impact sur les performances sera plus faible parce que votre IO sera plus faible. Comme l'a dit @ohadsc, essayez-le et voyez ce qu'il vous donne. –

3

Si vous souhaitez stocker les données de manière relativement sûre, vous pouvez en chiffrer le contenu. Si vous le cryptez simplement en tant que chaîne et que vous le décryptez avant votre logique d'analyse actuelle, vous devriez être en sécurité. Et cela ne devrait pas avoir d'impact sur les performances. Pour plus d'informations, voir Encrypt and decrypt a string.

3

Le cryptage se fait au détriment de la gestion des clés. Et, bien sûr, même les algorithmes de cryptage/décryptage les plus rapides sont plus lents que pas de cryptage du tout. Pareil avec la compression, ce qui n'aidera que si vous êtes lié aux E/S.

Si la performance est votre principale préoccupation, commencez à regarder où se trouve réellement le goulot d'étranglement. Si le coupable est vraiment l'appel Convert.ToInt32(), j'imagine que vous pouvez stocker les bits Int32 directement et s'en tirer avec une distribution simple, ce qui devrait être plus rapide que l'analyse d'une valeur de chaîne. Pour masquer les chaînes, vous pouvez xor chaque octet avec une certaine valeur fixe, ce qui est rapide mais ne fournit rien de plus qu'un roadbump pour un attaquant déterminé.

1

Peut-être quelque chose comme:

static void Serialize(string path, IDictionary<string, int> data) 
    { 
     using (var file = File.Create(path)) 
     using (var writer = new BinaryWriter(file)) 
     { 
      writer.Write(data.Count); 
      foreach(var pair in data) 
      { 
       writer.Write(pair.Key); 
       writer.Write(pair.Value);      
      } 
     } 
    } 
    static IDictionary<string,int> Deserialize(string path) 
    { 
     using (var file = File.OpenRead(path)) 
     using (var reader = new BinaryReader(file)) 
     { 
      int count = reader.ReadInt32(); 
      var data = new Dictionary<string, int>(count); 
      while(count-->0) { 
       data.Add(reader.ReadString(), reader.ReadInt32()); 
      } 
      return data; 
     } 
    } 

Notez cette re quoi que ce soit le cryptage ne fait pas; C'est une préoccupation distincte. Vous trouverez peut-être aussi que l'ajout de dégonfler dans le mélange réduit fichier IO et augmente les performances:

static void Serialize(string path, IDictionary<string, int> data) 
    { 
     using (var file = File.Create(path)) 
     using (var deflate = new DeflateStream(file, CompressionMode.Compress)) 
     using (var writer = new BinaryWriter(deflate)) 
     { 
      writer.Write(data.Count); 
      foreach(var pair in data) 
      { 
       writer.Write(pair.Key); 
       writer.Write(pair.Value);      
      } 
     } 
    } 
    static IDictionary<string,int> Deserialize(string path) 
    { 
     using (var file = File.OpenRead(path)) 
     using (var deflate = new DeflateStream(file, CompressionMode.Decompress)) 
     using (var reader = new BinaryReader(deflate)) 
     { 
      int count = reader.ReadInt32(); 
      var data = new Dictionary<string, int>(count); 
      while(count-->0) { 
       data.Add(reader.ReadString(), reader.ReadInt32()); 
      } 
      return data; 
     } 
    } 
1

Est-il assez sûr d'utiliser BinaryFormatter au lieu de stocker le contenu directement dans le fichier texte? Évidemment pas. Parce que les autres peuvent facilement "détruire" le fichier en l'ouvrant par le bloc-notes et ajouter quelque chose, même s'il ne peut voir que des caractères étranges. C'est mieux si vous le stockez dans une base de données. Mais si vous insistez sur votre solution, vous pouvez facilement améliorer la performance en utilisant Parallel Programming en C# 4.0 (vous pouvez facilement obtenir beaucoup d'exemples utiles en le recherchant). Quelque chose ressemble à ceci:

//just an example 
Dictionary<string, int> source = GetTheDict(); 
var grouped = source.GroupBy(x => 
       { 
        if (x.Key.First() >= 'a' && x.Key.First() <= 'z') return "File1"; 
        else if (x.Key.First() >= 'A' && x.Key.First() <= 'Z') return "File2"; 
        return "File3"; 
       }); 
Parallel.ForEach(grouped, g => 
       { 
       ThreeStreamsToWriteToThreeFilesParallelly(g); 
       }); 

Une autre solution alternative de Parallel est de créer plusieurs threads, la lecture de/écriture de fichiers différents sera plus rapide.