【问题标题】:Saving a Dictionary<String, Int32> in C# - Serialization?在 C# 中保存 Dictionary<String, Int32> - 序列化?
【发布时间】:2011-04-30 15:07:42
【问题描述】:

我正在编写一个 C# 应用程序,该应用程序需要在启动时读取大约 130,000 个(字符串,Int32)对到字典。这些对存储在 .txt 文件中,因此任何人都可以轻松修改,这在上下文中是危险的。我想问一下有没有办法保存这个字典,这样信息可以合理安全地存储,而不会在启动时失去性能。我曾尝试使用BinaryFormatter,但问题是,虽然原始程序在启动时需要 125 毫秒到 250 毫秒才能从 txt 中读取信息并构建字典,但反序列化生成的二进制文件最多需要 2 秒,这还不算太本身很多,但与原始性能相比,速度降低了 8-16 倍。

注意:加密很重要,但最重要的应该是一种从磁盘(可能从二进制文件)保存和读取字典的方法,而不必在每一行都使用 Convert.ToInt32 ,从而提高性能。

【问题讨论】:

  • 比 tp 慢 8-16 倍 什么,您目前使用什么?如果 BinaryFormatter 不是最快的,我会感到惊讶。
  • 我正在使用 StreamReader 的 ReadLine()' method, and using Convert.ToInt32` 从 .txt 文件中读取字符串作为整数。这两个操作中的每一个都在启动时完成了大约 131,000 次。

标签: c# performance serialization binary


【解决方案1】:

有趣的问题。我做了一些快速测试,你是对的 - BinaryFormatter 出奇的慢:

  • 序列化 130,000 个字典条目:547ms
  • 反序列化 130,000 个字典条目:1046ms

当我使用带有逗号分隔值的 StreamReader/StreamWriter 对其进行编码时,我得到:

  • 序列化 130,000 个字典条目:121ms
  • 反序列化 130,000 个字典条目:111ms

但后来我尝试只使用 BinaryWriter/BinaryReader:

  • 序列化 130,000 个字典条目:22ms
  • 反序列化 130,000 个字典条目:36ms

代码如下:

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

正如其他人所说,如果您担心用户篡改文件,那么加密而不是二进制格式是前进的方向。

【讨论】:

  • 非常感谢您的建议!
  • 您是如何使用 BinaryReader/BinaryWriter 获得如此不同的?我使用 FileReader/FileWriter 和 BinaryReader/BinaryWriter 的时间大致相同...
  • @Miguel - 这是我的单元测试文件:pastie.org/1249910 - 可能是我的 StreamReader/StreamWriter 代码不如你的高效
  • 非常感谢马克。但是使用您的代码,我得到了类似的结果......这样会发生什么?
  • 奇怪 - 我使用的是 Windows XP 和 .NET 3.5,也许你的设置不同。你在准确地运行我的测试吗?可能是在我的 CustomBinarySerializer 上预先调整字典的大小对其速度优势有很大贡献
【解决方案2】:

如果您想让数据相对安全地存储,您可以对内容进行加密。如果您只是将其加密为字符串并在当前解析逻辑之前对其进行解密,那么您应该是安全的。而且,这不会对性能产生太大影响。

更多信息请参见Encrypt and decrypt a string

【讨论】:

    【解决方案3】:

    加密是以密钥管理为代价的。当然,即使是最快的加密/解密算法也比不加密要慢。与压缩相同,只有在您受 I/O 限制时才会有所帮助。

    如果性能是您主要关心的问题,请开始查看瓶颈的实际位置。如果罪魁祸首确实是 Convert.ToInt32() 调用,我想您可以直接存储 Int32 位并使用简单的强制转换,这应该比解析字符串值更快。要混淆字符串,您可以使用某个固定值对每个字节进行异或运算,这速度很快,但对于坚定的攻击者来说只不过是一个障碍。

    【讨论】:

      【解决方案4】:

      可能是这样的:

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

      请注意,这不会重新加密;这是一个单独的问题。您可能还会发现在组合中添加 deflate 会减少文件 IO 并提高性能:

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

      【讨论】:

        【解决方案5】:

        使用BinaryFormatter 而不是直接将内容存储在文本文件中是否足够安全?显然不是。因为其他人可以很容易地通过记事本打开文件并添加一些东西来“破坏”文件,即使他只能看到奇怪的字符。最好将其存储在数据库中。但是如果你坚持你的解决方案,你可以通过在 C#4.0 中使用Parallel Programming 轻松提高性能(你可以通过谷歌搜索轻松获得很多有用的示例)。看起来像这样:

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

        Parallel 的另一个替代解决方案是创建多个线程,读取/写入不同文件会更快。

        【讨论】:

          【解决方案6】:

          嗯,使用 BinaryFormatter 并不是一种真正安全的存储对的方法,因为您可以编写一个非常简单的程序来反序列化它(例如,在您的代码上运行反射器以获取类型之后)

          如何加密txt? 例如this 之类的东西? (为了获得最佳性能,请尝试不压缩)

          【讨论】:

          • 非常感谢您的建议。使用加密对性能有什么影响?而且,如果我理解得很好,这也是不安全的,因为任何用户都可以解压缩,更改 .txt 并再次压缩,对吗?
          • 我不知道,您可能应该针对您的情况进行测试。还要注意 Pieter 的回答,对于加密可能是一个更好的主意(我链接到一个压缩库,它也可以加密)
          • @Miguel - 请注意,当您结合压缩和加密时,您的性能影响很有可能会降低,因为您的 IO 会更低。正如@ohadsc 所说,只需尝试一下,看看它能给你带来什么。
          • @Pieter true,但您可以使用“无压缩”设置
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-12
          • 1970-01-01
          • 2022-10-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多