我使用C#3.5和VS 2010终极版。
我优化(速度)机器学习算法,它有四个嵌套的for循环。 我发现一个简单的缓存(一个对象的张量)可能会大大提高性能,因为有很多重新分配相同的对象。
这是实施之前和之后。
在:
four nested for-loops:
var object = new object(3 parameters);
Calculate(object, other params)
后:
var cache = new object[count1,count2,count3];
three nested for-loops:
cache[param1, param2, param3] = new object(3 params);
four nested for-loops:
var object = cache[3 parameters];
Calculate(object, other params)
我已经描述了这两种方法和"之前"版本速度相当快,在GC中花费了大约18%的时间,而在"之后#34;版本在GC中花费了大约88%。 显而易见,添加此缓存会使GC活动增加,但我没有看到这是如何实现的。
我在应用程序中使用了许多长期存在的对象。在分析时我的机器没有处于重负荷状态。张量是使用多维数组(不是锯齿状数组)实现的。上面两种方法中最内层的循环是使用Parallel.For
构造实现的,在循环之前,我正在分配一个小的double
数组。
如何减少在GC中花费的时间?
编辑#1:结果确实来自发布模式。
编辑#2:后方法的实际代码是四个for循环:List<int> labels = // count = 80
List<int> tokens = // count = 35
var table = new double[tokens.Count, labels.Count, labels.Count];
var cachedObjects = new CachedObject[tokens.Count, labels.Count, labels.Count];
for (int k = 0; k < tokens.Count; k++)
{
foreach (var tagCurrent in labels)
{
foreach (var labelBack in labels)
{
double[] value = new double[labels.Count];
Parallel.For(0, labels.Count, (i) =>
{
CachedObject CachedObject = cachedObjects[k, labelsBackFurther[i], labelBack];
var me = ModelEstimate(vOptimal, CachedObject, tagCurrent, labels);
value[i] = table[k - 1, labels[i], labelBack] * me;
});
var maxValue = 0;
var maxTagIdx = 0;
for (int j = 0; j < value.Length; j++)
{
var item = value[j];
if (item > maxValue)
{
maxValue = item;
maxTagIdx = j;
}
}
table[k, labelBack, tagCurrent] = maxValue;
}
}
}
答案 0 :(得分:2)
GC受两个因素的影响:分配数量和幸存者数量。
分配触发器会收集,因此您分配的频率越高。
如果应用程序持有大量数据,则第2代收藏可能会变得非常昂贵。如果您在GC中花费了很多时间,通常就是这种情况(收集第0代和第1代的速度很快,因为它们的大小有限)。
在你的情况下,听起来你想要保留缓存。如果你这样做,你需要确保你保持分配,因为你不想触发昂贵的Gen 2收集。
您可以使用PerfView来跟踪分配。
答案 1 :(得分:1)
环境变量:
OMV_PATH = C:\ WINDOWS \ Temp表示放置日志文件的位置。
答案 2 :(得分:1)
缓存可能会在应用程序上增加额外的内存开销,从而导致应用程序以较少的可用内存运行以用于其他目的。在这种情况下,垃圾收集器不仅被迫更频繁地运行,而且由于内存负载增长占应用程序可用总内存的百分比,因此效率通常较低。
如果您有足够的系统内存,请尝试将应用程序重新运行为64位应用程序,以查看问题是否仍然存在。
根据一次使用的缓存元素的数量,可能会导致不必要地阻止多达count1*count2*count3 - 1
个对象以及多维数组本身被收集。
答案 3 :(得分:0)
如何减少在GC中花费的时间?
&#34; Garbage Collection Guidelines&#34;中有一个非常有趣的建议。微软表示
在使用缓存数据时,请考虑使用弱引用 如果需要或释放,缓存的对象可以很容易地复活 有内存压力的垃圾收集。
实施例
void SomeMethod()
{
// Create a collection
var arr = new ArrayList(5);
// Create a custom object
var mo = new MyObject();
// Create a WeakReference object from the custom object
var wk = new WeakReference(mo);
// Add the WeakReference object to the collection
arr.Add(wk);
// Retrieve the weak reference
WeakReference weakReference = (WeakReference)arr[0];
MyObject mob = null;
if( weakReference.IsAlive ){
mob = (MyOBject)weakReference.Target;
}
if(mob==null){
// Resurrect the object as it has been garbage collected
}
//continue because we have the object
}