【问题标题】:outputing dictionary optimally优化输出字典
【发布时间】:2012-03-28 18:46:04
【问题描述】:

我有 4 个字典,其中包含 200 到 6000 个字符的 800k 字符串。 当我将它加载到内存中时,它会占用大约 11 GB 的内存。 解析数据需要 2 分钟,输出数据需要 2 分钟。 有没有比我在下面使用的更快地输出数据? 我每秒只能获得 20-31 MB 的磁盘 IO,而且我知道硬盘驱动器可以做 800 次

var hash1 = new Dictionary<int, Dictionary<string, string>>(f.Count + 2);
var hash2 = new Dictionary<int, Dictionary<string, string>>(f.Count + 2);
var hash3 = new Dictionary<int, Dictionary<string, string>>(f.Count + 2);
var hash4 = new Dictionary<int, Dictionary<string, string>>(f.Count + 2);
....
foreach (var me in mswithfilenames)
{
    filename = me.Key.ToString();
    string filenamef = filename + "index1";
    string filenameq = filename + "index2";
    string filenamefq = filename + "index3";
    string filenameqq = filename + "index4";

    StreamWriter sw = File.AppendText(filenamef);
    StreamWriter sw2 = File.AppendText(filenameq);
    StreamWriter swq = File.AppendText(filenamefq);
    StreamWriter sw2q = File.AppendText(filenameqq);

    for (i = 0; i <= totalinhash; i++)
    {
        if (hashs1[i].ContainsKey(filenamef))
        {
            sw.Write(hashs1[i][filenamef]);
        }
        if (hashs2[i].ContainsKey(filenameq))
        {
            sw2.Write(hashs2[i][filenameq]);
        }
        if (hashs3[i].ContainsKey(filenamefastaq))
        {
            swq.Write(hash4[i][filenamefastaq]);
        }

        if (hash4[i].ContainsKey(filenameqq))
        {
            sw2q.Write(hash4[i][filenameqq]);
        }
    }

    sw.Close();
    sw2.Close();
    sw3.Close();
    sw4.Close();
    swq.Close();
    sw2q.Close();
}

【问题讨论】:

  • 一目了然,您正在执行两倍于所需的哈希查找。请改用Dictionary.TryGetValue。如果返回 false 你知道它不存在,如果返回 true 你已经有了你的价值。留下评论,因为我没有时间在更高级别分析问题。另外....using 块是你的朋友。如果对Write 的任何调用抛出异常会发生什么?您不会随心所欲地在流后清理。
  • 这对code review 来说可能是一个更好的问题,因为您实际上并没有遇到问题。
  • @M.Babcock 感谢我在代码审查中发布了它
  • @Tigran mswithfilenames 有我用来将哈希输出到正确位置的文件名
  • 请不要在 SE 网站上交叉发布问题,因为这会给两个网站上的人带来不必要的重复和麻烦。

标签: c# performance dictionary disk-io


【解决方案1】:

你测量过什么吗?听起来您有大量数据要读取和写入 - 所以第一步是为您的磁盘子系统建立绝对基线,了解它读取/写入这么多数据的速度。简单地读取文件,然后写入您期望的大约数据量的新文件将显示您在优化它方面可以走多远。

您可能会觉得您的代码本身不会在读/写上花费太多时间。

【讨论】:

  • 是的,我确实测量了时间,只需将文件从一个文件夹复制并粘贴到另一个文件夹大约需要 3 秒(使用 855meg/s 的硬盘驱动器需要 2.5gigs)。我也想那么快地从记忆中写出来。
【解决方案2】:

最昂贵的部分是 I/O。而这个循环:

for (i = 0; i <= totalinhash; i++)
{
    if (hashs1[i].ContainsKey(filenamef))
    {
        sw.Write(hashs1[i][filenamef]);
    }
    if (hashs2[i].ContainsKey(filenameq))
    {
        sw2.Write(hashs2[i][filenameq]);
    }
    ...
}

在不同的文件之间交替。这可能会导致一些额外的头部运动并创建碎片文件(减慢对这些文件的未来操作)。

我会使用:

for (i = 0; i <= totalinhash; i++)
{
    if (hashs1[i].ContainsKey(filenamef))
    {
        sw.Write(hashs1[i][filenamef]);
    }
}

for (i = 0; i <= totalinhash; i++)
{
    if (hashs2[i].ContainsKey(filenameq))
    {
        sw2.Write(hashs2[i][filenameq]);
    }
}
...

但是,您当然应该衡量这一点。例如,它不会对 SSD 产生太大影响,仅在机械磁盘上​​。

【讨论】:

  • 感谢我在机械磁盘上​​,我会在稍后检查性能
【解决方案3】:

你能有一个Dictionary&lt;int, Dictionary&lt;string, myCustomDataHolder&gt;&gt;而不是四个独立的并行Dictionary&lt;int, Dictionary&lt;string, string&gt;吗?它不仅应该减少相当多的空间消耗,而且意味着 1/4 的字典查找。

鉴于您的问题,尚不清楚字典是否完全平行,但对我来说似乎足够了。

【讨论】:

  • 每个字典中的信息对于那个哈希来说都是唯一的,我真的不知道如何合并它们
  • 您在所有字典中使用文件名作为键,您只是在每个字典后面附加一个 'index1' 'index2' 等。如果您在同一个庄园中填充这些字典,只需使用普通文件名作为键而不附加“index1”等。如果数据已经附加了“index1”,你可以删除它吗?
  • 让我考虑几分钟,如果我尝试这样做,我可能会犯错误哈哈:)
  • +1,也许不是这个特定问题的正确答案,但对许多其他问题来说是正确的答案
【解决方案4】:

我想补充一下

if (hashs1[i].ContainsKey(filenamef))
{
   sw.Write(hashs1[i][filenamef]);
}

进行 2 次哈希表访问。一个用于包含键,一个用于实际访问。许多字典访问可以累加,因此您可以使用字典的 tryGetValue 方法将这些访问减半。这会将这两个调用合二为一。我可以解释它是如何工作的,但这比我做得更好:http://www.dotnetperls.com/trygetvalue

【讨论】:

    猜你喜欢
    • 2020-02-05
    • 2016-07-02
    • 2016-05-25
    • 2017-05-14
    • 1970-01-01
    • 2010-10-30
    • 1970-01-01
    • 2017-06-15
    • 2016-05-20
    相关资源
    最近更新 更多