2013-07-24 32 views
1

我正在開發.Net中的遺傳機器學習項目(而不是Matlab - 我的標準)。我不是親.net編碼器,所以請原諒任何noobish實現。性能閱讀大型數據集從多個並行線程

該項目本身是巨大的,所以我不會給你帶來完整的細節,但基本上人口的神經網絡(如決策樹)是在一個問題領域進行評估,在這種情況下,使用感官輸入流。人口中表現最佳的人員可以繁殖和生產後代(繼承父母的傾向),而窮人表演者則被淘汰或繁殖出人羣。進化一直持續到找到可接受的解決方案。一旦找到,最終進化的'網絡'將從實驗室中提取出來,放置在一個輕量級的真實世界的應用程序中。該技術可用於開發非常複雜的控制解決方案,這對於正常編程來說幾乎是不可能或太耗時的,例如自動駕駛汽車,機械穩定性控制,數據中心負載平衡等等。

無論如何,該項目已迄今爲止取得了巨大的成功,並且產生了驚人的結果,但唯一的問題是,一旦我轉向更大的數據集,性能會非常低下。我希望只是我的代碼,所以真的很感謝一些專家的幫助。

在這個項目中,收斂接近理想的解決方案往往需要7天左右的處理!只是對參數進行一些調整並等待結果太痛苦了。

基本上,多個並行線程需要閱讀一個非常大的數據集的順序區段(數據沒有改變一次加載)。該數據集由大約300至1000個雙打組成,超過500k行。由於數據集可能超過2GB的.Net對象限制,因此無法將其存儲在普通的2d數組中 - 最簡單的方法是使用單個數組的通用列表。

並行可伸縮性似乎是一個很大的限制因素,因爲在32位Xeon內核的服務器上運行代碼,通常在早餐時使用大數據集並不會比Corei3桌面帶來更多的性能提升!

隨着內核數量的增加,性能增益會迅速減小。

從剖析代碼(我有限的知識),我得到的印象是有爭論的從多個線程讀取數據集,數額巨大。

我試着使用交錯數組不同的數據集的實現和各種併發集合,但無濟於事試驗。

我已經懷孕了的基準測試代碼快速和髒位類似於核心實現原始的,仍然表現出類似的讀取性能問題和並行可擴展性問題。

任何想法或建議,將不勝感激或確認,這是我要得到最好的。

非常感謝

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Threading.Tasks; 

//Benchmark script to time how long it takes to read dataset per iteration 

namespace Benchmark_Simple 
{ 
class Program 

{ 
    public static TrainingDataSet _DataSet; 
    public static int Features = 100; //Real test will require 300+ 
    public static int Rows = 200000; //Real test will require 500K+ 
    public static int _PopulationSize = 500; //Real test will require 1000+ 
    public static int _Iterations = 10; 
    public static List<NeuralNetwork> _NeuralNetworkPopulation = new List<NeuralNetwork>(); 

    static void Main() 
    { 
     Stopwatch _Stopwatch = new Stopwatch(); 

     //Create Dataset 
     Console.WriteLine("Creating Training DataSet"); 
     _DataSet = new TrainingDataSet(Features, Rows); 
     Console.WriteLine("Finished Creating Training DataSet"); 

     //Create Neural Network Population 
     for (int i = 0; i <= _PopulationSize - 1; i++) 
     { 
      _NeuralNetworkPopulation.Add(new NeuralNetwork()); 
     } 

     //Main Loop 
     for (int i = 0; i <= _Iterations - 1; i++) 
     { 
      _Stopwatch.Restart(); 

      Parallel.ForEach(_NeuralNetworkPopulation, _Network => { EvaluateNetwork(_Network); }); 

      //######## Removed for simplicity ########## 

      //Run Evolutionary Genetic Algorithm on population - I.E. Breed the strong, kill of the weak 

      //########################################## 
      //Repeat until acceptable solution is found 

      Console.WriteLine("Iteration time: {0}", _Stopwatch.ElapsedMilliseconds/1000); 

      _Stopwatch.Stop(); 

     } 

     Console.ReadLine(); 

    } 

    private static void EvaluateNetwork(NeuralNetwork Network) 
    { 
     //Evaluate network on 10% of the Training Data at a random starting point 

     double Score = 0; 

     Random Rand = new Random(); 

     int Count = (Rows/100) * 10; 

     int RandonStart = Rand.Next(0, Rows - Count); 

     //The data must be read sequentially 
     for (int i = RandonStart; i <= RandonStart + Count; i++) 
     { 
      double[] NetworkInputArray = _DataSet.GetDataRow(i); 

      //####### Dummy Evaluation - just give it somthing to do for the sake of it 
      double[] Temp = new double[NetworkInputArray.Length + 1]; 
      for (int j = 0; j <= NetworkInputArray.Length - 1; j++) 
      { 
       Temp[j] = Math.Log(NetworkInputArray[j] * Rand.NextDouble()); 
      } 
      Score += Rand.NextDouble(); 
      //################## 
     } 
     Network.Score = Score; 
    } 

