簡體   English   中英

C# foreach 性能 vs 內存碎片

[英]C# foreach performance vs memory fragmentation

跟蹤性能問題(我知道微)我以這個測試程序結束。 使用框架 4.5 和發布模式編譯它在我的機器上花費了大約 10 毫秒。

如果我刪除這條線,我有什么困擾

public int[] value1 = new int[80];

時間接近 2 毫秒。 似乎存在一些內存碎片問題,但我未能解釋原因。 我已經用 Net Core 2.0 測試了該程序,結果相同。 誰能解釋這種行為?

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApp4
{

    public class MyObject
    {
        public int value = 1;
        public int[] value1 = new int[80];
    }


    class Program
    {
        static void Main(string[] args)
        {

            var list = new List<MyObject>();
            for (int i = 0; i < 500000; i++)
            {
                list.Add(new MyObject());
            }

            long total = 0;
            for (int i = 0; i < 200; i++)
            {
                int counter = 0;
                Stopwatch timer = Stopwatch.StartNew();

                foreach (var obj in list)
                {
                    if (obj.value == 1)
                        counter++;
                }

                timer.Stop();
                total += timer.ElapsedMilliseconds;
            }

            Console.WriteLine(total / 200);

            Console.ReadKey();
        }
    }
}

更新:

經過一些研究,我得出結論,這只是處理器緩存訪問時間。 使用 VS 分析器,緩存未命中似乎要高得多

  • 沒有數組

沒有數組

  • 帶陣列

帶陣列

有幾個影響。

當你有你的線路public int[] value1 = new int[80]; ,您有一個額外的內存分配:在堆上創建一個新數組,該數組將容納 80 個整數(320 字節)+ 類的開銷。 您進行了 500 000 次這些分配。

這些分配總計超過 160 MB 的 RAM,這可能會導致 GC 啟動並查看是否有內存要釋放。

此外,當您分配如此多的內存時,列表中的某些對象可能不會保留在 CPU 緩存中。 當您稍后枚舉集合時,CPU 可能需要從 RAM 中讀取數據,而不是從緩存中讀取數據,這會導致嚴重的性能損失。

我無法重現兩者之間的巨大差異,我也不指望它。 下面是我在 .NET Core 2.2 上得到的結果。

MyObject實例將在堆上分配。 在一種情況下,您有一個 int 和一個對 int 數組的引用。 在另一個你只有int。 在這兩種情況下,您都需要執行遵循列表中的參考的額外工作。 這在兩種情況下都是相同的,編譯后的代碼顯示了這一點。

分支預測將影響此運行的速度,但是由於您每次都在相同條件下進行分支,因此我不希望它在運行之間發生變化(除非您更改數據)。

BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.376 (1803/April2018Update/Redstone4)
Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=2.2.200-preview-009648
  [Host]     : .NET Core 2.2.0 (CoreCLR 4.6.27110.04, CoreFX 4.6.27110.04), 64bit RyuJIT
  DefaultJob : .NET Core 2.2.0 (CoreCLR 4.6.27110.04, CoreFX 4.6.27110.04), 64bit RyuJIT


       Method |   size |     Mean |     Error |    StdDev | Ratio |
------------- |------- |---------:|----------:|----------:|------:|
    WithArray | 500000 | 8.167 ms | 0.0495 ms | 0.0463 ms |  1.00 |
 WithoutArray | 500000 | 8.167 ms | 0.0454 ms | 0.0424 ms |  1.00 |

以供參考:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Collections.Generic;

namespace CoreSandbox
{
    [DisassemblyDiagnoser(printAsm: true, printSource: false, printPrologAndEpilog: true, printIL: false, recursiveDepth: 1)]
    //[MemoryDiagnoser]
    public class Test
    {
        private List<MyObject> dataWithArray;
        private List<MyObjectLight> dataWithoutArray;

        [Params(500_000)]
        public int size;

        public class MyObject
        {
            public int value = 1;
            public int[] value1 = new int[80];
        }

        public class MyObjectLight
        {
            public int value = 1;
        }

        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<Test>();
        }

        [GlobalSetup]
        public void Setup()
        {
            dataWithArray = new List<MyObject>(size);
            dataWithoutArray = new List<MyObjectLight>(size);

            for (var i = 0; i < size; i++)
            {
                dataWithArray.Add(new MyObject());
                dataWithoutArray.Add(new MyObjectLight());
            }
        }

        [Benchmark(Baseline = true)]
        public int WithArray()
        {
            var counter = 0;

            foreach(var obj in dataWithArray)
            {
                if (obj.value == 1)
                    counter++;
            }

            return counter;
        }

        [Benchmark]
        public int WithoutArray()
        {
            var counter = 0;

            foreach (var obj in dataWithoutArray)
            {
                if (obj.value == 1)
                    counter++;
            }

            return counter;
        }

    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM