【问题标题】:Faster file move method other than File.Move比 File.Move 更快的文件移动方法
【发布时间】:2015-03-07 04:01:22
【问题描述】:

我有一个控制台应用程序,大约需要 625 天才能完成。除非有办法让它更快。

首先,我在一个包含大约 4,000,000 个文件(如果不是更多的话)的目录中工作。我正在一个数据库中工作,每个文件都有一行,然后是一些。

现在使用 SQL 比较快,瓶颈是当我使用File.Move() 时,每次移动需要 18 秒才能完成。

有没有比File.Move()更快的方法?

这是瓶颈:

File.Move(Path.Combine(location, fileName), Path.Combine(rootDir, fileYear, fileMonth, fileName));

所有其他代码都运行得非常快。我需要做的就是将一个文件移动到一个新位置,然后更新数据库位置字段。

如果需要,我可以显示其他代码,但实际上以上是当前唯一的瓶颈。

【问题讨论】:

  • 如果您仍然使用数据库,为什么需要 4,000,000 个文件?
  • @TimSchmelter 他们最初是这样设计的。数据库包含文件中的一些信息,我需要更新的唯一部分是位置列。该列告诉应用程序他们使用文档所在的位置来打开它。
  • 如果每次移动需要 18 秒,那么其他地方就非常错了,这可能不是你使用 API 造成的。
  • @JamesWilson 那么操作系统更新包含目录可能​​需要很长时间。

标签: c# console-application


【解决方案1】:

事实证明,从 File.Move 切换到设置 FileInfo 并使用 .MoveTo 显着提高了速度。

现在大约需要 35 天,而不是 625 天。

FileInfo fileinfo = new FileInfo(Path.Combine(location, fileName));
fileinfo.MoveTo(Path.Combine(rootDir, fileYear, fileMonth, fileName));

【讨论】:

  • 这是个好信息。不过,看起来很奇怪。我可能需要研究为什么会这样。
  • @JimMischel 是的,我整天都在测试这个,这么多文件的速度一直在变化。我所能找到的只是 File.Move 在每次调用时检查权限/安全性,其中 fileInfo.MoveTo() 只检查一次。如果您发现任何其他信息,我很想知道。
  • 很奇怪。我没有发现任何速度提升:10529 ms (32824028 tiks) Directory.Move,13358 ms (41642456 tiks) new FileInfo().Move,10926 ms (34061807 tiks) File.Move()。它适用于 16385 个文件
【解决方案2】:

18 秒并不罕见。当单个目录中有大量文件时,NTFS 性能不佳。当您请求文件时,它必须对其目录数据结构进行线性搜索。对于 1,000 个文件,这不会花费太长时间。您会注意到 10,000 个文件。拥有 400 万个文件。 . .是的,这需要一段时间。

如果您将所有目录条目预加载到内存中,您可能会更快地执行此操作。然后,无需为每个文件调用 FileInfo 构造函数,您只需在字典中查找即可。

类似:

var dirInfo = new DirectoryInfo(path);
// get list of all files
var files = dirInfo.GetFileSystemInfos();
var cache = new Dictionary<string, FileSystemInfo>();
foreach (var f in files)
{
    cache.Add(f.FullName, f);
}

现在,当您从数据库中获取名称时,您可以在字典中查找它。这可能比每次都尝试从磁盘获取它要快。

【讨论】:

  • 我不敢对此进行测试,因为它需要将 400 万个文件加载到目录中,然后才能开始移动它们。然后,一旦它们在字典中,如果我没有记错,我仍然需要对文件执行 File.Move 或 fileinfo.MoveTo()?
  • @JamesWilson:是的,你仍然需要fileinfo.MoveTo()。这个想法是预加载所有条目将消除您必须逐个搜索它们。 400 万个条目是否是内存问题,我不知道。我也不知道加载需要多长时间,尽管我怀疑它不到一个小时。结果是否会比您的 35 天更快,我不确定。
【解决方案3】:

您可以并行移动文件,也可以使用 Directory.EnumerateFiles 为您提供一个延迟加载的文件列表(当然我还没有使用 4,000,000 个文件对其进行测试):

var numberOfConcurrentMoves = 2;
var moves = new List<Task>();
var sourceDirectory = "source-directory";
var destinationDirectory = "destination-directory";

foreach (var filePath in Directory.EnumerateFiles(sourceDirectory))
{
    var move = new Task(() =>
    {
        File.Move(filePath, Path.Combine(destinationDirectory, Path.GetFileName(filePath)));

        //UPDATE DB
    }, TaskCreationOptions.PreferFairness);
    move.Start();

    moves.Add(move);

    if (moves.Count >= numberOfConcurrentMoves)
    {
        Task.WaitAll(moves.ToArray());
        moves.Clear();
    }
}

Task.WaitAll(moves.ToArray());

【讨论】:

    猜你喜欢
    • 2013-11-03
    • 2015-12-16
    • 2012-12-04
    • 1970-01-01
    • 1970-01-01
    • 2018-06-11
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    相关资源
    最近更新 更多