我试图了解何时以及何时不使用 restrict
C中关键字,以及在什么情况下它提供了明显的好处。
阅读了“ Demystifying The Restrict Keyword ”(提供了一些使用经验的规则)后,我得到的印象是,当函数传递指针时,它必须考虑所指向的数据可能重叠的可能性(别名)并将任何其他参数传递到函数中。给定一个功能:
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];
}
}
编译器必须c
在第二个表达式中重新加载,因为也许b
并c
指向同一位置。由于相同的原因,它还必须等待b
存储后才能加载a
。然后,它必须等待a
存储和必须重新加载b
,并c
在下一循环的开始。如果您像这样调用函数:
int a[N];
foo(a, a, a, N);
然后您会看到编译器为什么必须这样做。restrict
有效使用会告诉编译器您永远都不会这样做,因此它可以删除存储的冗余负载c
和a
之前的负载b
。
在另一篇SO文章中,Nils Pipenbrinck提供了此方案的一个有效示例,展示了性能优势。
到目前为止,我已经收集到,使用它是一个好主意 restrict
传递给不会内联的函数的指针。显然,如果代码是内联的,则编译器可以确定指针没有重叠。
现在,这里对我来说开始变得模糊。
在Ulrich Drepper的论文中,“每个程序员都应该了解内存的知识”,他说:“除非使用限制,否则所有指针访问都是潜在的别名来源”,并且他给出了子矩阵矩阵乘以的具体代码示例,其中用途restrict
。
但是,无论有没有编译我的示例代码restrict
,两种情况下我都得到相同的二进制文件。我正在使用gcc version 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_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;
}