经常迭代字典时减少分配和GC

时间:2017-08-05 17:43:36

标签: c# unity3d foreach garbage-collection

上下文:我正在使用Unity3D的IMGUI,其中OnGUI{}方法被非常频繁地调用/更新(每帧几次)以保持GUI内容的相关性。我需要通过Dictionary迭代数据并显示所述数据,但因为我也将对Dictionary内容进行更改(添加/删除),我必须遍历单独的List / Array /等等。 所以换句话说,我现在有:

foreach (string line in new List<string>(this.myDic.Keys))
{
 //fill GUI
 //edit Dictionary content if needed
}

这里的问题是它每帧分配短暂的List多次,每秒数千次和数千次以及一般的疯狂量,从而产生GC。我想要的是通过重用在开始时初始化的相同List来避免这种分配。然而,另一个问题出现了:

tempList.Clear();
foreach (KeyValuePair<string,string> pair in myDic)
{
 tempList.Add(pair.key)
}
var j = tempList.Count;
for (int i = 0; i < j; i++)
{
 //fill GUI
 //edit Dictionary content if needed
}

正如你现在所看到的,我基本上有两个循环,都处理相同数量的数据。 这引出了我的问题:这里尝试使用可重复使用的List来优化分配问题是不是没有意义?或者即使它看起来很可怕,双循环变体仍然是更好的解决方案?

P.S。是的,我知道,最好的选择是从IMGUI切换但是现在我有点受限。

1 个答案:

答案 0 :(得分:0)

首先,通过调用new List<string>(this.myDic.Keys)(或者this.myDic.Keys.ToArray())分配的唯一对象是包含对现有对象(在您的情况下为字符串)的引用的数组。因此,当范围结束时,GC只收集一个对象。

该对象的大小约等于objectCount * referenceSize。参考大小取决于所选平台:32位或64位。

正式讲,您可以通过重用现有列表来节省一些内存流量,但我认为这不值得。

无论如何,如果您要这样做,请注意您重新填写列表的方法并不是最佳选择。我建议您使用.AddRange(内部使用Array.Copy)。

tempList.Clear();
tempList.AddRange(myDic.Keys)
foreach (key in tempList) { ... } //replace with 'for' if you also want to avoid allocation of an iterator

最有可能是过早优化,请做一些性能基准来测试它是否对您的应用程序有任何意义。