Parallel.Foreach只使用20-30%的CPU功率

时间:2015-03-15 20:00:50

标签: c# multithreading task-parallel-library

我有这个Parallel.Foreach,当我运行它时只使用20-30%的CPU。 forEach是:

Parallel.ForEach(activePointList,   new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, activePoints =>
                    {
    var nodesCPD = graph.NodesCPDIds.ToDictionary(d => d, d => new List<CostParentDestination>());
    var heap = new MinHeapGeneric<KeyValuePair<int, int>>();
    foreach (var d in activePoints.Select(a => graph.OldNodes[new KeyValuePair<double, double>(a.XCoord, a.YCoord)]))
    {
        nodesCPD[d].Add(new CostParentDestination(0, -1, d, -1, 0));
        heap.Add(new KeyValuePair<int, int>(d, 0), 0);
    }

    Calculate(graph.Nodes, nodesCPD, heap, graph.CalculationParams.MaxJourneyCost);
});

正如您所看到的,main函数是Calculate,您可以在此处找到它的实现:

  public static void Calculate(
         Dictionary<int, MDDNode> nodes,
         Dictionary<int, List<CostParentDestination>> nodesCPD,
         MinHeapGeneric<KeyValuePair<int, int>> heap,
         int maxJourneyCost)
        {
            while (!heap.Empty())
            {
                var minItem = heap.PopMinItem();

                var currentNodeId = minItem.Key.Key;
                int cpdIndex = minItem.Key.Value;
                var cpd = nodesCPD[currentNodeId][cpdIndex];

                foreach (var edge in nodes[currentNodeId].Edges)
                {
                    var edgeId = edge.Key;
                    var activeCPD = nodesCPD[edgeId].FirstOrDefault(c => c.ParentId == currentNodeId);

                    if (activeCPD == null || activeCPD.Met || activeCPD.CostToNearestDestination <= cpd.CostToNearestDestination + edge.Value.Cost) continue;

                    int zcpdId = -1;
                    for (int idx = 0; idx < nodesCPD[edgeId].Count; idx++)
                        if (nodesCPD[edgeId][idx].ParentId == currentNodeId)
                            zcpdId = idx;

                    activeCPD.CostToNearestDestination = cpd.CostToNearestDestination + edge.Value.Cost;
                    activeCPD.DestinationId = cpd.DestinationId;
                    heap.Add(new KeyValuePair<int, int>(edgeId, zcpdId), cpd.CostToNearestDestination + edge.Value.Cost);
                }
                cpd.Met = true;
            }           
        }

它读取最小项目然后迭代该点的边缘,过程继续,直到它设置所有点到目的地的距离。

这是堆的Add和PopMinItem方法的实现,我认为这是Calculate()方法的代价很高的部分:

 public class MinHeapGeneric<T>
    {
        public Dictionary<int, T> KeyOfId = new Dictionary<int, T>();
        public Dictionary<T, int> IdOfKey = new Dictionary<T, int>();
        private readonly MinHeapLite _heap = new MinHeapLite();
        private int _id;

        public void Add(T key, double value)
        {
            int id;
            if (IdOfKey.ContainsKey(key))
                id = IdOfKey[key];
            else
            {
                id = _id++;
                KeyOfId.Add(id, key);
                IdOfKey.Add(key, id);
            }
            _heap.Add(new HeapItem(id, value));
        }

        public KeyValuePair<T, double> PopMinItem()
        {
            var item = _heap.PopMinItem();
            var key = KeyOfId[item.Id];
            IdOfKey.Remove(key);
            KeyOfId.Remove(item.Id);
            return new KeyValuePair<T, double>(key, item.Value);
        }
}

public class MinHeapLite
    {
        private readonly HeapItem[] _items;
        private readonly int _maxHeapSize;
        private int _lastItemIndex; //Items will start from 1. (root == 1)
        private readonly Dictionary<int, int> _idToIndexMap = new Dictionary<int, int>();

        public HeapItem PopMinItem()
        {
            if (Empty())
            {
                throw (new Exception("Popping from an empty Heap."));
            }
            HeapItem minItem = new HeapItem(_items[1]);

            HeapItem data = _items[_lastItemIndex];
            int parentIndex = 1;
            int childIndex = parentIndex * 2;
            while (childIndex < _lastItemIndex)
            {
                if (childIndex + 1 < _lastItemIndex && _items[childIndex].Value > _items[childIndex + 1].Value)
                    childIndex++;
                if (_items[childIndex].Value < data.Value)
                {
                    _items[parentIndex] = _items[childIndex];
                    _idToIndexMap[_items[parentIndex].Id] = parentIndex;
                    parentIndex = childIndex;
                    childIndex *= 2;
                }
                else
                    break;
            }
            _items[parentIndex] = data;
            _idToIndexMap[_items[parentIndex].Id] = parentIndex;
            _lastItemIndex--;
            _idToIndexMap.Remove(minItem.Id);
            return minItem;
        }
}

我的问题是代码的哪一部分阻止主要的Foreach使用整个CPU的电源?

0 个答案:

没有答案