寻找最快的大文件拷贝方法(转自:http://www.cnblogs.com/hesicong/archive/2007/08/18/860763.html)

众所周知微软的操作系统自带的拷贝是很“弱智”的,速度不高,无断点续传,而且拷贝会拖累其他的应用程序,占用大量的文件缓存。所以很多高级的拷贝工具孕育而生,用过最好的是FastCopy。FastCopy的拷贝速度基本上可以达到磁盘的极限,还因为他开源,所以可以看到其实现。但是很可惜他的工程是VC6的,而且源代码注释都是日文的,不仅如此,其源代码风格很让人迷惑。证实了我的那句话:开源软件的最高境界就是,我开源了,你看不懂;等你看懂了,已经过时了。
  要达到最快的拷贝速度和减少对内存的占用,需要对拷贝的过程有一个了解。拷贝无非就是将文件的数据读出来,然后再写进去的一个过程。XP操作系统自带的拷贝工具会首先打开文件句柄,然后将一块数据读取到缓存中,然后再写入到磁盘中。打开“Windows任务管理器”,进程,查看,选择列,打开I/O读取字节,I/O写入字节。拷贝一个文件,注意explorer.exe进程即可看到整个读写过程。基本上可以看到XP对于文件拷贝几乎是属于同时进行的,换句话说其开的缓存比较小,但其效率可能并不见得很高。在我的200G Seagate 7200.8硬盘上,复制速度在15M/s左右。而这个硬盘的平均读取速度在40M/s,平均写入速度也在35M/s以上。
  在Vista下面文件拷贝做了一些优化,虽然一些BUG导致复制小文件会感觉很慢,但是复制大文件的思路已经不同于XP了。还是打开任务管理器,进行同样的操作。会发现Vista的会读取将近100M后,再将文件写入磁盘。explorer.exe进程也会在拷贝的瞬间内存占用飙升到100M以上,我的电脑商测试是120M左右,而复制完成以后内存占用将恢复正常。Vista的状态显示复制速度在18M/s左右。还是没有达到硬盘的极限速度。
观察Vista和XP的拷贝过程可以得出一个结论,Vista试图对磁盘的拷贝做优化了,但是其无论XP的分小块的复制,还是Vista的大缓存大块复制,都不能达到磁盘的最快速度。
  在两个操作系统复制的过程中,你会发现一个有趣的现象。XP的“任务管理器”的“性能”页面种的“物理内存”种的“系统缓存”的值会不断的增大,大到一个值以后就不会在增长。系统缓存主要用于缓存使用过的一些程序的内存、缓存打开并读写过的文件,已达到更快的读写速度。Win32 API的CreateFile函数默认是使用系统缓存的读写,所以简单的用CreateFile打开的文件是要先到系统缓存的。explorer也是这样,所以当你打开了比较多的后台程序,复制完一个大文件以后,再打开这些后台程序就变得十分缓慢,硬盘不停的读取。这是因为文件缓存占用了太多的内存空间的缘故,将一些程序缓存占用了,所以后台程序会变得十分缓慢。Vista下面这种情况要好一些。虽然这样的设计可以加速很多文件操作的应用,但是对于文件拷贝这样的一次性操作,使用系统缓存固然就是浪费资源了。
  我还发现当使用FastCopy的拷贝大文件的时候会出现另一个现象,就是“系统缓存”会骤降,磁盘读写速度基本达到极限。在XP下面能够改善后台程序的性能,因为此时FastCopy使用的是不使用操作系统缓存的读写操作,软件自生打开了一个32M的缓存(可自定义)。Vista下面的行为有些古怪,“系统缓存”也会减少,但是当复制完以后,硬盘会不断的读取,直到达到复制之前的大小,XP无此现象。

那么怎么样才能达到极限速度呢?是需要缓存还是不需要缓存呢?要缓存需要多大的缓存才好呢?为此我做了一个小实验。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;


namespace csharp
{
    class Program
    {
        public const short FILE_ATTRIBUTE_NORMAL = 0x80;
        public const short INVALID_HANDLE_VALUE = -1;
        public const uint GENERIC_READ = 0x80000000;
        public const uint GENERIC_WRITE = 0x40000000;
        public const uint CREATE_NEW = 1;
        public const uint CREATE_ALWAYS = 2;
        public const uint OPEN_EXISTING = 3;
        public const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
        public const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
        public const uint FILE_SHARE_READ = 0x00000001;
        public const uint FILE_SHARE_WRITE = 0x00000002;

        // Use interop to call the CreateFile function.
        // For more information about CreateFile,
        // see the unmanaged MSDN reference library.
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
          uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
          uint dwFlagsAndAttributes, IntPtr hTemplateFile);

        static void Main(string[] args)
        {
            bool useBuffer = false;
            SafeFileHandle fr = CreateFile("d:\\source", GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, useBuffer? 0:FILE_FLAG_NO_BUFFERING , IntPtr.Zero);
            SafeFileHandle fw = CreateFile("d:\\dest", GENERIC_WRITE, FILE_SHARE_READ, IntPtr.Zero, CREATE_ALWAYS,useBuffer? 0:(FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH), IntPtr.Zero);

            int bufferSize = useBuffer? 1024*1024*32:1024 * 1024 * 32;

            FileStream fsr = new FileStream(fr, FileAccess.Read);
            FileStream fsw = new FileStream(fw, FileAccess.Write);

            BinaryReader br = new BinaryReader(fsr);
            BinaryWriter bw = new BinaryWriter(fsw);

            byte[] buffer = new byte[bufferSize];
            Int64 len = fsr.Length;
            DateTime start = DateTime.Now;
            TimeSpan ts;
            while (fsr.Position < fsr.Length)
            {
                int readCount = br.Read(buffer, 0, bufferSize);
                bw.Write(buffer, 0, readCount);
                ts = DateTime.Now.Subtract(start);
                double speed=(double)fsr.Position / ts.TotalMilliseconds * 1000 / (1024 * 1024);
                double progress=(double)fsr.Position / len * 100;
                Console.WriteLine("Speed:{0},  Progress:{1}",speed ,progress );
            }
            br.Close();
            bw.Close();
            sw.Close();
            Console.WriteLine("End");
            Console.ReadLine();
        }
    }
}


