2014-01-06 35 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

请标记为正确的答案,如果它帮助你:) –