简体   繁体   English

C# foreach 性能 vs 内存碎片

[英]C# foreach performance vs memory fragmentation

Tracking down a performance problem (micro I know) I end with this test program.跟踪性能问题(我知道微)我以这个测试程序结束。 Compiled with the framework 4.5 and Release mode it tooks on my machine around 10ms.使用框架 4.5 和发布模式编译它在我的机器上花费了大约 10 毫秒。

What bothers me if that if I remove this line如果我删除这条线,我有什么困扰

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

times get closer to 2 ms.时间接近 2 毫秒。 It seems that there is some memory fragmentation problem but I failed to explain the why.似乎存在一些内存碎片问题,但我未能解释原因。 I have tested the program with Net Core 2.0 with same results.我已经用 Net Core 2.0 测试了该程序,结果相同。 Can anyone explain this behaviour?谁能解释这种行为?

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();
        }
    }
}

UPDATE:更新:

After some research I came to the conclusion that it's just the processor cache access time.经过一些研究,我得出结论,这只是处理器缓存访问时间。 Using the VS profiler, the cache misses seem to be a lot higher使用 VS 分析器,缓存未命中似乎要高得多

  • Without array没有数组

没有数组

  • With array带阵列

带阵列

There are several implications involved.有几个影响。

When you have your line public int[] value1 = new int[80];当你有你的线路public int[] value1 = new int[80]; , you have one extra allocation of memory: a new array is created on a heap which will accommodate 80 integers (320 bytes) + overhead of the class. ,您有一个额外的内存分配:在堆上创建一个新数组,该数组将容纳 80 个整数(320 字节)+ 类的开销。 You do 500 000 of these allocations.您进行了 500 000 次这些分配。

These allocations total up for more than 160 MBs of RAM, which may cause the GC to kick in and see if there is memory to be released.这些分配总计超过 160 MB 的 RAM,这可能会导致 GC 启动并查看是否有内存要释放。

Further, when you allocate so much memory, it is likely that some of the objects from the list are not retained in the CPU cache.此外,当您分配如此多的内存时,列表中的某些对象可能不会保留在 CPU 缓存中。 When you later enumerate your collection, the CPU may need to read the data from RAM, not from cache, which will induce a serious performance penalty.当您稍后枚举集合时,CPU 可能需要从 RAM 中读取数据,而不是从缓存中读取数据,这会导致严重的性能损失。

I'm not able to reproduce a big difference between the two and I wouldn't expect it either.我无法重现两者之间的巨大差异,我也不指望它。 Below are the results I get on .NET Core 2.2.下面是我在 .NET Core 2.2 上得到的结果。

Instances of MyObject will be allocated on the heap. MyObject实例将在堆上分配。 In one case, you have an int and a reference to the int array.在一种情况下,您有一个 int 和一个对 int 数组的引用。 In the other you have just the int.在另一个你只有int。 In both cases, you need to do the additional work of following the reference from the list.在这两种情况下,您都需要执行遵循列表中的参考的额外工作。 That is the same in both cases and the compiled code shows this.这在两种情况下都是相同的,编译后的代码显示了这一点。

Branch prediction will affect how fast this runs, but since you're branching on the same condition every time I wouldn't expect this to change from run to run (unless you change the data).分支预测将影响此运行的速度,但是由于您每次都在相同条件下进行分支,因此我不希望它在运行之间发生变化(除非您更改数据)。

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 |

For reference:以供参考:

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