在循环中创建的列表数量非常多的性能问题

时间:2016-04-07 16:37:56

标签: c# performance list garbage-collection

这是我在这里发表的第一篇文章...... 请考虑我是一个业余爱好程序员 的症状:
我的目标是提高代码的性能。 该程序仅使用65%的CPU和500Mb内存。还有另外800Mb的可用物理内存可用于该程序,大约30%的CPU空闲运行。我不知道在哪里可以成为进一步利用资源和提高代码性能的瓶颈 的背景
我写了一个批量测试财务算法的程序。这个算法有很多参数,我试图找到最好的参数组合。为此,我运行它以获得所有可能的参数组合 该算法具有数据序列的输入。它遍历数据系列并产生结果 为此,我将算法的代码插入到Parallel.Foreach循环中,该循环对每个参数组合运行一次。优化的关键代码是循环 由于代码很长,我发布了主干:

...
class candle : IComparable<candle>  //Data element to represent a candle and all chart data
    {
        public double open;
        public double high;
        ...40 more
    }
class param : IComparable<param>
{
    public int par1;
    public int par2;
    public int par3;
    ... a few more    
}
//Running program
{
List<candle> sourceCandleList = new List<candle>();
List<param> paramList = new List<param>(1000000);

// Code to populate sourceCandleList and paramList in here
}
// EDIT: Start parallel processing
Parallel.ForEach(paramList,
    p =>
    {
        List<candle> CandleList = new List<candle>(sourceCandleList.Count);
        foreach (var cndl in sourceCandleList)
           {
            candle c = new candle();
            c.open = cndl.open;
            c.high = cndl.high;
            ...
            //Run calculations and populate fields in CandleList
            }
         //Evaluate results
     }

参数列表有大约140.000个元素。 sourceCandleList有大约2.000个元素。这意味着我不断创建每个包含2000个项目的列表,然后在处理后删除列表。我在运行代码时可以看到GC每秒清理大约200Mb内存。目前Parallel.ForEach的1个循环需要80ms。该程序不会将任何数据写入磁盘,只能将最少的数据写入控制台 也许阻止GC工作的一种方法是将CandleList保存在内存中,然后在下一个循环运行时覆盖它。为此,我需要为每个线程定义一个静态列表,但我不确定它是如何可能的。也许只是创建一堆列表,然后尝试在新线程启动时使用TryEnter捕获一个免费的列表?

<小时/> 的修改
我没有明确说明程序首先生成2个列表:sourceCandleList和paramList。然后,一旦完成,这些永远不会改变。在完全填充这两个列表之后,程序开始测试算法:sourceCandleList是输入,paramList中的一个记录是要应用的参数集。问题是程序每次都定义一个新的CandleList。所以我需要这样的东西:

ConcurrentQueue<List<candle>> ListOfCanldes = new ConcurrentQueue<List<candle>>();

但是我无法弄清楚正确的语法

<小时/> 的的问题:
为什么CPU没有充分利用内存?我是否达到内存的最大读/写速度!? 我怎么能避免GC减慢程序?
我怎么能看到因为GC我实际上已经失去了多少时间? 我怎样才能提高绩效?

2 个答案:

答案 0 :(得分:2)

为什么CPU没有充分利用内存?我是否达到内存的最大读/写速度!?

  • 没有足够的信息可讲。但是使用Parallel.ForEach将限制您使用的线程数。可能没有使用列表,而是ConcurrentQueueConcurrentStack

我会做这样的事情来更快地处理它们:

class Program
    {
        static ConcurrentQueue<candle> sourceCandleList = new ConcurrentQueue<candle>();
        static ConcurrentBag<param> paramList = new ConcurrentBag<param>();
        static void Main(string[] args)
        {
            var threads = new List<Thread>();
            var numberOfThreads = 10;
            for (int i = 0; i < numberOfThreads; i++)
            {
                threads.Add(new Thread(Run));
            }
            threads.ForEach(i => i.Start());
        }
        static void Run()
        {
            candle item;
            while (sourceCandleList.TryDequeue(out item))
            {
                //do you processing here
            }
        }
    }

我怎样才能避免GC减慢程序? :不要担心GC。

我怎么能看到因为GC我实际上失去了多少时间? 您可以使用VS 2015的分析 - 或者下载像Ant Profiler这样的工具。但严重停止担心GC。

我怎样才能提高表现?请参阅上面的代码片段。我不知道你的程序还在做什么。

答案 1 :(得分:0)

最后我用以下代码解决了。原因是我不能让多个线程在同一个队列上工作,因为我需要保留顺序或元素。在这种情况下,不需要额外的订单 结果是内存使用率减少了50%,并且在程序运行时变化很小。现在,该程序以100%运行所有核心。

static void Main(string[] args)
    {
        // Parameters to test
        List<candle> paramList = new List<candle>();

        // Original list
        List<candle> sourceList = new List<candle>();

        // Create a copy for each thread
        int maxThreads = 16;
        ConcurrentBag<int> pool = new ConcurrentBag<int>();
        List<candle>[] processList = new List<candle>[maxThreads];
        for (int i = 0; i <= maxThreads - 1; i++)
        {
            pool.Add(i);
            processList[i] = sourceList.ConvertAll(p => p);
        }
        Parallel.For(0, paramList.Count,
        new ParallelOptions { MaxDegreeOfParallelism = maxThreads },
        p =>
        {
            int slot = 0;
            int item;
            for (int i = 0; i <= 2; i++)
            {
                if (pool.TryTake(out item))
                {
                    slot = item;
                    break;
                }
                else
                {
                    i = 0;
                }
            }
            lock (processList[slot])
            {
                // Do processing here
            }
            pool.Add(slot);
        });
    }