加速硬盘备份代码的建议

Suggestions on speeding up hard drive backup code

我有以下硬盘备份代码,比较每个文件在复制前的 .LastWriteTime() 时间,它比我预期的要慢 运行ning。我的假设是,如果没有要更新的文件,它应该 运行 非常快(大约几分钟)。我发现通过 USB3.0 传输 210 GB 仍然需要一个多小时。我想知道我的代码中是否有任何不必要的、耗时的部分可以改进。我也在考虑将每个 directorycopy() 调用放在不同的线程上(至少对于目录的第一级,但不确定这是否是不好的做法)。

代码大部分借鉴自:

https://docs.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories

我进行了更改以忽略 $Recycle Bin 文件夹,记录已更改或存在长文件名等问题的文件,并仔细考虑异常的处理方式。但最重要的是,我在复制之前添加了一个检查以查看哪个文件较新。

    private void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
    {
        // Get the subdirectories for the specified directory.
        DirectoryInfo dir = new DirectoryInfo(sourceDirName);
        if (sourceDirName.Contains("$")) // avoids $Recycle Bin
            return;

        if (!dir.Exists)
        {
            textb_Status.AppendText("Issue with " + dir.FullName + " This folder will not be compied.");
            return;
            //throw new DirectoryNotFoundException(
               // "Source directory does not exist or could not be found: "
              //  + sourceDirName);
        }

        DirectoryInfo[] dirs = dir.GetDirectories();
        // If the destination directory doesn't exist, create it.
        if (!Directory.Exists(destDirName))
        {
            Directory.CreateDirectory(destDirName);
        }

        // Get the files in the directory and copy them to the new location.
        FileInfo[] files = dir.GetFiles();
        foreach (FileInfo file in files)
        {
            string temppath = Path.Combine(destDirName, file.Name);
            try
            {
                file.CopyTo(temppath);
            }
            catch (PathTooLongException)
            {
                textb_Status.AppendText("Filename Too long \n " + file.FullName + "\n");
            }
            catch (IOException ex)
            {
                FileInfo sourcefile = new FileInfo(file.FullName);
                FileInfo destFile = new FileInfo(temppath);
                int CompareValue = sourcefile.LastWriteTime.CompareTo(destFile.LastWriteTime); //<0==> Earlier (old)  =0 ==> same  >0 Later (newer)
                //textb_Status.AppendText("CompareValue: " + CompareValue + "\n");
                if (CompareValue > 0) // Represents newer file
                {
                    file.CopyTo(temppath, true);
                    textb_Status.AppendText("Updated: " + file.FullName + "\n");
                }
            }

            catch (Exception ex2)
            {
                textb_Status.AppendText("Issue with " + file.FullName + "\n");
                textb_Status.AppendText("Error Message \n");
                textb_Status.AppendText(ex2.Message + "\n");
            }

        }

        // If copying subdirectories, copy them and their contents to new location.
        if (copySubDirs)
        {
            foreach (DirectoryInfo subdir in dirs)
            {
                string temppath = Path.Combine(destDirName, subdir.Name);
                DirectoryCopy(subdir.FullName, temppath, copySubDirs);
            }
        }
    }

如果只有几个文件要更新,我预计备份过程大约需要几分钟。

我不认为,是数据量减慢了进程,而是文件数量。无论文件大小如何,初始文件访问(检查它是否存在,获取统计信息)都非常昂贵。此外,许多人考虑将异常用于控制流不良风格,并且抛出和捕获异常可能非常昂贵。从您的用例(即大多数文件未更改)来看,抛出了 MANY 个异常。

另外,根据您的磁盘(SSD 或 HDD),多线程读写可能是一个非常糟糕的主意,并且会减慢整个过程。

并且根据 File.Copy() 的实施,您可能会过得更好,首先检查目标,只有在确实有必要时才执行 Copy。但这是一个基准测试后才能知道的事情。

谢谢@derpirscher。对于每个文件,我在尝试写入之前检查它是否存在。这样,不会抛出异常。驱动器在大约 5 秒内完成检查!我修改了源目录深处的几个文件,以确保它们被检测到并被复制。他们是。

我觉得异常很昂贵,我只是不知道它这么糟糕......很棒的一课!

我的代码如下。注意:我在尝试从我的系统卷信息文件夹中获取文件时收到错误消息,因此我开始检查以确保 sourceDirName 不等于该目录。

    private void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
    {
        if (sourceDirName.Contains("System Volume Information"))
            return;
        //textb_Status.AppendText("Current Directory: " + sourceDirName +"\n");
        DirectoryInfo[] dirs = null;
        // Get the subdirectories for the specified directory.
        DirectoryInfo dir = new DirectoryInfo(sourceDirName);
        if (sourceDirName.Contains("$")) // avoids $Recycle Bin
            return;

        if (!dir.Exists)
        {
            textb_Status.AppendText("Issue with " + dir.FullName + " This folder will not be compied.");
            return;
            //throw new DirectoryNotFoundException(
            // "Source directory does not exist or could not be found: "
            //  + sourceDirName);
        }

        {

            dirs = dir.GetDirectories();

            // If the destination directory doesn't exist, create it.
            if (!Directory.Exists(destDirName))
            {
                Directory.CreateDirectory(destDirName);
            }

            // Get the files in the directory and copy them to the new location.
            FileInfo[] files = dir.GetFiles();
            foreach (FileInfo file in files)
            {
                string temppath = Path.Combine(destDirName, file.Name);
                try
                {
                    if (File.Exists(temppath)) // Check for newer
                    {
                        FileInfo sourcefile = new FileInfo(file.FullName);
                        FileInfo destFile = new FileInfo(temppath);
                        int CompareValue = sourcefile.LastWriteTime.CompareTo(destFile.LastWriteTime); //<0==> Earlier (old)  =0 ==> same  >0 Later (newer)
                                                                                                       //textb_Status.AppendText("CompareValue: " + CompareValue + "\n");
                        if (CompareValue > 0) // Represents newer file
                        {
                            file.CopyTo(temppath, true);
                            textb_Status.AppendText("********** Updated: " + file.FullName + "********* \n");
                        }
                    }
                    else
                    {
                        file.CopyTo(temppath);
                    }

                }
                catch (PathTooLongException)
                {
                    textb_Status.AppendText("Filename Too long \r\n\n " + file.FullName + "\r\n\n");
                }
                catch (IOException ex)
                {
                    FileInfo sourcefile = new FileInfo(file.FullName);
                    FileInfo destFile = new FileInfo(temppath);
                    int CompareValue = sourcefile.LastWriteTime.CompareTo(destFile.LastWriteTime); //<0==> Earlier (old)  =0 ==> same  >0 Later (newer)
                                                                                                   //textb_Status.AppendText("CompareValue: " + CompareValue + "\n");
                    if (CompareValue > 0) // Represents newer file
                    {
                        file.CopyTo(temppath, true);
                        textb_Status.AppendText("Updated: " + file.FullName + "\n");
                    }
                }

                catch (Exception ex2)
                {
                    textb_Status.AppendText("Issue with " + file.FullName + "\n");
                    textb_Status.AppendText("Error Message \n");
                    textb_Status.AppendText(ex2.Message + "\n\n");
                }
            }


            // If copying subdirectories, copy them and their contents to new location.
            if (copySubDirs)
            {
                foreach (DirectoryInfo subdir in dirs)
                {
                    string temppath = Path.Combine(destDirName, subdir.Name);
                    DirectoryCopy(subdir.FullName, temppath, copySubDirs);
                }

            }
        }
    }