【发布时间】:2015-11-16 10:02:57
【问题描述】:
假设如下代码:
if (myDictionary.ContainsKey(aKey))
myDictionary[aKey] = aValue;
else
myDictionary.Add(aKey, aValue);
此代码访问字典两次,一次用于确定aKey 是否存在,另一次用于更新(如果存在)或添加(如果不存在)。我猜当这段代码只执行几次时,这种方法的性能是“可以接受的”。但是,在我的应用程序中,类似的代码大约执行了 500K 次。我分析了我的代码,它显示了 80% 的 CPU 时间花在这部分上(见下图),因此这激发了改进。
第一个解决方法很简单:
myDictionary[aKey] = aValue;
如果aKey 存在,则其值将替换为aValue;如果不存在,则将KeyValuePair 与aKey 作为键和aValue 作为值添加到myDictionary。但是,这种方法有两个缺点:
首先,您不知道aKey 是否存在,这会阻止您执行其他逻辑。例如,您不能基于此解决方法重写以下代码:
int addCounter = 0, updateCounter = 0;
if (myDictionary.ContainsKey(aKey))
{
myDictionary[aKey] = aValue;
addCounter++;
}
else
{
myDictionary.Add(aKey, aValue);
updateCounter++;
}
第二,更新不能是旧值的函数。例如,您不能执行类似以下的逻辑:
if (myDictionary.ContainsKey(aKey))
myDictionary[aKey] = (myDictionary[aKey] * 2) + aValue;
else
myDictionary.Add(aKey, aValue);
第二种解决方法是使用ConcurrentDictionary。很明显,使用delegates 可以解决上述秒的问题;但是,我仍然不清楚如何解决 first 问题。
提醒一下,我关心的是加速。鉴于只有一个线程使用此过程,我不认为只有一个线程值得使用ConcurrentDictionary 的并发(带锁)惩罚。
我错过了一点吗?有人有更好的建议吗?
【问题讨论】:
-
再次确认一下:这是单线程的,这里没有同步访问的理由吗?所以你实际上并不需要 atomic 操作,而只是一种快速设置值并确定你是添加还是更新值的方法?
-
您可以检查
Count的项目在myDictionary[aKey] = aValue之后是否在字典中发生了变化,以绕过第一个缺点。 -
首先,将指定区域[_i]缓存在一个循环范围变量中,然后,将指定区域[_i].lambdas缓存在另一个循环范围变量中。你看到指定区域[_i].mu-- 中的 4,5% cpu 时间了吗?单独这样做可能会大大加快您的速度。
-
我的意思是,在你的循环中,有一个 DegisnatedRegion currentRegion = _designatedRegions[_i]; 类型的变量。然后,有另一个变量 Dictionary
currentLambdas = currentRegion.lambdas;我不是在谈论缓存,而是在本地将您处理的对象分配给变量,而不是通过索引器或属性获取器访问它们。仅从指定区域列表中获取指定区域就花费了 4.5% -
@ghord 检查字典中是否存在键也是 O(1)。
标签: c# dictionary atomic