C# - IEnumerable选择1个单个随机元素CPU使用率

时间:2018-05-03 12:14:51

标签: c#

是否有可能从IEnumerable中选择1个随机元素而不直接将所有元素加载到内存中?

我试图在循环中每秒两次从17,000个KeyValuePairs列表中选择1个随机代理。

KeyValuePairs是:string,bool。其中string是代理,bool是否被禁用。

concDict == ConcurrentDictionary<string, bool> (btw)
IEnumerable<KeyValuePair<string, bool>> temp = concDict.Where(p => !p.Value);
activeProxies = temp.Count();
proxy = temp.ElementAt(proxyRandom.Next(0, activeProxies - 1)).Key;

以上就是我到目前为止所做的。这里的问题是它导致了大量的CPU使用。下图是大约10秒的CPU性能分析。

enter image description here

也许LINQ .Where或.Count的东西正在呈现所有元素?如果是这样,有没有像Element所示的方法,只有在Bool设置为false的元素上?

我现在唯一想到的方法就是在没有.Where的情况下进行,并在while(boolIsTrue)中进行。机会是,它会击中一个错误的,但当几乎所有这些都是真的时,性能会比where()更差。
由于activeProxies随机数发生器的ElementAt计数不会是最新的,因此无法正常工作。

2 个答案:

答案 0 :(得分:1)

IEnumerable在这里使用是错误的。相反,使用List - 这将允许您更快地计算和选择随机项目,并且CPU使用率更低:

// this enumerate the dictionary one and the result of the where once.
List<KeyValuePair<string, bool>> temp = concDict.Where(p => !p.Value).ToList(); 
// now this will be a lot faster...
activeProxies = temp.Count;
proxy = temp.[proxyRandom.Next(0, activeProxies - 1)].Key;

答案 1 :(得分:0)

关于您的问题,如果不先评估所有成员,就不可能选择IEnumerable的随机成员。您当前的代码(如评论中的其他成员所述)枚举两次:获取活动代理,然后获取其计数。可能是第三次跳过随机数(不确定实现细节)。

更好的解决方案(在我看来)是使用StampedLock#tryOptimisticRead()这样可以更快地获得一个活跃的随机代理,并且不必反复枚举。 17k条目没有太多数据担心在内存中加载,特别是它们是简单类型而不是很大。

ConcurrentDictionary<bool, List<string>>

如果密钥不存在,这将添加列表,如果密钥存在,则将字符串添加到列表中。 您可以使用空列表初始化字典,并将其作为第二个参数传递给null,以减少在每次调用时创建新列表的成本,但只有在您确定字典已经初始化时才会这样做。