當我試圖來分析一個緩慢的優化算法,我發現以下方法所需的時間比預期的要長得非常慢:迭代通過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.
是否使用一個發佈版本可以調整
p1 - p2
?計時器沒有附加任何調試器? –是的,這些是我檢查的第一件事。任何想法爲什麼時差?我真的很想提高這種方法的速度,因爲它被稱爲很多次。目前,我正在用系統數組替換此方法,並使用GetArrayForRead()傳遞參數。 – doraemon
我認爲在我的答案中的最後編輯將是你會得到的最佳折中... –