5

在我的小型文件传输网站(这个,运行 .NET 4.5.1)中,我按照 Microsoft 知识库文章812406将先前上传的文件从服务器发送到浏览器。

做性能优化我惊讶地发现那行

var buffer = new byte[10000];

需要相当多的时间(我正在使用 Red Gate 的ANTS Performance Profiler)。每个完整下载/客户端仅分配一次缓冲区。

我的问题:

  • 以这种方式和这种大小分配缓冲区是一种好习惯吗?
  • 分配≈10k缓冲区的任何替代方案?

更新1:

感谢您的评论,我看到内存也在循环内分配。

尽管如此,ANTS Profiler 仅在循环之外标记该分配以花费这么多时间,老实说,我(还)不明白。我已经删除了循环内的(无意义的)分配。

更新 2:

实施了建议BufferManager并将缓冲区大小从 10k 减少到 4096(以防万一......),我的网站从几天以来运行非常流畅。

4

4 回答 4

4

是的。实际上,WCF 使用“缓冲区管理器”来防止这个问题

我自己一直在开发网络服务,在分析过程中我发现Byte[]缓冲区的分配造成了瓶颈。不仅在分配期间,而且处理器在 GC 中浪费的时间也非常高。重用这些缓冲区和避免分配的改进产生了非常大的性能改进。

您可以使用BufferManager该类来避免编写自己的缓冲区管理策略。

于 2014-04-05T19:29:48.967 回答
4

在 .NET 中创建对象通常非常快,但由于此数组对象的大小很大,因此清除其所有字节需要很长时间。C# 总是将所有字节0设置为因此在创建对象时将所有字段设置为其默认值。(构造函数和字段初始值设定项当然可以在类和结构中分配不同的值。)

Microsoft 给出的示例中,缓冲区是在循环之前分配的,并且永远不会更改其大小。此外,写入输出流只会写入所需的字节。

// Gets the exact number of bytes read
length = iStream.Read(buffer, 0, 10000);

// Writes only 'length' bytes to the output
Response.OutputStream.Write(buffer, 0, length);

因此,无需在每次循环迭代时通过分配一个新缓冲区来“清除”缓冲区。肮脏的额外字节不会受到伤害。

buffer= new Byte[10000];解决方案:在while循环中删除该行!

于 2014-04-05T19:43:00.567 回答
1

调用这样的构造函数,尤其是使用如此大的缓冲区时,肯定会占用大量 CPU 时间。当然,您的问题的答案将基于意见,但这是我的:

  1. 分配该大小的缓冲区没有任何问题。10K 在现代系统上并不是那么多内存。当然,在任何类型的循环中这样做都会很快耗尽 CPU 时间。

  2. 如果可以的话,尽量避免为每次使用重建缓冲区。使用先前定义的缓冲区可以避免每次需要时都重新分配内存。当然,如果这是线程化的(多个连接),则每个连接/线程都需要自己的缓冲区,但至少每个连接一个分配,而不是链接示例中的每个流数据“块”的新缓冲区。

于 2014-04-05T19:27:21.377 回答
1

如果您有文件传输任务,我建议使用Microsoft.IO.RecyclableMemoryStream

它有 RecyclableMemoryStreamManager 类型,可以创建 RecyclableMemoryStream (内部重用字节)。

优点:

  • 它在内部重用字节数组。例如,如果您需要临时内存流,这些类将为您获取它,而无需额外的清理。
  • 它按块划分内部数组。

缺点:

  • 您应该处理每个流(否则您的应用程序将使用大量内存)
于 2018-10-26T09:38:00.530 回答