2014-01-06 53 views
2

當我試圖來分析一個緩慢的優化算法,我發現以下方法所需的時間比預期的要長得非常慢:迭代通過ILArray <double>是

protected virtual bool Dominates(ILInArray<double> p1, ILInArray<double> p2, int nObjectives) 
{ 
    using (ILScope.Enter(p1, p2)) 
    { 
     ILArray<double> p1In = p1; 
     ILArray<double> p2In = p2; 
     bool strong = false; 
     for (int i = 0; i < nObjectives; i++) 
     { 
      using (ILScope.Enter()) 
      { 
       if (p1In[i] > p2In[i]) 
        strong = true; 
       else if (p1In[i] < p2In[i]) 
       { 
        return false; 
       } 
      } 
     } 
     return strong; 
    } 
} 

然後我用下面的執行和更換它速度增加了巨大的倍數。

protected virtual bool DominatesSys(double[] p1, double[] p2, int nObjectives) 
{ 
    bool strong = false; 
    for (int i = 0; i < nObjectives; i++) 
    { 
     if (p1[i] > p2[i]) 
      strong = true; 
     else if (p1[i] < p2[i]) 
     { 
      return false; 
     } 
    } 
    return strong; 
} 

我不明白它,並試圖編寫一個測試來比較差異。以下是使用的測試代碼:

[Test] 
public void TestILNumericsSpeed() 
{ 
    ILArray<double> p1Il = ILMath.rand(100); 
    ILArray<double> p2Il = p1Il - 0.01; 
    double[] p1 = p1Il.GetArrayForRead(); 
    double[] p2 = p2Il.GetArrayForRead(); 
    int length = p1.Length; 
    Func<bool> func1 =() => 
     { 
      Dominates(p1Il, p2Il, length); 
      return true; 
     }; 
    Func<bool> func2 =() => 
     { 
      DominatesSys(p1, p2, length); 
      return true; 
     }; 
    var stats1 = CollectStats(func1, 20, 1000); 
    var stats2 = CollectStats(func2, 20, 1000); 
    log.InfoFormat("Mean time taken by ILNumerics = {0}.", stats1.Skip(1).Average(t => t.TotalSeconds)); 
    log.InfoFormat("Mean time taken by system array = {0}.", stats2.Skip(1).Average(t => t.TotalSeconds)); 
} 

protected virtual IList<TimeSpan> CollectStats(Func<bool> func, int n = 100, int nPerRound = 100) 
{ 
    Stopwatch watch = new Stopwatch(); 
    var stats1 = new List<TimeSpan>(); 
    watch.Reset(); 
    for (int i = 0; i < n; i++) 
    { 
     using (ILScope.Enter()) 
     { 
      watch.Reset(); 
      watch.Start(); 
      for (int j = 0; j < nPerRound; j++) 
      { 
       bool ret = func(); 
       Assert.IsTrue(ret); 
      } 
      watch.Stop(); 
      stats1.Add(watch.Elapsed); 
     } 
    } 
    return stats1; 
} 

我對這個奇怪的結果感到非常震驚。我預計ILArray在順序索引方面比系統陣列慢一點,但是1000倍太多了。有人可以幫助這裏診斷我的問題:

[INFO ] | 14:38:19,974 | [odes] |   NumericsTest 383 | Mean time taken by ILNumerics = 0.294103963157895. 
[INFO ] | 14:38:20,036 | [odes] |   NumericsTest 383 | Mean time taken by system array = 0.000271984210526316. 

haymo的之後的建議,下面的測試運行比較不同的實現速度。我認爲在這種情況下,由於不需要使用DominatesSys創建中間陣列,因此其性能是最快的。

