下面是 .net Core 的一些性能改进。这在 .net core 8 上进行了测试。在较新版本上,结果可能会有所不同。所有基准测试也在同一台计算机上运行。
与往常一样,在尝试优化性能时,请尝试考虑可读性。优化程度越高的代码有时可能更难阅读。
可读性>性能
在这里,我们测试了消息模板与内联创建字符串的用法。
基准测试代码:
[Benchmark]
public void LogMessage()
{
for (var i = 0; i < IterationCount; i++)
{
Log.Logger.Information($"test {i}");
}
}
[Benchmark]
public void LogMessageTemplate()
{
for (var i = 0; i < IterationCount; i++)
{
Log.Logger.Information("test {0}", i);
}
}
基准测试结果:
在这里,当我们在 message 模板中连接一个字符串时,我们会分配内存。这也使此方法变慢。最佳实践是使用消息模板并将其他值设置为附加参数。
在这里,我们尝试读取一个大型 JSON 数组。我们测试了 JsonDocument 方法与 JsonSerializer.Deserialize 的方法。
基准测试代码:
[WarmupCount(0)]
[IterationCount(3)]
[ProcessCount(1)]
[MemoryDiagnoser]
public class JsonBenchmarks
{
private const string FileName = "large.json";
public JsonBenchmarks()
{
CreateJson();
}
[Benchmark(Baseline = true)]
public async Task JsonBenchmark_JsonDocument()
{
using var file = File.OpenRead(FileName);
using var document = await JsonDocument.ParseAsync(file);
var enumerator = document.RootElement.EnumerateArray();
foreach(var node in enumerator)
{
var number = node.GetProperty("Number").GetInt32();
}
}
[Benchmark]
public void JsonBenchmark_JsonSerializer()
{
using var file = File.OpenRead(FileName);
var list = JsonSerializer.Deserialize<List<JsonNode>>(file);
foreach(var item in list)
{
var number = item.Number;
}
}
private void CreateJson()
{
if(File.Exists(FileName))
{
return;
}
const int NodeCount = 100_000;
var array = Enumerable
.Repeat(0, NodeCount).Select(x => new JsonNode { Name = $"Name{x}", Value = $"Value{x}", Number = x })
.ToList();
var json = JsonSerializer.Serialize(array);
File.WriteAllText(FileName, json);
}
private class JsonNode
{
public string Name { get; set; }
public string Value { get; set; }
public int Number { get; set; }
}
}
基准测试结果:
对于小型 JSON 文件,JSONSerializer.Deserialize 很好用。尤其是当您使用较大的文件时,内存占用会很高。
JsonDocument 是一种只读方法,用于解析具有一些内置优化的 JSON。另一个好处是使用 JsonDocument,我们可以逐个元素读取。
[MemoryDiagnoser]
public class ArrayPoolBenchmark
{
[Params(100, 1000, 10_000, 100_000, 1_000_000)]
public int ArraySize { get; set; }
[Benchmark]
public void Array()
{
var array = new int[ArraySize];
for (var i = 0; i < ArraySize; i++)
{
array[i] = i;
}
}
[Benchmark]
public void ArrayInStack()
{
unsafe
{
Span<int> array = ArraySize < 1024 ? stackalloc int[ArraySize] : new int[ArraySize];
for (var i = 0; i < ArraySize; i++)
{
array[i] = i;
}
}
}
[Benchmark]
public void ArrayPool()
{
var array = ArrayPool<int>.Shared.Rent(ArraySize);
for (var i = 0; i < ArraySize; i++)
{
array[i] = i;
}
ArrayPool<int>.Shared.Return(array);
}
}
关于 arrayPool 的一些注释:
基准测试结果:
数组池分配的内存比默认数组方法少得多。使用数组池,您可以多次使用同一个数组,并且不需要每次都分配内存。