【问题标题】:performance issue with nested for loops嵌套 for 循环的性能问题
【发布时间】:2017-05-05 17:27:16
【问题描述】:

我有一个嵌套的 for 循环来执行一些计算,并且数学已经在很大程度上简化了,但是我仍然有一个性能问题,我不确定我是否可以解决。由于这些 for 循环执行的次数太多,我不相信它可以解决。现在我不熟悉使用任何分析工具来帮助确定其中发生减速的位置,但我相当肯定这只是这些循环执行的次数。

如果能帮助我减少此代码并提高此代码的性能,我将不胜感激。我正在尝试远离 HPC 或高度并行化的解决方案,但如果这是使其真正有效的唯一方法,那么我会考虑走这条路。

这是 X= 20,000 和 N_zero= 45,420 的代码(从实际测试中提取的值):

Dictionary<decimal, int> n_alpha = new Dictionary<decimal, int>();
Random rand = new Random();
decimal r = 0m;
decimal check=0m;

for (int i = 0; i < X; i++)
{
    B = N_0 = N_1 = N0_ = N1_ = 0;
    for (int j = 0; j < N_zero; j++)
    {
        // need a random decimal value between 0 and 1
        r = (decimal)rand.Next() / int.MaxValue;
        if (r <= r1)
        {
            N0_ += 1;
            N_0 += 1;
        }
        else if (r1 < r && r <= r2)
        {
            B += 1;
            N0_ += 1;
            N_1 += 1;
        }
        else if (r2 < r && r <= r3)
        {
            B += 1;
            N_0 += 1;
            N1_ += 1;
        }
        else if (r > r3)
        {
            N1_ += 1;
            N_1 += 1;
        }

    }
    check = N_0 * N_1 * N0_ * N1_;
    if (check != 0)
    {
        decimal a = 1 - (B * N_zero) / ((N_0 *N1_) + (N0_ *  N_1 ));
        // technically only tracking 4 decimal points, so key should reflect this
        decimal key = Math.Round(a, 4);
        if (n_alpha.ContainsKey(key))
        {
            n_alpha[key] += 1;
        }
        else
        {
            n_alpha.Add(key, 1);
        }
    }
}

【问题讨论】:

  • 如果不需要精度,您可以将小数类型更改为浮点数,并且您不介意一些舍入错误。
  • @Gilles decimal 无法避免舍入错误。它只是改变发生什么样的舍入错误。在这种情况下没有足够的上下文可以确定,但我同意在这种情况下使用decimal 可能不合适(并且可能是导致性能不佳的一个相当重要的原因)。
  • 这段代码中有很多可疑之处。与其详细指出所有这些,不如学习使用分析器。只有通过经验测量,才能始终如一地解决性能问题。设定一个目标,衡量绩效,将其与您的目标进行比较,找到最慢的事情,尝试修复它,然后重复直到完成。 互联网上的陌生人无法可靠地告诉您代码慢的地方
  • 也就是说,加速这段代码最明显的事情是使用双精度数,而不是小数。你能说说为什么你选择十进制而不是双精度吗?这些是财务计算吗?
  • 顺便说一句,我使用双精度(或适当的整数)重新实现了这段代码,使用双精度的版本执行速度快了近 14 倍(它在大约 19 秒内完成,而几乎是 4 秒半给定代码的分钟数)。给定相同的随机种子,它也会产生相同的输出。

标签: c# performance for-loop


【解决方案1】:
  1. rand.Next() 的使用有点性能问题。如果你能和一个自制的、快速的、肮脏的Linear Congruential Generator一起生活,你会过得更好。

  2. decimal 的使用也是一个主要的性能问题。如果可以避免,请不要使用decimal。请改用double

  3. 使用Dictionary 也可能有点问题,尤其是当您的数据分布导致大量哈希冲突时。我不知道你的数值数据的范围是什么,也不知道它在那个范围内是如何分布的,但是如果你可以用数组替换字典,一定要这样做。

  4. 您也许可以完全消除您的内部嵌套循环。由于rand.Next() 应该产生具有均匀分布的数字,并且由于r1r2r3 是预先知道的并且不会在循环中更改,因此您可以简单地计算将发出多少rs低于r1,有多少rs 将落在r1r2 之间,等等。实际上没有发出任何rs。因此,只需将相应数量添加到您的N0N_1 等,而不是每次都添加1

编辑关于#4的说明:

因此,从您发出r 的方式来看,它的值可以从0.01.0。 (包括或完全无关紧要。)所以,我假设r1r2r3 也在0.01.0 之间。因此,假设完全均匀分布,您的rs 应该如下:

  • r &lt; r1 将出现N_Zero * r1

  • r1 &lt; r &lt; r2 将出现N_Zero * (r2 - r1)

  • r2 &lt; r &lt; r3 将出现N_Zero * (r3 - r2)

  • r3 &lt; r 将出现N_Zero * (1 - r3) 次。

【讨论】:

  • 您如何预先确定每张支票将开出多少 r?我正在考虑使用数组而不是字典,但是我需要一些时间来弄清楚如何处理数组和索引。同样根据我的要求,我认为线性同余生成器不会在这里工作,但我会牢记这一点并进行一些测试。
  • @shadonar 我修改了答案。
猜你喜欢
  • 2014-12-09
  • 1970-01-01
  • 1970-01-01
  • 2012-01-13
  • 1970-01-01
  • 2014-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多