【问题标题】:How to speed up binary read/write如何加快二进制读/写
【发布时间】:2018-10-29 02:18:05
【问题描述】:

我有这个代码

//N = 32;
//B = 27;
using (FileStream fs = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
    using (BinaryReader br = new BinaryReader(fs))
    {
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            for (int k = B; k < N; ++k)
            {
                Console.WriteLine(k);
                long pt = 0;
                long j = 1L << k;
                for (long i = 0; i < (1L << (N - 1)); ++i)
                {
                    long b1;
                    long b2;

                    br.BaseStream.Seek(8 * (pt), SeekOrigin.Begin);
                    b1 = br.ReadInt64();
                    br.BaseStream.Seek(8 * (j - 1), SeekOrigin.Current);
                    b2 = br.ReadInt64();

                    long t1 = b1 + b2;
                    long t2 = b1 - b2;

                    bw.BaseStream.Seek(8 * (pt), SeekOrigin.Begin);
                    bw.Write(t1);
                    bw.BaseStream.Seek(8 * (j - 1), SeekOrigin.Current);
                    bw.Write(t2);

                    pt += 1;
                    if ((pt & (j - 1L)) == 0)
                    {
                        pt += j;
                    }
                    if ((i % 100000) == 0) Console.WriteLine(i);
                }
            }
        }
    }
}

发生的情况是,程序从一个非常大 (17 GB) 文件中的不同位置读取两个 long,添加/减去它们,然后在相同位置重写新值。

据我所知,读取数据的最有效方法是将大块读取到缓冲区中,然后使用它。但是,这种方法在这里不起作用,因为根据 ptj 的值,它可以从文件的开头和结尾读取,当然我无法将所有 17 GB 存储在内存中。

线

if ((i % 100000) == 0) Console.WriteLine(i);

用于调试,在我的计算机上它们之间大约 2 秒。我需要这个更快。我正在关注的论文说,他们的实现在这个循环中花费了不到 30 分钟。有没有更快的方法来快速读取大量数值数据?

【问题讨论】:

  • 停止向控制台写入时会发生什么?
  • 此操作的主要部分将是磁盘操作。数据要么在内存中,要么必须从磁盘读取。您可以尝试将其留给磁盘缓存,但它们也会因两个 17 GiB 文件而负担过重。磁盘非常缓慢。唯一可能较慢的是网络。
  • 我可以肯定地说有一种更快的方法来做你正在做的事情。但是,如果没有法医逐行检查您的代码,和/或理解或猜测您要解决的实际问题是什么,以及您拥有的神奇数字是什么,除了说“是”之外,不可能给您任何建议可能可以加快速度
  • @RichardHubley: 1) 虽然我同意实际写入控制台可能很昂贵,但这并不是 2 秒的成本。 2)他已经说过它只是为了调试。 3) 这是磁盘操作。没有很多其他事情会与这些相关。

标签: c# io binaryfiles binary-data


【解决方案1】:

这本身并不是一个真正的答案。但是,它应该为您提供有关如何具体加快速度的想法

乍一看,这分为概率、平行度和卡盘尺寸。

如果很有可能在较大的块中找到下一次/写入读取,则较大的块大小将是性能增益。反过来,它不必继续扫描磁盘。

如果您使用的是 SSD,您可能会以比它可能使用的默认 4k​​ 块更高的性能方式(一次)加载大量 Mbs。

此外,看起来这可以分解为并行工作负载...虽然确实不清楚一开始您需要进行哪些修改。

但是,如果你真的想要这么快

  • 去给自己买 32 gig 的 ram
  • 创建一个继承的 Stream Class,或者更好的只是一个自定义类
  • 将整个数据集加载到内存中,分成大约 1 gig 的块数组。
  • 利用直接指针访问
  • 使用并行工作负载

如果你能做到这一点,(这是推测性的)你可能会更快地加速许多因素。还要花费几百美元的内存和一天的编码。

来自 @NPras的精彩评论

除了自己管理 RAM 缓存/分块之外,您还可以 想看看memory-mapped files的概念,让OS 为您管理它

来自Managing Memory-Mapped Files

【讨论】:

  • 谢谢!我不是全职程序员或任何东西,所以我对使用 C# 的更好原则感到好奇。但是,我认为我不妨只购买那个 RAM,然后我可以将整个输入向量加载到其中,而根本不必从磁盘读取。为什么在 RAM 中分解数组会更快?
  • @HiddenBabel 被警告,它不像加载 17 gig 那样简单,你需要打破它,因为我相信 .net 中的数组大小是 2 gig。不过老实说,我很想把我的牙齿投入其中,看看我能多快得到它。
  • 哦,这就是为什么它在尝试制作更大的数组时会引发错误......我将开始研究这些东西。我也没有在 C# 中使用过指针。但如果你编辑更多信息,我会回来看看哈哈。
  • 与其自己管理 RAM 缓存/分块,您可能还想看看 memory-mapped files 的概念并让操作系统为您管理它
  • @NPras 很棒的评论。您介意我将其添加到答案中吗?
【解决方案2】:

如果我理解正确,结果会写回到您刚刚读取的位置。

因此,如果您颠倒写入顺序,第一次写入将与您上次读取的位置相同。

这将减少寻道时间。

此外,这意味着下一次读取也将与另一次写入连续,再次减少寻道时间。

现在“i”上的主循环显然很长,但我认为你可以:

  • 将其分成中等大小的块(您可能只需要 64M 左右)
  • 执行整个读取块
  • 执行第二个读取块
  • 对两个块进行内存计算
  • 写出来

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-01
    • 2011-01-03
    • 1970-01-01
    • 2012-06-18
    • 2012-01-26
    • 2018-07-26
    • 2016-06-14
    • 2021-06-03
    相关资源
    最近更新 更多