如何在没有并行化的情况下更快地制作循环

时间:2016-07-21 17:41:20

标签: c# optimization

我不确定这是属于这里还是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));
            }

        }

    }

4 个答案:

答案 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。从整个系列中随机选择一个,直到你得到满足条件的一个,似乎要便宜得多。当你点击不符合条件的那些时,你将它们从集合中删除。您将从完整集合的副本开始。