如何使此代码更快(学习最佳实践)

How to make this code faster (learning best practices)?

本文关键字:最佳 学习 何使此 代码      更新时间:2023-10-16

我这里有一个小循环,我想知道我是否犯了一些大错误,性能明智。

例如,有没有办法以不同的方式重写其中的某些部分,使矢量化成为可能(假设GCC4.8.1和所有vecotrization友好标志都已启用)?

这是向列表传递数字(常量浮点数 name_of_var[])的最佳方法吗?

代码的思想是获取(未排序数)y的向量(在数学意义上,不一定是 std::vector)和两个绑定值 (ox[0]<=ox[1] ),并将整数向量存储在整数向量中rdx满足 ox[0]<=y[i]<=ox[1] y条目的索引i

rdx可以包含m元素,y具有容量nn>m。如果超过 m满足 ox[0]<=y[i]<=ox[1] y[i]的值,则代码应返回第一个m

提前感谢,

void foo(const int n,const int m,const float y[],const float ox[],int rdx[]){
    int d0,j=0,i=0;
    for(;;){
        i++;
        d0=((y[i]>=ox[0])+(y[i]<=ox[1]))/2;
        if(d0==1){
            rdx[j]=i;
            j++;
        }
        if(j==m)    break;
        if(i==n-1)  break;
    }
}
d0=((y[i]>=ox[0])+(y[i]<=ox[1]))/2;
if(d0==1)

我相信使用中间变量是没有用的,需要更多循环

这是我能想到的最优化的版本,但它完全不可读......

void foo(int n, int m, float y[],const float ox[],int rdx[])
{
    for(int i = 0; i < n && m != 0; i++)
    {
        if(*y >= *ox && *y <= ox[1])
        {
            *rdx=i;
            rdx++;
            m--;
        }
        y++;
    }
}

我认为以下带有体面优化标志的版本应该可以完成这项工作

void foo(int n, int m,const float y[],const float ox[],int rdx[])
{
    for(int j = 0, i = 0; j < m && i < n; i++) //Reorder to put the condition with the highest probability to fail first
    {
        if(y[i] >= ox[0] && y[i] <= ox[1])
        {
            rdx[j++] = i;
        }
    }
}

只是为了确保我是正确的:您正在尝试找到第一个 m+1(如果它实际上是 m,j == m-1)值在 [ ox[0], ox[1] ] 范围内?

如果是这样,这样做不是更好吗:

for (int i=0, j=0;;++i) {
    if (y[i] < ox[0]) continue;
    if (y[i] > ox[1]) continue;
    rdx[j] = i;
    j++;
    if (j == m || i == n-1) break;
}
  1. 如果 y[i] 确实在该范围内,您必须像我们一样执行这两个比较。
  2. 如果 y[i] 在 ox[0] 下,则无需执行第二次比较。
  3. 我避免使用除法。

A.是的,将浮点数组作为 float[] 传递不仅有效,而且是唯一的方法(并且与 float * 参数相同)。

回答 1.但在C++您可以使用更好的类型而不会降低性能。访问向量或数组(标准库容器)不应比访问纯 C 样式数组慢。我强烈建议您使用这些。在现代C++中,还可以使用迭代器和函子;我不是那里的专家,但如果您可以通过更抽象的方式来表达对不同元素的操作的独立性,您可能会让编译器有机会生成更适合矢量化的代码。

二.您应该将除法替换为逻辑 AND、运算符 &&。第一个优点是,如果第一个条件为假,则根本不评估第二个条件 - 这可能是您在这里最重要的性能提升。第二个优点是表现力,因此可读性。

三.当您使用 -O3 编译时,中间变量 d0 可能会消失,但它仍然是不必要的。

其余的在性能方面还可以。从习惯上讲,正如已经表明的那样,还有改进的余地。

D.我不确定是否有机会使用此处介绍的代码进行矢量化。编译器可能会在 -O3 处执行一些循环展开;尝试让它发出 SSE 代码(参见 http://gcc.gnu.org/onlinedocs/,特别是 http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/i386-and-x86-64-Options.html#i386-and-x86-64-Options)。谁知道呢。

哦,我刚刚意识到您的原始代码将常量间隔边界作为具有 2 个元素的数组 ox[] 传递。由于数组访问不必要的间接寻址,因此可能会带来开销,因此此处首选使用两个正常的浮点参数。让它们像你的阵列一样持续存在。你也可以很好地命名它们。