2010-10-26 26 views
17

Estoy escribiendo una aplicación C# que necesita leer sobre 130,000 (String, Int32) pares en el inicio de un Diccionario. Los pares se almacenan en un archivo .txt y, por lo tanto, son fácilmente modificables por cualquier persona, lo cual es algo peligroso en el contexto. Me gustaría preguntar si hay una manera de guardar este diccionario para que la información se pueda almacenar de forma razonablemente segura, sin perder rendimiento en el inicio. He intentado usar BinaryFormatter, pero el problema es que mientras el programa original tarda entre 125ms y 250ms al inicio para leer la información del txt y construir el diccionario, deserializar los archivos binarios resultantes toma hasta 2s, lo que no es demasiado por en sí, pero en comparación con el rendimiento original es una disminución de 8-16x en la velocidad.Guardar un diccionario <String, Int32> en C# - ¿Serialización?

Nota: cifrado es importante, pero el más importante debe ser una manera de ahorrar y leer el diccionario desde el disco - posiblemente de un archivo binario - sin tener que utilizar Convert.ToInt32 en cada línea, lo que mejora el rendimiento .

+0

8-16 veces más lento comparado tp qué, ¿qué estás usando actualmente? Me sorprendería que BinaryFormatter no sea el más rápido. – Aliostad

+0

Estoy leyendo cadenas del archivo .txt usando el método 'ReadLine() 'de StreamReader, y usando' Convert.ToInt32' para los enteros. Cada una de estas dos operaciones se realizan unas 131,000 veces al inicio. – Miguel

Respuesta

26

pregunta interesante. Hice algunas pruebas rápidas y tienes razón - BinaryFormatter es sorprendentemente lenta:

  • Serialize 130.000 entradas de diccionario: 547ms
  • Deserialize 130.000 entradas de diccionario: 1046ms

Cuando codifiqué se con un StreamReader/StreamWriter con valores separados por comas que obtuve:

  • Serialize 130.000 entradas de diccionario: 121ms
  • Deserialize 130.000 entradas de diccionario: 111ms

Pero entonces he intentado simplemente utilizando un BinaryWriter/BinaryReader:

  • Serialize 130.000 entradas de diccionario: 22MS
  • Deserializar 130,000 entradas en el diccionario: 36ms

El código para que se parece a esto:

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;     
} 

Como otros han dicho, sin embargo, si usted está preocupado acerca de los usuarios manipulación de los archivos, cifrado, en lugar de formato binario es el camino a seguir.

+0

¡Muchas gracias por su sugerencia! – Miguel

+0

¿Cómo obtuviste esa diferencia usando BinaryReader/BinaryWriter? Recibo aproximadamente las mismas veces usando FileReader/FileWriter y BinaryReader/BinaryWriter ... – Miguel

+1

@Miguel - aquí está mi archivo de prueba de unidad: http://pastie.org/1249910 - puede ser que mi código de StreamReader/StreamWriter no fuera tan eficiente como el suyo –

1

Bueno, usando un BinaryFormatter no es realmente una forma segura de almacenar los pares, como se puede escribir un programa muy simple para deserializar (después de, por ejemplo, correr reflector en su código para obtener el tipo)

¿Qué tal encriptar el txt? ¿Con algo como this por ejemplo? (para obtener el máximo rendimiento, intente sin compresión)

+0

Muchas gracias por su sugerencia. ¿Cuál es el impacto en el rendimiento del uso del cifrado? Y, si lo entiendo bien, también es inseguro porque cualquier usuario puede descomprimirlo, cambiar el archivo .txt y comprimirlo de nuevo, ¿verdad? – Miguel

+1

No tengo ni idea, probablemente debería probar su caso. también tenga en cuenta la respuesta de Pieter, podría ser una mejor idea para el cifrado (me vinculé a una biblioteca de compresión, que también puede encriptar) –

+0

@Miguel - Tenga en cuenta que existe una gran posibilidad de que su impacto en el rendimiento sea menor cuando combina la compresión y el cifrado porque tu IO será más bajo Como dijo @ohadsc, solo pruébalo y mira lo que te da. –

3

Si desea que los datos se almacenen de forma relativamente segura, puede cifrar los contenidos. Si lo encriptas como una cadena y lo descifras antes de tu lógica de análisis actual, deberías estar a salvo. Y esto no debería afectar tanto el rendimiento.

Consulte Encrypt and decrypt a string para obtener más información.

3

El cifrado tiene el costo de la administración de claves. Y, por supuesto, incluso los algoritmos de cifrado/descifrado más rápidos son más lentos que ningún cifrado. Lo mismo con la compresión, que solo ayudará si está vinculado a E/S.

Si el rendimiento es su principal preocupación, comience a ver dónde está realmente el cuello de botella. Si el culpable es realmente la llamada Convert.ToInt32(), imagino que puede almacenar los bits de Int32 directamente y salirse con un simple molde, que debería ser más rápido que analizar un valor de cadena. Para ofuscar las cadenas, puede xor cada byte con algún valor fijo, que es rápido pero no proporciona nada más que una mejora en la ruta para un atacante determinado.

1

Tal vez algo como:

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; 
     } 
    } 

Nota: este no hace nada re cifrado; esa es una preocupación separada. También podría encontrar que la adición de desinflarse en la mezcla reduce archivo IO y aumenta el rendimiento:

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

¿Es seguro suficiente para usar BinaryFormatter en lugar de almacenar los contenidos directamente en el archivo de texto? Obviamente no. Porque otros pueden "destruir" fácilmente el archivo abriéndolo con un bloc de notas y agregar algo, aunque solo pueda ver caracteres extraños. Es mejor si lo almacena en una base de datos. Pero si insiste en su solución, puede mejorar mucho el rendimiento fácilmente, utilizando Parallel Programming en C# 4.0 (puede obtener fácilmente muchos ejemplos útiles buscándolo en Google). Algo tiene este aspecto:

//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); 
       }); 

Otra solución alternativa de Parallel es la creación de varios hilos, leyendo/escribiendo a diferentes archivos será más rápido.

Cuestiones relacionadas