[Test] 
public void TestILNumericsSpeed() 
{ 
    ILArray<double> p1Il = ILMath.rand(1000); 
    ILArray<double> p2Il = p1Il - 0.01; 
    double[] p1 = p1Il.GetArrayForRead(); 
    double[] p2 = p2Il.GetArrayForRead(); 
    int length = p1Il.S[0]; 
    Func<bool> func1 =() => 
     { 
      Dominates1(p1Il, p2Il, length); 
      return true; 
     }; 
    Func<bool> func2 =() => 
     { 
      Dominates2(p1Il, p2Il, length); 
      return true; 
     }; 
    Func<bool> func3 =() => 
     { 
      Dominates3(p1Il, p2Il, length); 
      return true; 
     }; 
    Func<bool> func4 =() => 
    { 
     Dominates4(p1Il, p2Il, length); 
     return true; 
    }; 
    Func<bool> func5 =() => 
    { 
     Dominates5(p1Il, p2Il, length); 
     return true; 
    }; 
    Func<bool> funcSys =() => 
     { 
      DominatesSys(p1, p2, length); 
      return true; 
     }; 
    var stats1 = IO.CollectStats(func1, 10, 1000); 
    var stats2 = IO.CollectStats(func2, 10, 1000); 
    var stats3 = IO.CollectStats(func3, 10, 1000); 
    var stats4 = IO.CollectStats(func4, 10, 1000); 
    var stats5 = IO.CollectStats(func5, 10, 1000); 
    var statsSys = IO.CollectStats(funcSys, 10, 1000); 
    log.InfoFormat("Mean time taken by Dominates1 = {0}.", stats1.Skip(1).Average(t => t.TotalSeconds)); 
    log.InfoFormat("Mean time taken by Dominates2 = {0}.", stats2.Skip(1).Average(t => t.TotalSeconds)); 
    log.InfoFormat("Mean time taken by Dominates3 = {0}.", stats3.Skip(1).Average(t => t.TotalSeconds)); 
    log.InfoFormat("Mean time taken by Dominates4 = {0}.", stats4.Skip(1).Average(t => t.TotalSeconds)); 
    log.InfoFormat("Mean time taken by Dominates5 = {0}.", stats5.Skip(1).Average(t => t.TotalSeconds)); 
    log.InfoFormat("Mean time taken by system array = {0}.", statsSys.Skip(1).Average(t => t.TotalSeconds)); 
} 

protected virtual bool Dominates1(ILInArray<double> p1, ILInArray<double> p2, int nObjectives) 
{ 
    using (ILScope.Enter(p1, p2)) 
    { 
     ILArray<double> p1In = p1; 
     ILArray<double> p2In = p2; 
     bool strong = false; 
     for (int i = 0; i < nObjectives; i++) 
     { 
      using (ILScope.Enter()) 
      { 
       if (p1In[i] > p2In[i]) 
        strong = true; 
       else if (p1In[i] < p2In[i]) 
       { 
        return false; 
       } 
      } 
     } 
     return strong; 
    } 
} 

protected virtual bool Dominates2(ILInArray<double> p1, ILInArray<double> p2, int nObjectives) 
{ 
    using (ILScope.Enter(p1, p2)) 
    { 
     ILArray<double> n = p1[r(0, nObjectives - 1)] - p2[r(0, nObjectives - 1)]; 
     if (any(n < 0)) return false; 
     return any(n > 0); 
    } 
} 

protected virtual bool Dominates3(ILInArray<double> p1, ILInArray<double> p2, int nObjectives) 
{ 
    using (ILScope.Enter(p1, p2)) 
    { 
     ILArray<double> n = p1[r(0, nObjectives - 1)] - p2[r(0, nObjectives - 1)]; 
     var strong = false; 
     foreach (var d in n) 
     { 
      if (d < 0) return false; 
      if (d > 0) strong = true; 
     } 
     return strong; 
    } 
} 

protected virtual bool Dominates4(ILInArray<double> p1, ILInArray<double> p2, int nObjectives) 
{ 
    using (ILScope.Enter(p1, p2)) 
    { 
     ILArray<double> p1In = p1; 
     ILArray<double> p2In = p2; 
     bool strong = false; 
     for (int i = 0; i < nObjectives; i++) 
     { 
      using (ILScope.Enter()) { // probably does not help with such tiny arrays ... 
       if (p1In.GetValue(i) > p2In.GetValue(i)) 
        strong = true; 
       else if (p1In.GetValue(i) < p2In.GetValue(i)) 
       { 
        return false; 
       } 
      } 
     } 
     return strong; 
    } 
} 

protected virtual bool Dominates5(ILArray<double> p1, ILArray<double> p2, int nObjectives) 
{ 
    bool strong = false; 
    for (int i = 0; i < nObjectives; i++) 
    { 
     if (p1.GetValue(i) > p2.GetValue(i)) 
      strong = true; 
     else if (p1.GetValue(i) < p2.GetValue(i)) 
      return false; 
    } 
    return strong; 
} 


protected virtual bool DominatesSys(double[] p1, double[] p2, int nObjectives) 
{ 
    bool strong = false; 
    for (int i = 0; i < nObjectives; i++) 
    { 
     if (p1[i] > p2[i]) 
      strong = true; 
     else if (p1[i] < p2[i]) 
     { 
      return false; 
     } 
    } 
    return strong; 
} 

結果如下:

