2010-10-26 11 views
17

Piszę aplikację C#, która musi odczytać około 130 000 par (String, Int32) podczas uruchamiania do słownika. Pary są przechowywane w pliku .txt, przez co są łatwo modyfikowane przez każdego, co jest niebezpieczne w danym kontekście. Chciałbym zapytać, czy istnieje sposób na zapisanie tego słownika, aby informacja mogła być bezpiecznie przechowywana, bez utraty wydajności podczas uruchamiania. Próbowałem używać BinaryFormatter, ale problem polega na tym, że podczas początkowego programu trwa od 125ms do 250ms przy uruchomieniu, aby odczytać informacje z txt i zbudować słownik, deserializacja wynikowych plików binarnych zajmuje do 2s, co nie jest zbyt wiele przez sam w sobie, ale w porównaniu do pierwotnej wydajności jest 8-16-krotny spadek prędkości.Zapisywanie słownika <String, Int32> w języku C# - Serialization?

Uwaga: Szyfrowanie jest ważne, ale najważniejszy powinien być sposób, aby zapisać i odczytać z dysku słownika - ewentualnie z binarnego pliku - bez konieczności korzystania Convert.ToInt32 na każdej linii, tym samym zwiększając wydajność .

+0

8-16x wolniej porównywane tp co, czego aktualnie używasz? Byłbym zaskoczony, gdyby BinaryFormatter nie był najszybszy. – Aliostad

+0

Czytam ciągi z pliku .txt, używając metody 'ReadLine() 'StreamReadera i używając' Convert.ToInt32' dla liczb całkowitych. Każda z tych dwóch operacji jest wykonywana około 131 000 razy podczas uruchamiania. – Miguel

Odpowiedz

26

ciekawe pytanie. Zrobiłem kilka szybkich testów i masz rację - BinaryFormatter jest zaskakująco wolno:

  • Serializować 130.000 wpisy słownikowe: 547ms
  • deserializowania 130.000 słownika wpisy: 1046ms

Kiedy zakodowane go z StreamReader/StreamWriter z wartościami oddzielonymi przecinkami dostałem:

  • Serializować 130000 słownika wpisy: 121ms
  • deserializowania 130.000 słownika wpisy: 111ms

Ale potem próbowałem tylko przy użyciu BinaryWriter/BinaryReader:

  • Serializować 130.000 w słowniku: 22ms
  • Deserializuj 130 000 wpisów do słownika: 36ms

Kod na który wygląda tak:

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

Jak mówili inni jednak, jeśli chodzi o użytkowników manipulacji z plików, szyfrowanie, zamiast formatowania binarny jest droga naprzód.

+0

Dziękuję bardzo za sugestię! – Miguel

+0

Jak to się stało, że używasz BinaryReader/BinaryWriter? Dostaję mniej więcej w tym samym czasie przy użyciu FileReader/FileWriter i BinaryReader/BinaryWriter ... – Miguel

+1

@Miguel - tutaj jest mój plik testowy jednostki: http://pastie.org/1249910 - być może mój kod StreamReader/StreamWriter nie był tak wydajny jak twój –

1

Cóż, używając BinaryFormatter nie jest to bezpieczny sposób na przechowywanie par, jak można napisać bardzo prosty program, aby go (po, powiedzmy, reflektor działa na kodzie, aby uzyskać typ)

deserializowania

Co powiesz na szyfrowanie txt? Na przykład z takim przykładem jak this? (aby uzyskać maksymalną wydajność, spróbuj bez kompresji)

+0

Dziękuję bardzo za sugestię. Jaki jest wpływ na wydajność korzystania z szyfrowania? A jeśli dobrze rozumiem, jest to również niebezpieczne, ponieważ każdy użytkownik może go rozpakować, zmienić plik .txt i ponownie go skompresować, prawda? – Miguel

+1

Nie mam pojęcia, powinieneś prawdopodobnie sprawdzić swój przypadek. zauważ także odpowiedź Pietera, może być lepszym pomysłem na szyfrowanie (połączyłem się z biblioteką kompresji, która może również szyfrować) –

+0

@Miguel - Pamiętaj jednak, że istnieje bardzo duża szansa, że ​​twój wpływ na wydajność będzie niższy po połączeniu kompresji i szyfrowania ponieważ twoje IO będzie niższe. Jak powiedział @ohadsc, po prostu wypróbuj i zobacz, co daje. –

3

Jeśli chcesz bezpiecznie przechowywać dane, możesz zaszyfrować zawartość. Jeśli po prostu zaszyfrujesz go jako ciąg i odszyfrujesz przed bieżącą logiką parsowania, powinieneś być bezpieczny. A to nie powinno mieć tak dużego wpływu na wydajność.

Aby uzyskać więcej informacji, zobacz Encrypt and decrypt a string.

3

Szyfrowanie odbywa się kosztem zarządzania kluczami. I, oczywiście, nawet najszybsze algorytmy szyfrowania/odszyfrowywania są wolniejsze niż brak szyfrowania. To samo z kompresją, która pomoże tylko, jeśli jesteś związany z I/O.

Jeśli twoim głównym problemem jest wydajność, zacznij od tego, gdzie w rzeczywistości jest wąskie gardło. Jeśli sprawcą naprawdę jest wywołanie Convert.ToInt32(), wyobrażam sobie, że możesz przechowywać bity Int32 bezpośrednio i uzyskać prostą obsadę, która powinna być szybsza niż analiza wartości ciągu. Aby zaciemnić ciągi znaków, możesz ustawić każdy bajt z pewną ustaloną wartością, która jest szybka, ale zapewnia niczym więcej niż zwykłe zadanie dla określonego agresora.

1

Może coś takiego:

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

Uwaga to nie robić nic ponownego szyfrowania; to jest oddzielna sprawa. Można również stwierdzić, że dodając do mieszanki opróżnić pliku IO zmniejsza i zwiększa wydajność:

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

Czy to bezpieczne wystarczy użyć BinaryFormatter zamiast zapisywania zawartości bezpośrednio w pliku tekstowym? Oczywiście, że nie. Ponieważ inni mogą łatwo "zniszczyć" plik otwierając go za pomocą notatnika i dodając coś, nawet jeśli widzi tylko dziwne postacie. Lepiej, jeśli przechowujesz go w bazie danych. Ale jeśli upierasz się przy swoim rozwiązaniu, możesz łatwo poprawić wydajność, używając Parallel Programming w C# 4.0 (możesz łatwo uzyskać wiele przydatnych przykładów, googlując). Coś wygląda następująco:

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

Inną alternatywą rozwiązanie Parallel tworzy kilka wątków, odczyt z/zapis do różnych plików będzie szybciej.

Powiązane problemy