Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C中使用restrict关键字的规则?_C_Optimization_Memory - Fatal编程技术网

在C中使用restrict关键字的规则?

在C中使用restrict关键字的规则?,c,optimization,memory,C,Optimization,Memory,我试图理解在C语言中什么时候以及什么时候不使用restrict关键字,以及在什么情况下它提供了切实的好处 在阅读“,”(它提供了一些关于使用的经验规则)之后,我得到的印象是,当向函数传递指针时,它必须考虑指向的数据可能与传递到函数中的任何其他参数重叠(别名)的可能性。给定一个函数: foo(int *a, int *b, int *c, int n) { for (int i = 0; i<n; ++i) { b[i] = b[i] + c[i];

我试图理解在C语言中什么时候以及什么时候不使用
restrict
关键字,以及在什么情况下它提供了切实的好处

在阅读“,”(它提供了一些关于使用的经验规则)之后,我得到的印象是,当向函数传递指针时,它必须考虑指向的数据可能与传递到函数中的任何其他参数重叠(别名)的可能性。给定一个函数:

foo(int *a, int *b, int *c, int n) {
    for (int i = 0; i<n; ++i) {
        b[i] = b[i] + c[i];
        a[i] = a[i] + b[i] * c[i];
    } 
}
然后您就可以了解编译器为什么要这样做了。使用
restrict
有效地告诉编译器您永远不会这样做,这样它就可以在存储
b
之前删除
c
和load
a
的冗余负载

到目前为止,我认为在传递给函数的指针上使用
restrict
是个好主意,这些函数不会内联。显然,如果代码是内联的,编译器可以发现指针没有重叠

现在,事情开始变得模糊起来。

在Ulrich Drepper的论文中,“他声明,”除非使用限制,否则所有指针访问都是潜在的别名来源”,他给出了一个子矩阵乘法的具体代码示例,其中他使用
restrict

然而,当我编译他的示例代码时,无论是否使用
restrict
,我都会得到相同的二进制文件。我使用的是gcc版本4.2.4(Ubuntu 4.2.4-1ubuntu4)

在下面的代码中,我无法弄清楚的是,是否需要重写它以更广泛地使用
restrict
,或者GCC中的别名分析是否非常好,以至于它能够弄清楚没有任何一个参数彼此别名出于纯粹的教育目的,我如何使使用或不使用
restrict
在本代码中起作用?为什么?

对于
restrict
,编译时使用:

gcc -DCLS=$(getconf LEVEL1_DCACHE_LINESIZE) -DUSE_RESTRICT -Wextra -std=c99 -O3 matrixMul.c -o matrixMul
只需删除
-DUSE\u RESTRICT
即可不使用
RESTRICT

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

#ifdef USE_RESTRICT
#else
#define restrict
#endif

#define N 1000
double _res[N][N] __attribute__ ((aligned (64)));
double _mul1[N][N] __attribute__ ((aligned (64)))
    = { [0 ... (N-1)] 
    = { [0 ... (N-1)] = 1.1f }};
double _mul2[N][N] __attribute__ ((aligned (64)))
    = { [0 ... (N-1)] 
    = { [0 ... (N-1)] = 2.2f }};

#define SM (CLS / sizeof (double))

void mm(double (* restrict res)[N], double (* restrict mul1)[N], 
        double (* restrict mul2)[N]) __attribute__ ((noinline));

void mm(double (* restrict res)[N], double (* restrict mul1)[N], 
        double (* restrict mul2)[N])
{
 int i, i2, j, j2, k, k2; 
    double *restrict rres; 
    double *restrict rmul1; 
    double *restrict rmul2; 

    for (i = 0; i < N; i += SM)
        for (j = 0; j < N; j += SM)
            for (k = 0; k < N; k += SM)
                for (i2 = 0, rres = &res[i][j],
                    rmul1 = &mul1[i][k]; i2 < SM;
                    ++i2, rres += N, rmul1 += N)
                    for (k2 = 0, rmul2 = &mul2[k][j];
                        k2 < SM; ++k2, rmul2 += N)
                        for (j2 = 0; j2 < SM; ++j2)
                          rres[j2] += rmul1[k2] * rmul2[j2];
}

int main (void)
{

    mm(_res, _mul1, _mul2);

 return 0;
}
#包括
#包括
#包括
#ifdef使用限制
#否则
#定义限制
#恩迪夫
#定义N 1000
双精度[N][N]uuu属性(对齐(64));
double _mul1[N][N]___属性(对齐(64)))
={[0…(N-1)]
={[0…(N-1)]=1.1f};
double _mul2[N][N]___属性(对齐(64)))
={[0…(N-1)]
={[0…(N-1)]=2.2f};
#定义SM(CLS/sizeof(双精度))
无效mm(双(*限制res)[N],双(*限制mul1)[N],
双(*restrict mul2)[N])属性(noinline));
无效mm(双(*限制res)[N],双(*限制mul1)[N],
双(*2)[N])
{
inti,i2,j,j2,k,k2;
双*限制RRE;
双*限制rmul1;
双*限制rmul2;
对于(i=0;i
可能是在这里进行的优化不依赖于没有别名的指针?除非在res2中写入结果之前预加载多个mul2元素,否则我看不到任何别名问题

在您展示的第一段代码中,很清楚会出现什么样的别名问题。 这里不太清楚

在重读德雷珀的文章时,他并没有明确表示限制可能解决任何问题。甚至有这样一句话:

{理论上是restrict关键字 在 1999年的修订应该解决这个问题 问题。编译器还没有赶上 然而,主要原因是 存在太多不正确的代码 会误导编译器并导致 无法生成不正确的目标代码。}

在这段代码中,已经在算法中对内存访问进行了优化。剩余优化似乎在附录中的矢量化代码中完成。所以对于这里给出的代码,我想没有什么区别,因为没有进行依赖于限制的优化。每个指针访问都是别名的来源,但并非每个优化都依赖于别名


过早优化是万恶之源,restrict关键字的使用应限于您正在积极研究和优化的情况,而不是在任何可以使用的地方使用。

这是对代码优化器的提示。使用restrict可以确保它可以将指针变量存储在CPU寄存器中,而不必将指针值的更新刷新到内存中,从而也可以更新别名


它是否利用它在很大程度上取决于优化器和CPU的实现细节。代码优化器已经在检测非锯齿方面投入了大量资金,因为这是一项非常重要的优化。在代码中检测到这一点应该没有问题。

您是在32位还是64位Ubuntu上运行的?如果是32位,则需要添加
-march=core2-mfpmath=sse
(或任何处理器架构),否则它不使用sse。其次,为了使用GCC 4.2启用矢量化,您需要添加
-ftree vectorize
选项(从4.3或4.4开始,这作为默认值包含在
-O3
中)。可能还需要添加
-ffast math
(或提供宽松浮点语义的另一个选项),以允许编译器对浮点操作重新排序

另外,添加
-ftree vectorizer verbose=1
选项以查看它是否能够对循环进行矢量化;这是一种检查添加restrict关键字效果的简单方法。

还有GCC4.0.0-
#include <stdlib.h>
#include <stdio.h>
#include <emmintrin.h>

#ifdef USE_RESTRICT
#else
#define restrict
#endif

#define N 1000
double _res[N][N] __attribute__ ((aligned (64)));
double _mul1[N][N] __attribute__ ((aligned (64)))
    = { [0 ... (N-1)] 
    = { [0 ... (N-1)] = 1.1f }};
double _mul2[N][N] __attribute__ ((aligned (64)))
    = { [0 ... (N-1)] 
    = { [0 ... (N-1)] = 2.2f }};

#define SM (CLS / sizeof (double))

void mm(double (* restrict res)[N], double (* restrict mul1)[N], 
        double (* restrict mul2)[N]) __attribute__ ((noinline));

void mm(double (* restrict res)[N], double (* restrict mul1)[N], 
        double (* restrict mul2)[N])
{
 int i, i2, j, j2, k, k2; 
    double *restrict rres; 
    double *restrict rmul1; 
    double *restrict rmul2; 

    for (i = 0; i < N; i += SM)
        for (j = 0; j < N; j += SM)
            for (k = 0; k < N; k += SM)
                for (i2 = 0, rres = &res[i][j],
                    rmul1 = &mul1[i][k]; i2 < SM;
                    ++i2, rres += N, rmul1 += N)
                    for (k2 = 0, rmul2 = &mul2[k][j];
                        k2 < SM; ++k2, rmul2 += N)
                        for (j2 = 0; j2 < SM; ++j2)
                          rres[j2] += rmul1[k2] * rmul2[j2];
}

int main (void)
{

    mm(_res, _mul1, _mul2);

 return 0;
}