[INFO ] | 12:55:01,911 | [odes] |   NumericsTest 379 | Mean time taken by Dominates1 = 2.85064264444444. 
[INFO ] | 12:55:01,976 | [odes] |   NumericsTest 380 | Mean time taken by Dominates2 = 0.0402656666666667. 
[INFO ] | 12:55:01,977 | [odes] |   NumericsTest 381 | Mean time taken by Dominates3 = 0.173880833333333. 
[INFO ] | 12:55:01,978 | [odes] |   NumericsTest 382 | Mean time taken by Dominates4 = 0.148000711111111. 
[INFO ] | 12:55:01,979 | [odes] |   NumericsTest 383 | Mean time taken by Dominates5 = 0.0593142444444444. 
[INFO ] | 12:55:01,980 | [odes] |   NumericsTest 383 | Mean time taken by system array = 0.00180445555555556. 
+0

是否使用一個發佈版本可以調整p1 - p2?計時器沒有附加任何調試器? –

+0

是的,這些是我檢查的第一件事。任何想法爲什麼時差?我真的很想提高這種方法的速度,因爲它被稱爲很多次。目前,我正在用系統數組替換此方法,並使用GetArrayForRead()傳遞參數。 – doraemon

+0

我認爲在我的答案中的最後編輯將是你會得到的最佳折中... –

回答

1

您的時間是多少太小。也許你的問題大小也。增加兩者(迭代次數,問題大小)以獲得更可靠的結果。此外,不要忘記排除測量的第一次迭代(由於JIT編譯的開銷較大)。

我懷疑,因素1000是可靠的。但總的來說,很明顯,以你所做的方式迭代ILArray不能帶來與迭代double []相同的性能。 ILNumerics的工作方式(你應該適應)是矢量化。以整個數組參與計算而不是單個元素的方式重寫算法。

如果這是不可能的,那麼打破這種微內核的ILArray沒有任何錯誤。您使用的方式(GetArrayForRead(),GetArrayForWrite())就好,爲了訪問底層系統數組並將其用於這種元素計算。

另一種方式,你可以嘗試如下:

protected virtual bool Dominates(ILInArray<double> p1, ILInArray<double> p2, int nObjectives) { 
    using (ILScope.Enter(p1, p2)) { 
     ILArray<double> p1In = p1; 
     ILArray<double> p2In = p2; 
     bool strong = false; 
     for (int i = 0; i < nObjectives; i++) { 
      //using (ILScope.Enter()) { // probably does not help with such tiny arrays ... 
       if (p1In.GetValue(i) > p2In.GetValue(i)) 
        strong = true; 
       else if (p1In.GetValue(i) < p2In.GetValue(i)) { 
        return false; 
       } 
      //} 
     } 
     return strong; 
    } 
} 

但更有前途,國際海事組織將是類似的東西。(期待中的代碼被ILMath派生的類的範圍內,所以ILMath是中省略)

protected virtual bool Dominates(ILInArray<double> p1, ILInArray<double> p2, int nObjectives) { 
    using (ILScope.Enter(p1, p2)) { 
     ILArray<double> n = p1 - p2; 
     if (any(n < 0)) return false; 
     return any(n > 0); 
    } 
} 

請再次測量,並讓我們知道您的結果!由於

@Edit:再次思考,還有另一種選擇:

protected virtual bool Dominates(ILInArray<double> p1, ILInArray<double> p2, int nObjectives) { 
    using (ILScope.Enter(p1, p2)) { 
     ILArray<double> n = p1 - p2; 
     var strong = false; 
     foreach (var d in n) { 
      if (d < 0) return false; 
      if (d > 0) strong = true; 
     } 
     return strong; 
    } 
} 

如果p1.Length != p2.Length,你因此...

+0

感謝您的建議。我再次測試了它們,並將結果添加到我的帖子中。系統陣列仍然勝過。正如你所說的,可能是因爲數組的大小很小,而不是ILNumerics完成的內存管理的理由。由於在評估Dominates函數時不需要中間數組,所以可能直接使用系統數組來處理這種情況是更好的選擇? – doraemon

+0

可能是的,是的。而就你而言,最內層的功能非常簡單,使用複雜的ILArray甚至不會增加可讀性。此外,問題規模太小,所以自動並行化,緩存優化和ILNumerics中OOB檢查的刪除的優點完全由子數組創建和較慢的順序索引訪問完成。我想,還是可以改進實施。但它可能不會在這裏得到回報。 –

+0

請標記爲正確的答案,如果它幫助你:) –