Parallel.ForEach實測

  • 10732
  • 0

Parallel.ForEach實測

Parallel.ForEach()是.NET 4.0下才有的新功能,用法就像一般的foreach迴圈一樣好用,但重點是,其內的程式碼不再是”依順序”執行了。變成在多執行緒下”平行”執行。

 

用法就不在這邊多做介紹了,接下我用實際的程式碼來進行測試,我寫了二個版本的程式,第一版用傳統的foreach,第二版用Parallel.ForEach(),並比較其速度及CPU使用情況

在介紹程式碼之前,先看一下流程圖,我要做的任務如下:

image

以下程式碼為非平行處理的版本:

               SearchEngine src = new SearchEngine();
                foreach (var job in db.KeywordQueue.Take(1000).ToList())
                {
 
     
                    List<string> listKey = src.parter.KeywordPartition(prdName);
 
                    foreach (var k in listKey)
                    {
                            db2.sp_ProductKeywordReleation_Create(job.TableName, job.ProductId, k);
                    }
 
                    db.KeywordQueue.DeleteOnSubmit(job);
 
                }
                db.SubmitChanges(); 

接下來是平行處理的版本:

                Parallel.ForEach(db.KeywordQueue.Take(1000).ToList(), (job, loopState) =>
                {
                            SearchEngine src = new SearchEngine();
                            List<string> listKey = src.parter.KeywordPartition(prdName);
 
                            SaverDbDataContext db2 = new SaverDbDataContext();
                            foreach (var k in listKey)
                            { 
                                    db2.sp_ProductKeywordReleation_Create(job.TableName, job.ProductId, k);
                            }
 
                            lock (db)                           
                            {
                                db.KeywordQueue.DeleteOnSubmit(job);
                            }

                });
                db.SubmitChanges();

二段程式碼本上是一模一樣的。現在來比較其執行結果


  執行速度 雙核CPU效能

Parllel.ForEach

image image

foreach

image image

 

 

 

 

結論:

  1. 在執行速度上快了近0.3倍
  2. 雙核的CPU,二個CPU的執行效能都有大幅度的提升,充份使用了CPU的處理程式

 

特別注意:

在寫Parllel.ForEach裏面的程式碼時,要把裏面的程式碼當成在”執行緒”裏執行。因此對於靜態,或公用變數的存取要加入lock,例如範例程式碼中,對db進行lock後再執行DeleteOnSubmit()方法。

如果不lock此處的db物件,在執行n筆之後(隨機),會出現NullException的例外錯誤。

 

PARTII 效能再優化

 


先看一下修正後的程式碼,如下…

               SearchEngine src = new SearchEngine();
                Parallel.ForEach(db.KeywordQueue.Take(1000).ToList(), (job, loopState) =>
                {
 
                        try
                        {
 
                            List<string> listKey = new List<string>();
                            //SearchEngine src = new SearchEngine();<--這裏拿掉了
                            src.parter.KeywordPartition(prdName, ref listKey);
 
                            SaverDbDataContext db2 = new SaverDbDataContext();
                            foreach (var k in listKey)
                            {
  
                                db2.sp_ProductKeywordReleation_Create(job.TableName, job.ProductId, k);
                               
                            }
 
                            lock (db)
                            {
                                db.KeywordQueue.DeleteOnSubmit(job);
                            }
 
                     
 
                });

 

 

 

 

 

請注意看綠色mark掉的部份…

 

SearchEngine是一個dll,裏面主要是將傳進去的prdName進行斷字斷詞,原先的寫法是在迴圈裏面創建出來的,現在把它拉到最上面。並且修改了方法,讓listKey這個串列改用外部程式碼傳入。

(原先SearchEngine裏面有一個List Class Member,一但拉出來共用之後,這個負責收集回傳結果list 就會被所有的多緒程式一起塞入keyword造成混亂。因此將list 改成區域變數傳進去,讓每個執行緒裏的list指向不同的記憶體。)

 

 

修改後,效能提升了30毫秒~

image image

 

結論:

若資料量不大,那麼有沒用平行處理其實是沒有太大差別的。但如果資料量一大,例如有100萬筆,每筆53毫秒,只要14.7小時就會處理完,而在原先的foreach情況下則需要29小時才會處理完。