分配字节数组是否对性能至关重要

本文关键字:性能 至关重要 是否 数组 字节 字节数 分配 | 更新日期: 2023-09-27 17:56:44

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

做性能优化我惊讶地发现这条线

var buffer = new byte[10000];

需要相当大比例的时间(我使用的是Red Gate的ANTS性能分析器)。每个完整下载/客户端仅分配一次缓冲区。

我的问题:

  • 以这种方式和这种大小分配缓冲区是一种好的做法吗?
  • 分配 ≈10k 缓冲区有什么选择吗?

更新 1:

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

尽管如此,ANTS Profiler 只标记循环的分配需要那么多时间,老实说,我还不明白。我已经删除了循环内的(无意义的)分配。

更新 2:

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

分配字节数组是否对性能至关重要

是的。实际上,WCF 使用"缓冲区管理器"来防止此问题。

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

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

在 .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循环中!

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

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

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

如果您有

文件传输任务,我建议使用Microsoft.IO.RecyclableMemoryStream。

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

优点:

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

缺点:

  • 您应该释放每个流(否则您的应用程序将使用大量内存)