我要分一个巨大的文件分成许多较小的文件。每个目的地的文件的定义是一个偏移量和长度的数字节。我使用了下列代码:

private void copy(string srcFile, string dstFile, int offset, int length)
{
    BinaryReader reader = new BinaryReader(File.OpenRead(srcFile));
    reader.BaseStream.Seek(offset, SeekOrigin.Begin);
    byte[] buffer = reader.ReadBytes(length);

    BinaryWriter writer = new BinaryWriter(File.OpenWrite(dstFile));
    writer.Write(buffer);
}

考虑到我有叫这个函数约100,000次,这是非常缓慢。

  1. 有一种方法,使作者直接连接到读者?(是,实际上并没有载入的内容,进入缓冲存储器中。)
有帮助吗?

解决方案

我不相信有什么内。净允许复制一部分的一个文件没有它在缓冲存储器。然而,它攻击我,这是效率低下,无论如何,因为它需要开输入文件并寻求的许多倍。如果你 只是 分裂的文件,为什么不打开输入文件的一次,然后只是写点东西,如:

public static void CopySection(Stream input, string targetFile, int length)
{
    byte[] buffer = new byte[8192];

    using (Stream output = File.OpenWrite(targetFile))
    {
        int bytesRead = 1;
        // This will finish silently if we couldn't read "length" bytes.
        // An alternative would be to throw an exception
        while (length > 0 && bytesRead > 0)
        {
            bytesRead = input.Read(buffer, 0, Math.Min(length, buffer.Length));
            output.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
    }
}

这有一个小的效率低下创建一个缓冲区,在每个调用-你可能会想要创造缓冲区的一次,通过得到的方法,以及:

public static void CopySection(Stream input, string targetFile,
                               int length, byte[] buffer)
{
    using (Stream output = File.OpenWrite(targetFile))
    {
        int bytesRead = 1;
        // This will finish silently if we couldn't read "length" bytes.
        // An alternative would be to throw an exception
        while (length > 0 && bytesRead > 0)
        {
            bytesRead = input.Read(buffer, 0, Math.Min(length, buffer.Length));
            output.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
    }
}

注意,这也将关闭的出流(由于采用发言)其原始代码没有。

重要的一点是,这会使用操作系统文件的缓冲效率更高,因为你重复使用相同的输入流,而不是重新讨论该文件的开始和然后在寻求。

想想 它将明显加快,但是很明显你需要试试看...

这假定毗连区块,当然。如果需要跳位的文件中,你可以做到这一点从以外的方法。还有,如果你正在写的非常小的文件,你可能想要优化这种情况过于简单的方法来做到这一点可能会引进一个 BufferedStream 包装的输入流。

其他提示

最快的方式来做到的文件I/O C#是使用Windows ReadFile和写文件的功能。我已经写C#类封装了这种能力以及基准制定程序,看起来在不同I/O方法,包括BinaryReader和BinaryWriter.看到我的博客中在:

http://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp/

有多大 length?你可以做的更好的再利用的一个固定的尺寸(适度大,但不是淫秽)缓冲区,忘记了 BinaryReader...只是使用 Stream.ReadStream.Write.

(编辑):

private static void copy(string srcFile, string dstFile, int offset,
     int length, byte[] buffer)
{
    using(Stream inStream = File.OpenRead(srcFile))
    using (Stream outStream = File.OpenWrite(dstFile))
    {
        inStream.Seek(offset, SeekOrigin.Begin);
        int bufferLength = buffer.Length, bytesRead;
        while (length > bufferLength &&
            (bytesRead = inStream.Read(buffer, 0, bufferLength)) > 0)
        {
            outStream.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
        while (length > 0 &&
            (bytesRead = inStream.Read(buffer, 0, length)) > 0)
        {
            outStream.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
    }        
}

你不应该重新开放源文件的每一次你做的副本,更好的打开一次,并通过所得BinaryReader的复制的功能。此外,它可能会帮助如果你为了你的要求,所以你不要让大的跳跃的内部文件。

如果长度不太大,你也可以尝试向集团的几个副本,电话分抵消附近的彼此和阅读整块你对他们的需要,例如:

offset = 1234, length = 34
offset = 1300, length = 40
offset = 1350, length = 1000

可以组一个阅读:

offset = 1234, length = 1074

然后你只有"寻求"在你的缓冲区,可以编写这三个新文件有没有读。

你有没有考虑使用CCR因为你写的单独的文件你可以做的一切并行(阅读和写)和CCR使得它很容易做到这一点。

static void Main(string[] args)
    {
        Dispatcher dp = new Dispatcher();
        DispatcherQueue dq = new DispatcherQueue("DQ", dp);

        Port<long> offsetPort = new Port<long>();

        Arbiter.Activate(dq, Arbiter.Receive<long>(true, offsetPort,
            new Handler<long>(Split)));

        FileStream fs = File.Open(file_path, FileMode.Open);
        long size = fs.Length;
        fs.Dispose();

        for (long i = 0; i < size; i += split_size)
        {
            offsetPort.Post(i);
        }
    }

    private static void Split(long offset)
    {
        FileStream reader = new FileStream(file_path, FileMode.Open, 
            FileAccess.Read);
        reader.Seek(offset, SeekOrigin.Begin);
        long toRead = 0;
        if (offset + split_size <= reader.Length)
            toRead = split_size;
        else
            toRead = reader.Length - offset;

        byte[] buff = new byte[toRead];
        reader.Read(buff, 0, (int)toRead);
        reader.Dispose();
        File.WriteAllBytes("c:\\out" + offset + ".txt", buff);
    }

这个代码的员额抵消CCR口导致一个线程,以执行代码在分裂的方法。这使你打开文件多次但摆脱需要同步。你可以让这更多记忆高效率但你不得不牺牲速度。

第一件事我会建议采取的测量。你失去你的时间吗?它是在阅读或写字吗?

100 000多名访问(总结的时期):花费多少时间分配的缓冲器阵列?花费多少时间打开文件的读取(这是相同文件每一次?) 多少时间花在阅读和写作?

如果你不做任何类型的转变在文件,你需要一个BinaryWriter,或者您可以使用一个文件流写入?(尝试,你获得完全相同的输出?它不会节省时间?)

使用文件流+StreamWriter我知道这有可能创造大量文件可以在很短的时间(不少于1分30秒)。我生成的三个文件共计700多兆从一个文件使用这种技术。

你的主要问题与你的代码使用的是你正在打开了文件的每一个时间。这是创建文件I/O开销。

如果你知道名字的文件将会产生的提前时间,你可以提取的文件。OpenWrite入一个单独的方法;它会增加的速度。没有看到码,确定如何你都是分裂的文件,我不认为你可以得到更快。

没有人建议穿?写的小文件看起来像文书的例子线是有用的。设置了一大堆的线创建较小的文件。这种方式,可以创造他们全部在并行,你不需要等待每一个要完成。我的假设是创建文件(磁盘操作)将采取的方式长于分裂的数据。当然,你应该检验第一顺序的做法是不适当的。

(为今后参考。)

很可能是最快的方式这样做将可使用的存储器映的文件(所以主要是复制记忆,和操作系统处理的文件的读写通过其寻呼/存储管理)。

存储器的映射的文件是支持在托管的代码。净4.0.

但是,正如所指出的,需要配置文件,并期望开关的司机代码最高性能。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top