问题 使用数组限制?


有没有办法告诉C99编译器我要访问给定数组的唯一方法是使用myarray [index]? 说这样的话:

int heavy_calcualtions(float* restrict range1, float* restrict range2)
{
    float __I promise I won't alias this__ tmpvalues[1000] = {0};

    ....
    heavy calculations using range1, range2 and tmpvalues;
    ....
}

通过使用restrict我承诺我不会为range1和range2设置别名但是如何为我的函数内部声明的数组做同样的事情?


12277
2017-09-26 11:11


起源

它抛出了什么样的警告? - dhein
“无效使用限制” - 它应该与指针一起使用,而不是数组(据我所知)。我可以做浮动*限制tmpvalues = malloc(sizeof(float)* 1000),但后来我没有在堆栈上分配,这也可能影响性能。除了告诉编译器访问数组索引是安全的(因此不需要防御性读取)似乎非常自然地扩展了限制使用指针,因此直观地必须有一种方法可以做到这一点。 - Piotr Lopusiewicz
我做了:float * restrict tmpvalues = alloca(sizeof(float)* 1000); memset(tmpvalues,0,sizeof(float)* 1000);这是可衡量的改进,但我更希望在标准中做到这一点(如在C99投诉方式) - Piotr Lopusiewicz
@PiotrLopusiewicz你可以在哪里发布一些实际的代码 float *restrict tmpvalues 优于VLA? (已经准备好了时间码) - M.M
使用restrict指针访问数组。 - Jeff


答案:


虽然Jeff的答案是正确的,即你总是可以指向分配的数组,但事实是编译器 知道 在编译时,tmpvalues不会被别名,因为变量被声明为实际数组,而不是指针。别名数组的唯一机会是声明指向它的指针,所以如果你不这样做,就没有必要将它声明为 restrict。如果这更明显 tmpvalues 是函数中唯一的变量。

如果将指针传递给另一个函数可能会出现问题,那么您应该说明接收的指针是否受限制。

我遇到的与此主题相关的文档包括 C99

设D是提供方法的普通标识符的声明   将对象P指定为类型T的限制限定指针。

请注意,它仅适用于指针。

这个其他文件 TI提供了一些性能调优提示 restrict 关键词。除了所有提示之外,第3.3节提供了可以应用此类型限定符的示例,而不是。寻找 x 在第16页中间的数组声明,它声明它没有声明指针,因此不能 restrict-合格。


10
2017-08-25 07:30



如果指向数组的指针传递给外部代码,则没有干净的方法来指示外部代码不会保留指针的副本,并在下次调用外部代码时将该副本用于任意目的。恕我直言,如果C有一个类似的限定词会有帮助 restrict但是用于已经采用地址但仅以非常有限的方式使用的变量,但没有定义这样的特征。 - supercat
@supercat是一个问题 - 你如何定义'各种方式'? gcc具有可以附加到函数的属性(例如'pure'),这些函数承诺函数不会执行某些类。除此之外,我们现在有“链接时间优化”,编译器工具集基本上可以查看函数实际执行的操作,并使用该信息。 - greggo
@greggo:如果我正在编写规则,我会说a register - 限定对象可能只在函数参数的计算中使用其地址,并且只有在函数返回之前以及在函数执行期间所有使用结果指针或从中派生的指针的情况下才会定义行为( 1)仅通过结果指针或从其导出的其他指针访问对象,或(2)不通过任何方式修改对象。如果构建过程从构建每个函数中使用的所有外部符号的列表开始... - supercat
...编译器可以安全地假设,如果“foo”和“foo”调用的任何函数都没有使用与注册限定对象关联的符号“bar”,则对“bar”的访问可以被视为未经序列的相对对于函数内发生的任何事情[具有给定签名的每个函数的定义将被视为从该签名类型的泛型函数到该函数的调用,并且对签名类型的指针的间接调用将是一个调用到该类型的泛型函数,从而调用与该签名匹配的每个函数]。 - supercat


你为什么不能做以下事情?您没有访问与之关联的数据 tmpvalues 通过该变量,因此在代码的计算密集型部分中使用限制指针是有效的。

#include <stdio.h>
#include <stdlib.h>

int heavy_calcs(int n, float* restrict range1, float* restrict range2)
{
    if (n>1000) return 1;
    float tmpvalues[1000] = {0};
    {
        float * restrict ptv = tmpvalues;
        for (int i=0; i<n; i++) {
            ptv[i] = range1[i] + range2[i];
        }
    }
    return 0;
}

int main(int argc, char * argv[])
{
    int n = (argc>1) ? atoi(argv[1]) : 1000;
    float * r1 = (float*)malloc(n*sizeof(float));
    float * r2 = (float*)malloc(n*sizeof(float));
    int rc = heavy_calcs(n,r1,r2);
    free(r1);
    free(r2);
    return rc;
}

我通过英特尔15编译器运行它,它可以很好地矢量化循环。当然,与我假设的相比,这个循环是微不足道的,所以你的里程可能会有所不同。


3
2018-04-28 21:07



无需投射结果 malloc 在C. - phuclv
没有必要 {} 周围的 ptv 任务。 - Jeff