我不确定这是属于这里还是Programmers.SE。
我有一个简单的for循环,里面有几个if语句。 for
循环需要超过200,000个元素,这样做非常慢。运行循环需要10多分钟,我必须运行此循环900次。代码如下。我已经计时了循环的内部体,它相当快。慢循环是因为元素的数量而不是循环的主体。这表明我无法并行化,因为“工作”本身不是CPU消耗,这是正确的吗?
任何人都有关于如何改善执行时间的想法?
//main interaction function for the day
// assumption: if a human is bit, and becomes infected.. can not get BIT again
public void Interaction()
{
// need to go through all mosquitos
for (int i = 0; i < Cnts.MosqPopulation; i++)
{
var mosq = Mosquitos[i];
// see if this mosquito will bite someone
if (mosq.bitedistribution[(int)mosq.age - 1] == 1)
{
// who will it bite? get a list of good humans to bite
// they have to non-isolated and they shouldnt have been bit before
var nonisohumans = Humans.FindAll(x => (x.health != HumanStatus.SymptomaticIsolated
&& x.swap == HumanStatus.Null));
// pick a random human from the pool of candidates to bite.
var rhuman = nonisohumans[Utils.random.Next(nonisohumans.Count)];
// if the human is susceptible and mosquito is symptomatic
if (rhuman.health == HumanStatus.Susceptible && mosq.health == MosquitoStatus.Infectious)
{
// see if the disease will transfer to him
if (Utils.random.NextDouble() < Cnts.Contgion_MtoH)
{
rhuman.swap = HumanStatus.Latent;
}
}
// if the human is (a)symptomatic and mosqutio is susceptible
if ((rhuman.health == HumanStatus.Symptomatic || rhuman.health == HumanStatus.ASymptomatic) && mosq.health == MosquitoStatus.Susceptible)
{
// see if the disease will transfer to the mosquito
double probabilityofswap;
if (rhuman.health == HumanStatus.Symptomatic)
{
probabilityofswap = Cnts.Contgion_HtoM;
}
else
{
//reduced transmission
probabilityofswap = Cnts.Contgion_HtoM * Cnts.ReductionFactor;
}
if (Utils.random.NextDouble() < probabilityofswap)
{
mosq.swap = MosquitoStatus.Latent;
}
}
// Console.WriteLine("Mosquito i:{0} will bite today", i);
// Console.WriteLine("Its distribution was: {0}", string.Join(",", Mosquitos[i].bitedistribution));
}
}
}
答案 0 :(得分:2)
首先:使用分析器。找出你真正花费时间的地方。在我进行性能分析的大约三分之一时间里,问题实际上在哪里我完全错了。不要猜;测量
第二:让我们假设FindAll是你的问题;这似乎是最有可能的罪魁祸首。这里的操作是:
如果人类的集合很大,这是一项重量级的操作。
考虑通过选择更好的数据结构来解决问题;根本没有人类的集合。有两个(或更多)人类集合:一组非孤立的人类和一群孤立的人类,比如说。
让我们做一些假设:
如果这些假设成立,那么我会将非孤立的人列为一个列表,并将人类隔离开来。当一个孤立的人变成一个非孤立的人时,它会从集合中移除 - 一个廉价的操作 - 并添加到列表的 end - 这也是一个廉价的操作。
现在,每次需要随机非隔离人类时,您都不需要构建新列表。您已经在维护该列表。
答案 1 :(得分:1)
建立Oli的评论,尝试更改:
var nonisohumans = Humans.FindAll(x => (x.health != humanStatus.SymptomaticIsolated && x.swap == HumanStatus.Null));
为:
var nonisohumans = Humans.Where(x => (x.health != humanStatus.SymptomaticIsolated && x.swap == HumanStatus.Null));
并将其从循环中完全删除,因此它只处理一次。然后在你再次得到随机值后再运行你的条件(x.health!= humanStatus.SymptomaticIsolated&amp;&amp; x.swap == HumanStatus.Null)检查,以确保你没有选择一个你的'。已经迭代过了(如果有的话,得到一个新的随机)。
看起来你也应该将你的最后一个if语句更改为else,否则如果前一个语句的计算结果为true(它们看起来是互斥的),它就不会被评估:
else if ((rhuman.health == HumanStatus.Symptomatic || rhuman.health == HumanStatus.ASymptomatic) && mosq.health == MosquitoStatus.Susceptible)
答案 2 :(得分:1)
首先,我会尝试优化循环。 例如,从循环中生成块。据我所知,它不依赖于循环参数。把它放在循环之前。
var nonisohumans = Humans.FindAll(x => (x.health != HumanStatus.SymptomaticIsolated
&& x.swap == HumanStatus.Null));
Parallel.For(0, Cnts.MosqPopulation, i =>
{
var mosq = Mosquitos[i];
// see if this mosquito will bite someone
if (mosq.bitedistribution[(int)mosq.age - 1] == 1)
{
// pick a random human from the pool of candidates to bite.
var rhuman = nonisohumans[Utils.random.Next(nonisohumans.Count)];
...
});
人类有多大集合?你在每次迭代时完全传递它。
循环中的其他位置不应该导致问题。
当然你可以使用&#34; Parallel.For&#34;并行化循环的方法。
答案 3 :(得分:0)
你可以完全摆脱FindAll。从整个系列中随机选择一个,直到你得到满足条件的一个,似乎要便宜得多。当你点击不符合条件的那些时,你将它们从集合中删除。您将从完整集合的副本开始。