【问题标题】:Optimizing a "Patch" process优化“补丁”过程
【发布时间】:2013-08-05 20:50:37
【问题描述】:

对于某些项目,我需要覆盖文件,但由于用户可能同时使用其他程序编辑此文件,因此我不经常在运行时保留流,而是将所有数据保存在字节数组中。保存我的程序时,应该只保存它编辑的区域,而不是整个文件。我已经(非常糟糕)编写了一个例程来这样做,但它预计会很慢,我不知道如何在这里提高性能。我实际上只是在循环整个数组,并不像看起来那么聪明:

    public Boolean Patch(string path)
    {
        FileStream fs = new FileStream(path, FileMode.Open);
        BinaryReader br = new BinaryReader(fs);
        BinaryWriter bw = new BinaryWriter(fs);
        if (fs.Length != this.rawdata.Length)
            throw new ArgumentException();
        for (int i = 0; i < this.rawdata.Length; ++i )
        {
            if (br.ReadByte() != rawdata[i])
            {
                fs.Position--;
                bw.Write(rawdata[i]);
            }
        }
        fs.Close();
        return true;
    }

【问题讨论】:

  • 这似乎是一场灾难。您不想保存整个文件,因为用户可能正在另一个程序中编辑它?您处理的唯一情况是文件大小不同。如果用户更改了您正在更改的确切代码块中的某些内容怎么办?答:您将覆盖他们的更改。本质上,零保证您的应用程序将保留用户所做的临时更改。如果用户从文件开头删除了 3 个字节,但在末尾追加了 3 个字节怎么办?然后一切都不同步了。
  • 最好的办法是锁定该文件,在应用关闭之前不要让用户在应用之外编辑它。

标签: c# performance file optimization patch


【解决方案1】:

您对硬盘驱动器(或任何其他流)的每次访问都是昂贵的。 将您的代码转换为使用下一个要读取的X(例如,1024)字节和要写入的Y(例如,1024)字节的缓存副本。

我并不完全理解你的代码应该做什么,但是说你想在流之间复制一个文件,你的函数应该是:

private const int BUFFER_SIZE = 1024;

void copy(BinaryReader inStream, BinaryWriter outStream)
{
    byte[] cache = new byte[BUFFER_SIZE];
    int readCount = 0;
    while ((readCount = inStream.Read(cache, 0, BUFFER_SIZE)) != 0)
    {
        outStream.Write(cache, 0, readCount);
    }
}

在这个例子中,BUFFER_SIZE 不能太小(这样批量读写会很有效),也不能太大——溢出你的内存。
在您的代码示例中,您每次都读取一个字节(即BUFFER_SIZE = 1),因此这会减慢您的应用程序的速度。

编辑:添加了您需要编写的代码:

public Boolean Patch(string path)
    {
        const int BUFFER_SIZE = 512;

        // VERY IMPORTANT: The throw operation will cause the stream to remain open the function returns.
        using (FileStream fs = new FileStream(path, FileMode.Open))
        {
            BinaryReader br = new BinaryReader(fs);
            BinaryWriter bw = new BinaryWriter(fs);
            if (fs.Length != this.rawdata.Length)
                throw new ArgumentException();
            byte[] cache = new byte[BUFFER_SIZE];
            int readCount = 0, location = 0;
            while ((readCount = br.Read(cache, 0, BUFFER_SIZE)) != 0) 
            {
                int changeLength = 0;

                for (int j = 0; j < readCount; j++)
                {
                    if (cache[j] != rawdata[j + location])
                    {
                        changeLength++;
                    }
                    else if (changeLength > 0)
                    {
                        fs.Position = location + j - changeLength;
                        bw.Write(rawdata, location + j - changeLength, changeLength);
                        fs.Position = location + j;

                        changeLength = 0;
                    }
                }

                location += readCount;
            } 

            return true;
        }
    }

【讨论】:

  • 我只需要把东西写到特定的地方,可以说是“修补”文件。我猜你的建议正是 BinaryWriter.Write(byte[]) 所做的。
  • 如果你在一个缓存的字节数组上工作会更好,然后将它作为一个整体写入所需的位置。
  • 了解这一点:硬盘驱动器是块设备,这意味着您一次访问其内容的块。直接访问内存(随机存取存储器或 RAM)单元。当您有一个普通数组时,它位于计算机的内存中,因此迭代其每个元素是基本且快速的。另一方面,当您在硬盘驱动器上有一个阵列时,每次加载它的一部分(尽可能大的内存可以处理)并对其进行迭代会更有效率。当您从硬盘驱动器加载某些内容时,对数据的访问不如对内存的访问快。
  • 是否可以选择复制数据数组并通过 MemoryStream 访问它,因为我不写入硬件会更快吗?不过我现在明白你的意思了。
  • 我添加了一个你应该写的例子。它可能有问题 - 我不能确定,但​​我认为它符合你一开始的意思。
猜你喜欢
  • 1970-01-01
  • 2019-05-25
  • 2011-06-21
  • 2014-12-15
  • 2012-02-02
  • 1970-01-01
  • 2019-01-04
  • 1970-01-01
  • 2022-06-14
相关资源
最近更新 更多