.NET Core 的 3 个性能提示

作者:微信公众号:【架构师老卢】
8-29 13:57
8

概述:下面是 .net Core 的一些性能改进。这在 .net core 8 上进行了测试。在较新版本上,结果可能会有所不同。所有基准测试也在同一台计算机上运行。与往常一样,在尝试优化性能时,请尝试考虑可读性。优化程度越高的代码有时可能更难阅读。可读性性能1. SeriLog 日志消息在这里,我们测试了消息模板与内联创建字符串的用法。基准测试代码: [Benchmark] public void LogMessage() { for (var i = 0; i IterationCount; i++) { Log.Logger.Information($test

下面是 .net Core 的一些性能改进。这在 .net core 8 上进行了测试。在较新版本上,结果可能会有所不同。所有基准测试也在同一台计算机上运行。

与往常一样,在尝试优化性能时,请尝试考虑可读性。优化程度越高的代码有时可能更难阅读。

可读性>性能

1. SeriLog 日志消息

在这里,我们测试了消息模板与内联创建字符串的用法。

基准测试代码:

 [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 模板中连接一个字符串时,我们会分配内存。这也使此方法变慢。最佳实践是使用消息模板并将其他值设置为附加参数。

2. 读取大型 JSON

在这里,我们尝试读取一个大型 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,我们可以逐个元素读取。

3. 数组与 ArrayPool

[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 的一些注释:

  • 最好确保始终返回租用的数组
  • 租用的数组并不总是请求的大小。如果你请求一个包含 10 个元素的数组,则可能是你得到一个包含 12 个元素的数组。请求的数组大小是最小数组大小
  • 该数组仍然可以包含来自先前 rent 的数据。您可以在返回数据时强制它清理以前的数据。

基准测试结果:

数组池分配的内存比默认数组方法少得多。使用数组池,您可以多次使用同一个数组,并且不需要每次都分配内存。

阅读排行