.NET性能周期性?

7
我们在一个复杂的图像处理算法的最后添加了以下代码,以将结果保存到文本文件中。此函数的输入是代表图像处理输出的 float[,] p_RangeMap
StringBuilder stringBuilder = new StringBuilder(30 * 1024 * 1024);
stringBuilder.AppendLine("Row" + ms_csvSeparator + p_RangeMap.GetLength(0));
stringBuilder.AppendLine("Col" + ms_csvSeparator + p_RangeMap.GetLength(1));

Stopwatch stopwatch = Stopwatch.StartNew();

for (int y = 0; y < p_RangeMap.GetLength(0); y++)
{
     for (int x = 0; x < p_RangeMap.GetLength(1); x++)
     {
          stringBuilder.Append(p_RangeMap[y, x].ToString(CultureInfo.InvariantCulture));
          stringBuilder.Append(ms_csvSeparator);
     }
     stringBuilder.AppendLine();
}

stopwatch.Stop();
Console.WriteLine("MeasureRunTime: RangemapConvert: " + stopwatch.Elapsed);

通过对这些代码进行6000次迭代的运行时间测量,我们得到了以下图表: 代码运行时间 这6000次迭代大约需要24小时才能完成。水平轴表示迭代次数,而垂直轴显示这些代码运行所需的经过时间(以秒为单位)。每次迭代的输入完全相同,p_RangeMap的尺寸为1312 x 3500。
它从大约两秒开始,在上升到13秒后,在900次迭代后回落,而(大约)900个周期形成一个循环。正如你所看到的,最高值约为22秒。
有什么想法可以解释运行时间的变化吗?
什么原因会导致这种周期性?
值得一提的是,代码的其他部分也显示出相同的行为,但这部分代码是最容易从源代码中抓取出来的。
更新1:
我已更新代码示例,StringBuilder预先估算了文件大小。我们也考虑了垃圾回收,请考虑以下内容:
900个周期意味着大约3.5小时,处理的输入数据约为16 GB(反复加载相同的图片)。更不用说在图像处理期间由于各种原因创建的副本了。我认为GC应该会更频繁地触发。
16 GB来自:1312 * 3500 * 4 * 900

10
考虑到操作涉及到分配,我期望有垃圾回收机制... - Dark Falcon
1
考虑预分配StringBuilder - 我认为结果相当大,如果它经常需要扩展大小,则效率不高。如果结果输出到文件,请考虑逐行输出到较小的StringBuilder - 这看起来像是垃圾回收问题,这可能是一个原因。 - TomTom
我已经更新了问题,StringBuilder已经预先分配。逐行输出听起来很有趣,我们可以尝试一下。将会用结果更新帖子。 - toderik
你的输出有多大?在将其输出到流之前,你是否真的需要将整个输出存储在内存中?你可以跳过缓冲区,直接将其输出到流中。 - Sten Petrov
如果你怀疑垃圾回收,你也可以在关键位置放置调试日志来监视内存分配并确保。如果你能使用实际的分析器,那就更好了。 - Sten Petrov
2个回答

5
这很有趣。根据您的更新,您分配了16GB,您可能需要将Float数组和StringBuilder封装在一个类中,以便您可以重用缓冲区,而不必在每次重新运行时重新分配它们。(像重用相同的string builder和array,每次整个函数被调用时仅创建1个实例,因此每6000次运行仅创建1个实例)。这是为什么:您的生成器大小约为30MB,因此它分配在大型对象堆中(任何时候分配超过85,000字节,它都进入LOH)。 LOH的GC规则略有不同。首先,它仅在第二代集合(最昂贵的类型)上进行GC,因此如果您经常创建和撕毁大型分配,您可能会更频繁地强制执行Gen2集合。此外,LOH在第二代GC上不被碎片化(因为它太昂贵了),因此您更可能在分配大型时导致GC,因为您需要线性32MB。如果您将浮点数组和字符串生成器封装起来,并在运行之间调用Clear()以将其清空而不释放它,并跟踪使用的浮点数组的大小,我会非常好奇您的表现如何。
下面是关于LOH的文章:http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

1
谢谢您提供的链接,我们一定会尝试您建议的方法,并且我会及时告知您结果。 - toderik
1
酷!期待着它的到来! - JMarsch
1
您可以选择在.NET 4.5.1上进行LOH压缩 - http://msdn.microsoft.com/en-us/library/system.runtime.gclargeobjectheapcompactionmode(v=vs.110).aspx - Ian Gilroy

1

首先看一下:

我猜测这是一个内存问题。图表中的急剧下降看起来像是GC启动进行垃圾回收。

Stringbuilder非常高效,但我怀疑它是否考虑了这种情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接