我在C#中编写了一个应用程序,它将jpgs从一组目录同时移动到另一组目录(每个固定子目录一个线程)。代码看起来像这样:
string destination = "";
DirectoryInfo dir = new DirectoryInfo("");
DirectoryInfo subDirs = dir.GetDirectories();
foreach (DirectoryInfo d in subDirs)
{
FileInfo[] files = subDirs.GetFiles();
foreach (FileInfo f in files)
{
f.MoveTo(destination);
}
}
然而,应用程序的性能是可怕的 - 大量的页面错误/秒。每个子目录中的文件数量可能会非常大,所以我认为一个很大的性能损失来自一个上下文切换,它不能同时将所有不同的文件阵列保存在RAM中,这样它就会进入磁盘几乎每一次。
我能想到两种不同的解决方案。第一种是用C或C ++重写它,第二种是使用多个进程而不是多线程。
编辑:文件根据时间戳命名,它们移动到的目录基于该名称。因此,它们被移动到的目录将对应于它创建的小时;例如3-27-2009 / 10。
我们正在为每个目录创建一个后台工作程序来进行线程化。
有什么建议吗?
答案 0 :(得分:18)
经验法则,不要将操作与串行依赖关系并行化。在这种情况下,您的硬盘驱动器是瓶颈,许多线程只会使性能变差。
如果您要使用线程,请尝试将数量限制为您可用的资源数量,核心和硬盘数量不是您要挂起的作业数量,要复制的目录。
答案 1 :(得分:7)
重新考虑回答
我一直在重新考虑下面的原始答案。我仍然怀疑使用更少的线程可能是一个好主意,但是因为你只是移动文件,它实际上不应该是IO密集型。 列出文件可能会占用大量磁盘。
但是,我怀疑你的文件内存真的不足。你有多少记忆力?这个过程占用了多少内存?您使用了多少个线程,以及您拥有多少个核心? (使用明显多于核心的线程是一个坏主意,IMO。)
我建议采取以下攻击计划:
原始回答
用C或C ++重写无济于事。使用多个过程无济于事。你正在做的就是给一个处理器一百个线程 - 除了你用磁盘做它。
如果还涉及相当多的计算,并行使用IO的任务是有意义的,但是如果它已经是磁盘绑定的,那么要求磁盘同时处理大量文件是只会让事情变得更糟。
您可能对我最近运行的基准测试(description和initial results感兴趣,测试文件各行的“加密”。当“加密”级别较低时(即它几乎不做任何CPU工作),最好的结果总是只有一个线程。
答案 2 :(得分:6)
如果您的工作块依赖于系统瓶颈,在这种情况下是磁盘IO,那么最好不要使用多个线程或进程。您最终将要做的就是在等待磁盘时产生大量额外的CPU和内存活动。如果您使用单个线程进行移动,您可能会发现应用程序的性能得到了改善。
答案 3 :(得分:2)
看来你正在移动目录,当然只需重命名/移动目录即可。如果你使用相同的源和硬盘,那将是即时的。
同样捕获每个文件的所有文件信息都是不必要的,文件的名称就足够了。
答案 4 :(得分:1)
性能问题来自硬盘驱动器用C / C ++做任何事情都没有意义,也没有从多个进程做任何事情
答案 5 :(得分:1)
您是否正在查看页面错误计数并从中推断内存压力?您可能会发现底层的Win32 / OS文件副本使用映射文件/页面错误来完成其工作,并且故障并不是问题的标志。 Window的大部分文件处理是通过页面错误完成的(例如'加载'可执行代码) - 它们本身并不是坏事。
如果 遭受内存压力,那么我猜测它更可能是由创建大量线程(非常昂贵)而不是文件复制引起的。
如果没有分析,请不要更改任何内容,如果您分析并发现时间花在框架方法上,这些方法只是Win32函数的包装器(下载框架源并查看这些方法是如何工作的),那么就不要浪费时间在C ++上。
答案 6 :(得分:0)
如果GetFiles()确实返回了大量数据,您可以编写一个枚举器,如:
IEnumerable<string> GetFiles();
答案 7 :(得分:0)
那么,您是将文件从一个子文件夹一次一个地移动到另一个子文件夹?当驱动器头来回移动时,你不会引起大量的磁盘搜索吗?通过将文件读入内存可以获得更好的性能(至少批量生成,如果不是全部一次),将它们写入磁盘,然后从磁盘中删除原件。
如果您在不同的线程中执行多组文件夹,那么您将更多地移动磁盘头。这是多个线程对你没有帮助的一种情况(尽管如果你有RAID或SAN,你可能会得到一些好处)。
如果您以某种方式处理文件,那么如果不同的CPU可以同时计算多个文件,则mulptithreading可能会有所帮助。但是你不能让四个CPU同时将一个磁盘头移动到四个不同的位置。