    public class TrainingDataSet 
    { 
     //Simple demo class of fake data for benchmarking 

     private List<double[]> DataList = new List<double[]>(); 

     public TrainingDataSet(int Features, int Rows) 
     { 
      Random Rand = new Random(); 

      for (int i = 1; i <= Rows; i++) 
      { 
       double[] NewRow = new double[Features]; 
       for (int j = 0; j <= Features - 1; j++) 
       { 
        NewRow[j] = Rand.NextDouble(); 
       } 
       DataList.Add(NewRow); 
      } 

     } 

     public double[] GetDataRow(int Index) 
     { 
      return DataList[Index]; 
     } 

    } 

    public class NeuralNetwork 
    { 
     //Simple Class to represent a dummy Neural Network - 
     private double _Score; 
     public NeuralNetwork() 
     { 
     } 

     public double Score 
     { 
      get { return _Score; } 
      set { _Score = value; } 
     } 

    } 
} 
} 
+0

有一兩件事我注意到的是你正在使用Parallel.ForEach這將阻止。你看看ThreadPool.QueueWorkItem和PLINQ嗎?我的經驗 – Learner

+0

Parallel.Foreach在整個列表中有很高的爭用。通過將totalItems除以procs的數量,我可以獲得更好的性能: 'var groupCount = totalItems/numProcessors; (0,groupCount,(groupId)=> {var start = groupId * groupsize; var end = Math.Min(start + groupsize,totalItems); for(int i = start; i Handcraftsman

回答

4

的第一件事是,回答任何的性能問題的唯一方法是通過分析應用程序。我使用的VS 2012內置分析器 - 還有其他https://stackoverflow.com/a/100490/19624

從初始讀取代碼,即靜態分析,唯一跳出我的是在循環內持續重新分配Temp;這是不高效的,如果可能的話需要移出循環。

有了分析器,你可以看到發生了什麼:

Profile Summary

我異形首先使用您發佈的代碼,(最高分,以你張貼的問題的一個完整的編譯例如,如果你hadn」我現在不會回答這個問題)。

這讓我發現,大部分是在循環裏面,我搬到了分配到的Parallel.ForEach循環。

Parallel.ForEach(_NeuralNetworkPopulation, _Network => 
{ 
    double[] Temp = new double[Features + 1]; 
    EvaluateNetwork(_Network, Temp); 
}); 

Original Code

所以,我可以從上面看到的是,有在再分配4.4%的浪費;但可能不令人吃驚的是,這是內部循環,佔87.6%。

該帶我到我的優化第一條規則是先檢查您的算法,而不是優化的代碼。一個好的算法的不好的實現通常比高度優化的差的算法更快。

除去重複分配的溫度略微改變圖像;

After moving allocation to outer loop

另外值得一通過指定並行調諧位;我發現Parallel.ForEach對於我使用它來說足夠好,但是再次手動將工作分區到隊列中可能會獲得更好的結果。

Parallel.ForEach(_NeuralNetworkPopulation, 
       new ParallelOptions { MaxDegreeOfParallelism = 32 }, 
       _Network => 
      { 
       double[] Temp = new double[Features + 1]; 
       EvaluateNetwork(_Network, Temp); 
      }); 

雖然運行我得到什麼,我會在CPU使用率方面預計:雖然我的機器也被運行的正在採取基礎水平(圖中下方剖析這個當峯值另一個漫長的過程程序)。

enter image description here

所以總結

  1. 審查執行最頻繁的部分,並提出了新的算法,如果可能的話。
  2. 目標機器上的配置文件
  3. 只有當您確信上述(1)時,才值得考慮優化算法;考慮以下 一)代碼的優化 B)內存優化/數據partioning以改進保持在高速緩存中 C作爲多)穿線使用
+0

非常感謝您在分析代碼和聲音建議方面的幫助。我將首先嚐試一些不同的核心算法實現,而不是徒勞地拋光我已經獲得並使用分析器的功能更多,一旦我確定了一個堅實的基礎,然後我將着眼於代碼優化。再次感謝您提供的信息豐富而全面的回覆 - 在前往您的路上,您可以品嚐到甜甜圈! – user2615145