我正在使用代码将文件从一个位置复制到另一个位置,并在复制过程中生成校验和。对于小文件,该代码可以正常运行,但是对于大文件(例如3.8GB),它的表现非常奇怪:在复制了大约1GB后,复制速度突然变慢,然后越来越慢(例如在达到1GB之前,每秒钟会复制约2%-4%的文件,而在达到1GB后,每%文件需要4-6秒钟)。
int bytesRead = 0;
int bytesInWriteBuffer = 0;
byte[] readBuffer = new byte[1638400];
byte[] writeBuffer = new byte[4915200];
MD5 md5Handler = new MD5CryptoServiceProvider();
using (FileStream sourceStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
md5Handler.TransformBlock(readBuffer, 0, bytesRead, null, 0);
FileStream destinationStream = File.Create(storageFileName);
while (bytesRead = sourceStream.Read(readBuffer, 0, readBuffer.Length))
{
Buffer.BlockCopy(readBuffer, 0, writeBuffer, bytesInWriteBuffer, bytesRead);
bytesInWriteBuffer += bytesRead
if (bytesInWriteBuffer >= 4915200)
{
destinationStream.Write(writeBuffer, 0, bytesInWriteBuffer);
bytesInWriteBuffer = 0;
Thread.Sleep(50);
}
}
}
正如评论中提到的那样:没有可以观察到的内存泄漏。方法开始时,内存使用量增加,然后保持稳定状态(包括在运行该方法的PC上运行所有应用程序的总内存使用量为56%)。 PC的总内存为8 GB。
应用程序本身是32位的(本身占用约300 MB的内存),所用的框架是4.5。
根据评论建议的测试更新:当我复制并通过令牌取消它并删除文件(所有这些都发生在减速开始之后),并立即开始第二个复制过程时,它与时间我取消它时的另一个复制一样慢(因此,在1 GB之前就已经开始减速)。但是,当我在删除完成后进行第二次复制时,它开始正常工作,并且只在1 GB处减速。
同时刷新目标流不会有任何区别。
对于减速而言,最初的复制速度约为84 MB /秒,到1 GB时减速至约14 MB /秒。
作为这个问题的一部分(不确定是否更适合作为评论):这可能不是C#相关的问题,而是OS缓存机制的“唯一”问题吗? (如果是这样,是否可以在那里做些事情)
根据建议,我查找了OS的写入缓存,还让性能监视器运行。
结果:
不同的源硬盘和源桌面具有相同的结果,减速的时间也相同
OS中的写缓存(目标)已禁用
位于目标位置的服务器上的性能监视显示没有重要信息(写入队列长度仅一次为4,一次为2,写入时间/空闲时间以及每秒写入均未显示出使用缓存或其他东西的100%)
进一步的测试显示了以下行为:
- 如果通过在每次写入后进行200毫秒的Thread.Sleep来减慢复制本身,平均复制速率为30 MB / s,保持恒定 - 如果我改为在每传输500 MB或800 MB后每5秒钟(Thread.Sleep)加入延迟,则再次发生减速,等待根本没有任何变化 - 如果我更改位置,以便源和目标位于我的本地硬盘上(通常目标位于网络文件夹中),则速率保持在50 MB / s的恒定状态,其中读取时间为100%,瓶颈在那里,写入时间远低于100%。 - 网络传输监视没有显示任何意外情况 - 当从相同的源复制3 GB文件到相同的目标时,Windows资源管理器的传输率为11 MB / s(因此尽管总体上会发生减速,但C#复制方法比Windows资源管理器复制更快)。
进一步行为:
- 根据监控显示,所有数据都以恒定的速度传输到目标驱动器(因此没有快速的第一部分和减速,但目标驱动器不断以相同的速度接收字节)。
此外,总体表现为3 GB文件的传输速度约为37 MB/s(首个GB为84 MB,其余GB为14 MB)。
Thread.Sleep()
看起来很奇怪? - David Heffernan