帮我优化这个平均计算片段

时间:2009-06-27 21:48:51

标签: c# .net algorithm optimization

是否可以加快此代码段的速度?

firstSample lastSample 是我对此次迭代感兴趣的数组的一部分。当这个间隔达到> 1时3000我明显减速。 _average 数组可以包含6到6千万个int值。

minY maxY 是此计算完成后使用的结果。

int minY = Int32.MaxValue;
int maxY = Int32.MinValue;
int Y = 0;
int sample = firstSample + 1;

while (sample <= lastSample)
{
       Y = _average[sample];
       minY = Math.Min(Y, minY);
       maxY = Math.Max(Y, maxY);
       sample++;
}

9 个答案:

答案 0 :(得分:9)

_average [sample]表达式是一个巨大的瓶颈,因为它包含每次迭代的隐式边界检查。使用指向“_average”数组(和unsafe关键字)的指针。然后避免调用任何函数,所以摆脱Math.Min / Max调用并自己进行检查。

我现在没有任何编译器,我认为这应该是它的样子:

unsafe
{
    fixed ( int* paverage = _average )   
    {
        int* p = paverage + firstSample + 1;
        for ( int sample = firstSample+1 ; sample <= lastSample ; sample++ )   
        {
            if ( *p < minY )
                minY = *p;
            if ( *p > maxY )
                maxY = *p;
            p++;
        }
    }   
}

最后,由于“sample”实际上并未在循环中使用,因此您可以将其更改为循环变量,该变量向下计数到零,以便针对常量(零)而不是变量完成循环终止检查

答案 1 :(得分:1)

不安全代码允许您使用指针来索引数组,因为在这种特殊情况下JIT编译器将无法删除边界检查。看看here如何做到这一点。

您也可以尝试自己内联最小/最大呼叫,但JIT很可能已经为您做了这样的事情。

最后,将它与.NET 4的Parallel Extensions并行化很容易(您可以使用CTP for .NET 3.5)。只需确保不要同时写入多个线程的最小值/最大值。不要锁定它,我会为每个线程设置一个最小/最大值,并在完成所有线程后在每个线程/任务的最小/最大值之间进行最终比较。

答案 2 :(得分:1)

您在评论中写了以下内容:

  

我没有排序。仅查找间隔的最大值和最小值。间隔每20毫秒移动

您似乎确实想要移动最小移动最大

我认为这可以比每次重新搜索整个区间更有效率,假设区间仅在一个方向上移动,并且后续区间之间存在显着重叠。

一种方法是保留一个特殊队列,其中每个新元素将其值复制到队列中较大的每个元素(对于移动最小值),例如:

(5 8 4 7 7 0 7 0 4 4 3 4 0 9 7 9 5 4 2 0)  ; this is the array
(4 4 4 4)  ; the interval is 4 elements long, and initialized to the minimum
           ; of the first 4 elements
  (4 4 4 7)  ; next step, note that the current minimum is always the first element
    (4 7 7 0)  ; now something happens, as 0 is smaller than the value before
    (4 7 0 0)  ; there are still smaller values ...
    (4 0 0 0)  ; and still ...
    (0 0 0 0)  ; done for this iteration
      (0 0 0 7)
        (0 0 0 0)  ; the 0 again overwrites the fatties before
          (0 0 0 4)
            (0 0 4 4)
              (0 3 3 3)  ; the 3 is smaller than the 4s before,
                         ; note that overwriting can be cut short as soon as a
                         ; value not bigger than the new is found
                (3 3 3 4)
                  (0 0 0 0)  ; and so on...

如果每次移动的元素超过1个,则可以先计算所有新值的最小值,然后将其用于反向覆盖。

此算法的最坏情况是当数组按降序排序时,则为O(nm),其中m是区间长度,n是数组长度。最好的情况是它按降序排序,然后是O(n)。对于一般情况,我推测O(n log(m))。

答案 3 :(得分:0)

如果你有3.5+框架

,你可以使用FOR比while更快或使用Parallel

答案 4 :(得分:0)

我要伸出脖子说不,我不认为有任何办法可以明显加快速度(除非将调用Min和Max的内容有所帮助,但我希望优化者会照顾到这一点)。

但是,如果你在同一数据上多次这样做,那么对数据进行排序(或每次处理的数据块)可能会使整个过程更快。

排序比找到最小值要慢,但排序一次比找到最少一千次更快。

(如果我教你在这里吮鸡蛋,请原谅我.8 - )

答案 5 :(得分:0)

首先,我将其重写为一个简单的for循环,并避免使用Pascal环境的局部变量,包括一个范围超出其需要的变量:

int minY = int.MaxValue;
int maxY = int.MinValue;

for (int sample = firstSample + 1; sample <= lastSample; sample++)
{
    int y = _average[sample];
    minY = Math.Min(y, minY);
    maxY = Math.Max(y, maxY);
}

这只是为了让它更熟悉和传统。 JIT知道在某些情况下循环数组,但我不知道在这种情况下它是否会有用 - 它可以只检查firstSample >= -1 && lastSample < _average.length然后消除边界检查,但我不知道不知道是不是。现在,已经处于当前最小/最大范围内的样本不需要任何副作用,所以在这种情况下让我们摆脱分配:

for (int sample = firstSample + 1; sample <= lastSample; sample++)
{
    int y = _average[sample];
    if (y < minY)
    {
        minY = y;
    }
    if (y > maxY)
    {
        maxY = y;
    }
}

我不知道这是否会有所帮助 - 我怀疑它不会,但它可能值得一试......

(正如另一个答案所说,这是一个非常简单的并行操作 - 它应该与CPU数量几乎线性地提高速度,即2个处理器〜=两倍的速度等,除了缓存未命中等。)

答案 6 :(得分:0)

您可以像其他人建议的那样尝试使用for循环。

它需要进行性能分析,但您也可以尝试消除方法调用和分支:

   Y = _average[sample];
   minY = minY + ((Y-minY) & (Y-minY)>>31);
   maxY = maxX - ((X-maxX) & (X-maxX)>>31);
   sample++;

只有在性能提升对您来说非常重要时才进行这些更改,因为代码的可维护性会因类似的结构而降低。

答案 7 :(得分:0)

像其他人所说的那样使用for循环,但是将其设置为比较为零。在大多数实现中,这是一个更快的比较。

答案 8 :(得分:0)

在找到新的分钟的情况下,你可以摆脱重复的比较。如果将最小值/最大值都设置为第一个值,那么如果找到新的最小值,则没有理由检查它是否也是新的最大值。这基本上是@Skeet的初始化代码和额外的'else'语句。

int firstIndex = firstSample + 1;
if (firstIndex <= lastSample)
{
    minY = _average[firstIndex];
    maxY = minY;

    for (int sample = firstIndex + 1; sample <= lastSample; sample++)
    {
        int y = _average[sample];
        if (y < minY)
        {
            minY = y;
        }
        else if (y > maxY)
        {
            maxY = y;
        }
    }
}