c#性能通过循环仅增加0,001%而迅速变化

时间:2017-09-28 13:19:02

标签: c# performance garbage-collection

我正在开发一个UI项目,该项目必须处理大型数据集(每隔35个新值),然后才会显示在图表中。用户应能够将视图从10分钟更改为月视图。为了归档这个,我自己编写了一个辅助函数,它将大量数据截断为一个600字节的数组,然后应该在LiveView图表上显示。

我发现在开始时软件运行得非常好而且速度很快,但是软件运行的时间越长(例如一个月),内存使用量增加(大约600 mb)功能得到&#39 ;慢很多(最多8倍)。

所以我做了一些测试来找到它的来源。非常惊讶我发现有一个像魔术数字这样的函数慢2倍,只需将 71494循环改为71495 19ms到39ms 运行时

我真的很困惑。即使你注释掉第二个for循环(数组被截断),它也会慢得多。 也许这与垃圾收集器有关?或者C#会自动压缩内存吗?

使用Visual Studio 2017和最新更新。

守则

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

namespace TempoaryTest
{
    class ProductNameStream
    {
        public struct FileValue
        {

            public DateTime Time;
            public ushort[] Value;
            public ushort[] Avg1;
            public ushort[] Avg2;
            public ushort[] DAvg;
            public ushort AlarmDelta;
            public ushort AlarmAverage;
            public ushort AlarmSum;
        }
    }


    public static class Program

    {
        private const int MAX_MEASURE_MODEL = 600;

        private const int TEST = 71494;
        //private const int TEST = 71495;//this one doubles the consuming time!
        public static void Main(string[] bleg)
        {
            List<ProductNameStream.FileValue> fileValues = new List<ProductNameStream.FileValue>();
            ProductNameStream.FileValue fil = new ProductNameStream.FileValue();

            DateTime testTime = DateTime.Now;

            Console.WriteLine("TEST: {0} {1:X}", TEST, TEST);
            //Creating example List
            for (int n = 0; n < TEST; n++)
            {
                fil = new ProductNameStream.FileValue
                {
                    Time = testTime = testTime.AddSeconds(1),
                    Value = new ushort[8],
                    Avg1 = new ushort[8],
                    Avg2 = new ushort[8],
                    DAvg = new ushort[8]
                };
                for (int i = 0; i < 8; i++)
                {
                    fil.Value[i] = (ushort)(n + i);
                    fil.Avg1[i] = (ushort)(TEST - n - i);
                    fil.Avg2[i] = (ushort)(n / (i + 1));
                    fil.DAvg[i] = (ushort)(n * (i + 1));
                }
                fil.AlarmDelta = (ushort)DateTime.Now.Ticks;
                fil.AlarmAverage = (ushort)(fil.AlarmDelta / 2);
                fil.AlarmSum = (ushort)(n);
                fileValues.Add(fil);
            }


            var sw = Stopwatch.StartNew();

            /* May look like the same as MAX_MEASURE_MODEL but since we use int
             *  as counter we must be aware of the int round down.*/
            int cnt = (fileValues.Count / (fileValues.Count / MAX_MEASURE_MODEL)) + 1;

            ProductNameStream.FileValue[] newFileValues = new ProductNameStream.FileValue[cnt];
            ProductNameStream.FileValue[] fileValuesArray = fileValues.ToArray();


            //Truncate the big list to a 600 Array
            for (int n = 0; n < fileValues.Count; n++)
            {
                if ((n % (fileValues.Count / MAX_MEASURE_MODEL)) == 0)
                {
                    cnt = n / (fileValues.Count / MAX_MEASURE_MODEL);
                    newFileValues[cnt] = fileValuesArray[n];
                    newFileValues[cnt].Value = new ushort[8];
                    newFileValues[cnt].Avg1 = new ushort[8];
                    newFileValues[cnt].Avg2 = new ushort[8];
                    newFileValues[cnt].DAvg = new ushort[8];

                }

                else
                {
                    for (int i = 0; i < 8; i++)
                    {
                        if (newFileValues[cnt].Value[i] < fileValuesArray[n].Value[i])
                            newFileValues[cnt].Value[i] = fileValuesArray[n].Value[i];
                        if (newFileValues[cnt].Avg1[i] < fileValuesArray[n].Avg1[i])
                            newFileValues[cnt].Avg1[i] = fileValuesArray[n].Avg1[i];
                        if (newFileValues[cnt].Avg2[i] < fileValuesArray[n].Avg2[i])
                            newFileValues[cnt].Avg2[i] = fileValuesArray[n].Avg2[i];
                        if (newFileValues[cnt].DAvg[i] < fileValuesArray[n].DAvg[i])
                            newFileValues[cnt].DAvg[i] = fileValuesArray[n].DAvg[i];
                    }
                    if (newFileValues[cnt].AlarmSum < fileValuesArray[n].AlarmSum)
                        newFileValues[cnt].AlarmSum = fileValuesArray[n].AlarmSum;
                    if (newFileValues[cnt].AlarmDelta < fileValuesArray[n].AlarmDelta)
                        newFileValues[cnt].AlarmDelta = fileValuesArray[n].AlarmDelta;
                    if (newFileValues[cnt].AlarmAverage < fileValuesArray[n].AlarmAverage)
                        newFileValues[cnt].AlarmAverage = fileValuesArray[n].AlarmAverage;
                }
            }
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
    }
}

2 个答案:

答案 0 :(得分:2)

这很可能是由垃圾收集器引起的,正如您所建议的那样。

我可以提供两个证据表明情况确实如此:

  1. 如果你在启动秒表之前放置GC.Collect(),时间差就会消失。
  2. 如果您改为将列表的初始化更改为new List<ProductNameStream.FileValue>(TEST);,则时间上的差异也会消失。
  3. (将列表的容量初始化为其构造函数中的最终大小可防止在添加项目时对其内部数组进行多次重新分配,这将减少垃圾收集器的压力。)

    因此,我基于这一证据断言确实是垃圾收集器正在影响你的时间。

    顺便提一下,对于我来说,阈值也略有不同,对于至少另一个人来说也是如此(如果时间差异是由垃圾收集器引起的,那就不足为奇了。)

答案 1 :(得分:1)

您的数据结构效率低下,迫使您在计算过程中进行大量分配。看看这个fixed size array inside a struct 。还要预先分配列表。不要依赖列表来不断调整其大小,这也会产生垃圾。