是否有可能从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性能分析。
也许LINQ .Where或.Count的东西正在呈现所有元素?如果是这样,有没有像Element所示的方法,只有在Bool设置为false的元素上?
我现在唯一想到的方法就是在没有 .Where
的情况下进行,并在while(boolIsTrue)
中进行。机会是,它会击中一个错误的,但当几乎所有这些都是真的时,性能会比where()更差。
由于activeProxies
随机数发生器的ElementAt
计数不会是最新的,因此无法正常工作。
答案 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,以减少在每次调用时创建新列表的成本,但只有在您确定字典已经初始化时才会这样做。