从互联网下载文件时,最佳的内存缓冲区大小是多少?

15

从互联网下载文件时,最佳内存缓冲区大小是多少?有些示例说应该是1K。好的,我一般需要知道为什么?还有如果我们下载一个小的.PNG或者一个大的.AVI会有什么不同吗?

Stream remoteStream;
Stream localStream;
WebResponse response;

try
{
    response = request.EndGetResponse(result);

    if (response == null)
        return;

    remoteStream = response.GetResponseStream();

    var localFile = Path.Combine(FileManager.GetFolderContent(), TaskResult.ContentItem.FileName);
    localStream = File.Create(localFile);

    var buffer = new byte[1024];
    int bytesRead;

    do
    {
        bytesRead = remoteStream.Read(buffer, 0, buffer.Length);
        localStream.Write(buffer, 0, bytesRead);
        BytesProcessed += bytesRead;
    } while (bytesRead > 0);
}

1024 - 大到足够有用,小到可以频繁更新进展(如果需要) - musefan
有任何官方的微软建议或最佳实践吗?你知道吗? - NoWar
1
不,这并不是非常关键的。当你仍在忙于写入前一个块时,Windows会使用自己的缓冲区来存储发送给你的内容。这就是为什么你的缓冲区并不是太关键。 - MSalters
6个回答

23

说实话,我测试了使用二的幂次方(2、4、8、16……)的递进大小来读取一个1484 KB的文本文件。我将每个大小所需的毫秒数打印到控制台窗口上。超过8192后,差别似乎不大了。这是在我的Windows 7 64位机器上的结果。

2^1 = 2 :264.0151
2^2 = 4 :193.011
2^3 = 8 :175.01
2^4 = 16 :153.0088
2^5 = 32 :139.0079
2^6 = 64 :134.0077
2^7 = 128 :132.0075
2^8 = 256 :130.0075
2^9 = 512 :133.0076
2^10 = 1024 :133.0076
2^11 = 2048 :90.0051
2^12 = 4096 :69.0039
2^13 = 8192 :60.0035
2^14 = 16384 :56.0032
2^15 = 32768 :53.003
2^16 = 65536 :53.003
2^17 = 131072 :52.003
2^18 = 262144 :53.003
2^19 = 524288 :54.0031
2^20 = 1048576 :55.0031
2^21 = 2097152 :54.0031
2^22 = 4194304 :54.0031
2^23 = 8388608 :54.003
2^24 = 16777216 :55.0032

2
有趣的结果。我会说看起来16k才是魔法数字,而不是8k。那里仍然有7%的改进。 - Will Calderwood
1
操作系统,文本文件,但这也取决于您的硬件和网络带宽,因此这些结果不应盲目应用于其他项目。我会避免“16k是魔法数字”的想法,因为这是错误的设计。 - Kikiwa

8

至少使用4KB。这是Windows的标准页面大小(即Windows本身管理内存的粒度),这意味着.Net内存分配器不需要将4KB页面拆分为1KB的分配。

当然,使用64KB块会更快,但仅略微提高速度。


谢谢你的指点!我找到了一些信息,分别在这里http://en.wikipedia.org/wiki/Page_(computer_memory)和这里http://jimmoyle.com/wordpress/wp-content/uploads/downloads/2011/05/Windows_7_IOPS_for_VDI_a_Deep_Dive_1_0.pdf。也许我需要动态获取系统内存页面大小...非常感谢! - NoWar

1
我在从IIS下载时,使用64K缓冲区时,与远程机器断开连接出现问题。
我通过将缓冲区提高到2M来解决了这个问题。

1

这将取决于硬件和范围。我从事云部署工作负载,在服务器领域,您可能会找到40G以太网卡,并且可以假设MTU为9000字节。此外,您不希望以太网卡为每个单独的帧中断处理器。因此,忽略Windows / Linux内核中间的参与者,您应该选择一倍或两倍以上的值: 100 * 9000 ~~ 900kB,所以我通常选择512KB作为默认值(只要我知道这个值不会超过正常预期的下载文件大小)。


1

2k、4k或8k都是不错的选择。 页面大小并不重要,速度变化会非常微小且难以预测。

首先,C#内存可以移动,C#使用紧凑的分代垃圾回收器。没有任何关于数据将被分配到哪里的信息。

其次,C#中的数组可以由非连续的内存区域组成! 数组在虚拟内存中是连续存储的,但连续的虚拟内存并不意味着连续的物理内存。

第三,C#中的数组数据结构占用的字节比内容本身多一些(它存储数组大小和其他信息)。如果你分配了页面大小的字节数,使用数组时几乎总是会切换页面!

我认为使用页面大小来优化代码可能是一种非优化。

通常情况下,C#数组表现得非常好,但如果你真的需要精确分配数据,你需要使用固定的数组或Marshal分配,但这会减慢垃圾回收器的速度。

使用Marshal分配和不安全的代码可能会快一点,但真的不值得这样做。

我认为最好只是使用你的数组,不要过多考虑页面大小。使用2k、4k或8k的缓冲区。


为什么假设垃圾回收器足够聪明,可以利用非连续的内存区域,但又不够聪明,在可能的情况下避免这样做呢? - Jon Hanna

0
在某些情况下,您可以找出(或知道,或在调试器中绕过并因此以非更改抵抗方式找出)流使用的缓冲区的大小,无论是写入还是读取。在这种情况下,如果您匹配该大小,则会稍微有优势,否则,一个缓冲区应为另一个缓冲区的整数倍。
否则,默认为4096,除非您有其他原因(例如想要小缓冲区以提供快速的UI反馈),原因已经给出。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接