【问题标题】:C# Performances, Array versus Span from the Memory BufferC# 性能,内存缓冲区中的数组与跨度
【发布时间】:2020-05-23 10:08:03
【问题描述】:

我正在测试使用标准 C# 数组与使用内存缓冲区的性能。标准阵列获胜。我不明白标准数组如何比使用内存缓冲区中的跨度更快。文档说内存缓冲区应该用于高性能场景。我正在使用 Release 优化配置。

class Program
{
    private static void MemoryPoolTest(IMemoryOwner<long> owner)
    {
        for (var i = 0; i < 100; i++)
        {
            var innerCounter = 0;
            for (var j = 0; j < 1000; j++)
            {
                if (j % 3 == 0)
                {
                    owner.Memory.Span[innerCounter++] = DateTime.UtcNow.Ticks;
                }
            }
            var result = owner.Memory.Slice(0, innerCounter - 1);
        }
    }

    private static void ArrayTest()
    {
        for (var i = 0; i < 100; i++)
        {
            var innerCounter = 0;
            Span<long> array = new long[1000];
            for (var j = 0; j < 1000; j++)
            {
                if (j % 3 == 0)
                {
                    array[innerCounter++] = DateTime.UtcNow.Ticks;
                }
            }
            var result = array.Slice(0, innerCounter - 1);
        }
    }

    static void Main(string[] args)
    {
        Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
        Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
        var m = new List<long>();
        var a = new List<long>();
        var sw = new Stopwatch();
        using var owner = MemoryPool<long>.Shared.Rent(1000);
        for (var i = 0; i < 1000; i++)
        {
            sw.Restart();
            MemoryPoolTest(owner);
            sw.Stop();
            m.Add(sw.ElapsedTicks);
            sw.Restart();
            ArrayTest();
            sw.Stop();
            a.Add(sw.ElapsedTicks);
        }
        Console.WriteLine(m.Skip(10).Average());
        Console.WriteLine(a.Skip(10).Average());
    }
}

【问题讨论】:

  • 我每天看到大约 3 个“为什么这么慢” 问题。他们通常会分解为不正确的基准测试方法,或者试图将苹果与橘子竞争。对于初学者来说,它不是数组与内存。并且您正在调用 memory.Span 每次迭代,这是相当密集的。但是,您仍在与苹果和橘子赛跑。
  • 是的,除了在这种情况下,它是可以轻松优化的糟糕代码;)

标签: c# .net performance memory-management .net-core


【解决方案1】:

A Span 被分配(或移动到)堆(堆栈),A Memory&lt;T&gt; 更有可能来自托管内存。

这取决于具体的 CPU,但堆最常用于 CPU 内部的小内存,有时称为缓存,如今每个内核高达 1MB 或 2MB,访问速度非常快,而托管内存是分配在常规 RAM 中。也可能被缓存,但可能性不大。

【讨论】:

  • 你是说堆和栈是一回事吗?您是说 Memory 使用 CPU 缓存而标准数组使用“托管内存”吗? CPU缓存=堆?托管内存堆?堆?相同?老实说,我不明白你的评论。
  • 我谈到了可能性,堆栈是内存中访问量很大的部分,因此 CPU 的缓存算法会非常喜欢将其移动到它的缓存中。这超出了我们的控制范围,但这可能是为什么访问堆栈比访问内存中的随机不可预测地址更快的原因。实际上,您提出了很多问题,但您没有解释或纠正任何问题,以防我误用了一个词。这也不是很有帮助。 '与 Span 不同,Memory 可以存储在托管堆上。'从这里:docs.microsoft.com/en-us/dotnet/standard/memory-and-spans/…
【解决方案2】:

问题在于每次迭代都使用 owner.Memory.Span。在迭代之前将 Span 分配给变量以减少无用的操作并且 BufferMemory 获胜

class Program
{
    private static void MemoryPoolTest(Span<long> span)
    {
        for (var i = 0; i < 100; i++)
        {
            var innerCounter = 0;
            for (var j = 0; j < 1000; j++)
            {
                if (j % 3 == 0)
                {
                    span[innerCounter++] = DateTime.UtcNow.Ticks;
                }
            }
            var result = span.Slice(0, innerCounter - 1);
        }
    }

    private static void ArrayTest()
    {
        for (var i = 0; i < 100; i++)
        {
            var innerCounter = 0;
            Span<long> array = new long[1000];
            for (var j = 0; j < 1000; j++)
            {
                if (j % 3 == 0)
                {
                    array[innerCounter++] = DateTime.UtcNow.Ticks;
                }
            }
            var result = array.Slice(0, innerCounter - 1);
        }
    }

    static void Main(string[] args)
    {
        Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
        Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
        var m = new List<long>();
        var a = new List<long>();
        var sw = new Stopwatch();
        using var owner = MemoryPool<long>.Shared.Rent(1000);
        var span = owner.Memory.Span;
        for (var i = 0; i < 1000; i++)
        {
            sw.Restart();
            MemoryPoolTest(span);
            sw.Stop();
            m.Add(sw.ElapsedTicks);
            sw.Restart();
            ArrayTest();
            sw.Stop();
            a.Add(sw.ElapsedTicks);
        }
        Console.WriteLine(m.Skip(10).Average());
        Console.WriteLine(a.Skip(10).Average());
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-14
    • 1970-01-01
    • 2017-04-19
    • 2013-03-21
    • 2021-04-26
    • 1970-01-01
    • 2011-07-16
    相关资源
    最近更新 更多