整个程序的思路比较简单,打开文件,读取数据到自定义的缓存,然后写入数据,关闭文件。.NET默认的FileStream默认是缓存的读写,而且没有参数指定非缓存的读写。但好在FileStream的一个构造函数能够很方便的传递一个句柄并为之所用。查阅MSDN的SafeFileHandle时发现了CreateFile的用法,只需要传递一个FILE_FLAG_NO_BUFFERING给CreateFile,就可以实现不使用系统缓存的读写。但使用非缓存的读写有一些操作上的限制,详细的可以见MSDN的相关文档。
  运行以上程序,对一个Segate 80G的硬盘的D盘(硬盘自身缓存8M,其拷贝极限速度在26M/s左右)的一个大文件进行复制操作,得到的结果如下:

缓存大小

不使用系统缓存
拷贝速度(MB/s)

使用系统缓存
拷贝速度(MB/s)
1M 11.99 n/a
2M 15.19 n/a
4M 20.42 n/a
8M 23.87 n/a
16M 25.09 n/a
32M 25.93 11.31
64M n/a 15.89
128M n/a 17.02
由于将在程序中将bufferSize设置为64M会导致出现异常,所以64M没有数据。对于使用缓存的拷贝的速度小于32M缓存的读取速度很慢,没有进行更多的测试。
  结果很明显,在同样的自定义缓存大小的同时,不使用系统缓存的拷贝速度明显要高于使用系统缓存的拷贝速度。当不使用系统缓存的拷贝时,当缓存大小等于磁盘物理缓存大小的时候拷贝速度就达到了90%的最大速度;当等于磁盘物理缓存2倍时基本达到磁盘存取极限。由于上述原因,这也就是为什么FastCopy不使用系统缓存的缘故了。
  当然如果大家有什么更好的看法或想法,欢迎留言探讨,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FastCopy ver2.08 2011/02/28 SHIROUZU Hiroaki FastCopy is the Fastest Copy/Delete Software on Windows. It can copy/delete unicode and over MAX_PATH(260byte) pathname files. It always run by multi-threading. It don't use MFC, it is compact and don't requre mfcxx.dll. FastCopy is BSD license, you can modify and use. License: --------------------------------------------------------------- Copyright 2004-2011 SHIROUZU Hiroaki All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY SHIROUZU Hiroaki ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SHIROUZU Hiroaki OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------- Usage丗 Please see fastcopy.chm Build: FastCopy/Install/ShellExt ... VC4.1 ShellExt64 ... x64 compiler
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值