为什么重新分配缓冲区比重置缓冲区更快?
Why is it faster to reallocate a buffer than to reset it?
在重构一些旧代码时,我决定做一些毫无意义的过早优化并重新利用缓冲区而不是每次都重新分配它。
令我惊讶的是,我发现它慢得令人难以置信。
复制:
private static void Main(string[] args)
{
const int N = (int)1e4;
const int size = 100 * 1024;
var sw = Stopwatch.StartNew();
var buffer = new byte[size];
for (int n = 0; n < N; n++)
{
buffer = new byte[size];
}
var reallocMs = sw.ElapsedMilliseconds;
sw = Stopwatch.StartNew();
buffer = new byte[size];
for (int n = 0; n < N; n++)
{
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = 0;
}
}
var rewriteMs = sw.ElapsedMilliseconds;
Console.WriteLine($"Reallocating: {reallocMs} ms");
Console.WriteLine($"Rewriting: {rewriteMs} ms");
Console.WriteLine($"Rewrite / reallocate = {(double)rewriteMs / (double)reallocMs}");
}
内置 Release
时,重写每个字节比重新分配新缓冲区慢约 8 倍。在 Debug
中,速度下降了将近 50 倍!
这怎么可能?我预计有一个固定的缓冲区会提高性能,但显然不是这样。
我在这里错过了什么?
P.S: 顺便说一下,Array.Clear()
始终比 for 循环 更慢 ,虽然它的性能独立于构建配置。
每次写入数组时,它都可能抛出异常。因此,在循环实现中的每一次写入中,都会对索引进行边界检查。这可以在首次初始化已知范围的内存时进行优化。
在重构一些旧代码时,我决定做一些毫无意义的过早优化并重新利用缓冲区而不是每次都重新分配它。 令我惊讶的是,我发现它慢得令人难以置信。
复制:
private static void Main(string[] args)
{
const int N = (int)1e4;
const int size = 100 * 1024;
var sw = Stopwatch.StartNew();
var buffer = new byte[size];
for (int n = 0; n < N; n++)
{
buffer = new byte[size];
}
var reallocMs = sw.ElapsedMilliseconds;
sw = Stopwatch.StartNew();
buffer = new byte[size];
for (int n = 0; n < N; n++)
{
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = 0;
}
}
var rewriteMs = sw.ElapsedMilliseconds;
Console.WriteLine($"Reallocating: {reallocMs} ms");
Console.WriteLine($"Rewriting: {rewriteMs} ms");
Console.WriteLine($"Rewrite / reallocate = {(double)rewriteMs / (double)reallocMs}");
}
内置 Release
时,重写每个字节比重新分配新缓冲区慢约 8 倍。在 Debug
中,速度下降了将近 50 倍!
这怎么可能?我预计有一个固定的缓冲区会提高性能,但显然不是这样。
我在这里错过了什么?
P.S: 顺便说一下,Array.Clear()
始终比 for 循环 更慢 ,虽然它的性能独立于构建配置。
每次写入数组时,它都可能抛出异常。因此,在循环实现中的每一次写入中,都会对索引进行边界检查。这可以在首次初始化已知范围的内存时进行优化。