是否可以加快此代码段的速度?
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++;
}
答案 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;
}
}
}