分配字节数组是否对性能至关重要
本文关键字:性能 至关重要 是否 数组 字节 字节数 分配 | 更新日期: 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 时间。当然,您的问题的答案将基于意见,但这是我的:
-
分配这种大小的缓冲区没有错。 在现代系统上,10K 并不是那么多内存。当然,在任何类型的循环中这样做都会很快占用 CPU 时间。
-
如果可以,请尽量避免为每次使用重建缓冲区。使用以前定义的缓冲区可避免每次需要内存时都必须重新分配内存。当然,如果这是线程化的(多个连接),则每个连接/线程都需要自己的缓冲区,但至少这是每个连接的一个分配,而不是像链接示例中那样为每个"块"流数据提供新的缓冲区。
文件传输任务,我建议使用Microsoft.IO.RecyclableMemoryStream。
它具有RecyclableMemoryStreamManager类型,可以创建RecyclableMemoryStream(在内部重用字节)。
优点:
- 它在内部重用字节数组。 例如,如果您需要临时内存流,这些类将为您获取它,而无需额外的清理。
- 它按块划分内部数组。
缺点:
- 您应该释放每个流(否则您的应用程序将使用大量内存)