【发布时间】:2015-05-19 07:04:33
【问题描述】:
我正在探索 C99 中简单循环的不同实现如何根据函数签名自动矢量化。
这是我的代码:
/* #define PRAGMA_SIMD _Pragma("simd") */
#define PRAGMA_SIMD
#ifdef __INTEL_COMPILER
#define ASSUME_ALIGNED(a) __assume_aligned(a,64)
#else
#define ASSUME_ALIGNED(a)
#endif
#ifndef ARRAY_RESTRICT
#define ARRAY_RESTRICT
#endif
void foo1(double * restrict a, const double * restrict b, const double * restrict c)
{
ASSUME_ALIGNED(a);
ASSUME_ALIGNED(b);
ASSUME_ALIGNED(c);
PRAGMA_SIMD
for (int i = 0; i < 2048; ++i) {
if (c[i] > 0) {
a[i] = b[i];
} else {
a[i] = 0.0;
}
}
}
void foo2(double * restrict a, const double * restrict b, const double * restrict c)
{
ASSUME_ALIGNED(a);
ASSUME_ALIGNED(b);
ASSUME_ALIGNED(c);
PRAGMA_SIMD
for (int i = 0; i < 2048; ++i) {
a[i] = ((c[i] > 0) ? b[i] : 0.0);
}
}
/* Undetermined size version */
void foo3(int n, double * restrict a, const double * restrict b, const double * restrict c)
{
ASSUME_ALIGNED(a);
ASSUME_ALIGNED(b);
ASSUME_ALIGNED(c);
PRAGMA_SIMD
for (int i = 0; i < n; ++i) {
if (c[i] > 0) {
a[i] = b[i];
} else {
a[i] = 0.0;
}
}
}
void foo4(int n, double * restrict a, const double * restrict b, const double * restrict c)
{
ASSUME_ALIGNED(a);
ASSUME_ALIGNED(b);
ASSUME_ALIGNED(c);
PRAGMA_SIMD
for (int i = 0; i < n; ++i) {
a[i] = ((c[i] > 0) ? b[i] : 0.0);
}
}
/* Static array versions */
void foo5(double ARRAY_RESTRICT a[2048], const double ARRAY_RESTRICT b[2048], const double ARRAY_RESTRICT c[2048])
{
ASSUME_ALIGNED(a);
ASSUME_ALIGNED(b);
ASSUME_ALIGNED(c);
PRAGMA_SIMD
for (int i = 0; i < 2048; ++i) {
if (c[i] > 0) {
a[i] = b[i];
} else {
a[i] = 0.0;
}
}
}
void foo6(double ARRAY_RESTRICT a[2048], const double ARRAY_RESTRICT b[2048], const double ARRAY_RESTRICT c[2048])
{
ASSUME_ALIGNED(a);
ASSUME_ALIGNED(b);
ASSUME_ALIGNED(c);
PRAGMA_SIMD
for (int i = 0; i < 2048; ++i) {
a[i] = ((c[i] > 0) ? b[i] : 0.0);
}
}
/* VLA versions */
void foo7(int n, double ARRAY_RESTRICT a[n], const double ARRAY_RESTRICT b[n], const double ARRAY_RESTRICT c[n])
{
ASSUME_ALIGNED(a);
ASSUME_ALIGNED(b);
ASSUME_ALIGNED(c);
PRAGMA_SIMD
for (int i = 0; i < n; ++i) {
if (c[i] > 0) {
a[i] = b[i];
} else {
a[i] = 0.0;
}
}
}
void foo8(int n, double ARRAY_RESTRICT a[n], const double ARRAY_RESTRICT b[n], const double ARRAY_RESTRICT c[n])
{
ASSUME_ALIGNED(a);
ASSUME_ALIGNED(b);
ASSUME_ALIGNED(c);
PRAGMA_SIMD
for (int i = 0; i < n; ++i) {
a[i] = ((c[i] > 0) ? b[i] : 0.0);
}
}
当我编译时
$ icc -O3 -std=c99 -opt-report5 -mavx -S foo.c
icc: remark #10397: optimization reports are generated in *.optrpt files in the output location
我看到 VLA 案例不是自动矢量化的,但是当我添加标志以断言没有别名 -fno-alias 时,它们是。因此,我得出结论,我应该在源代码中规定这个,所以我尝试通过编译来做到这一点
$ icc -O3 -std=c99 -opt-report5 -mavx -DARRAY_RESTRICT=restrict -S foo.c
icc: remark #10397: optimization reports are generated in *.optrpt files in the output location
编译器错误输出包括
foo.c(98): error: "restrict" is not allowed
void foo7(int n, double ARRAY_RESTRICT a[n], const double ARRAY_RESTRICT b[n],
const double ARRAY_RESTRICT c[n])
^
但如您所见,我的 VLA 参数不允许使用限制。
所以我的问题是:有没有办法在 ISO C 中断言 VLA 没有别名?
请注意,我可以使用编译指示在源代码中断言没有别名 - 例如simd、omp simd、ivdep 等 - 并获得我想要的自动矢量化,但这些不是 ISO C。
在这种情况下,ISO C 是指 C 的最新版本,在撰写本文时当然是 C11。
【问题讨论】:
-
foo5()中的数组并不是真正的 VLA。 -
foo[5678] 和 foo[78] 出现语法错误有 VLA 参数。虽然 foo[56] 不使用 VLA,但同样的问题也适用于限制限定符的使用。
-
ISO/IEC 9899:2011 第 6.7.3.1 节 restrict 的正式定义 是否有帮助:让
D成为一个普通标识符的声明,它提供了一种方法将对象P指定为指向T类型的限制限定指针。 数组a、b和c是否满足该定义。我承认我不确定,但我认为不是。 -
另外,第 6.7.6.3 节有示例 5,它表示以下函数原型声明符是等效的:
void f(double (* restrict a)[5]);、void f(double a[restrict][5]);、void f(double a[restrict 3][5]);和void f(double a[restrict static 3][5]);。您可能需要从那里进行一些追逐...... -
这是标准中唯一出现
restrict与数组类型直接关联的地方。 §6.7.6 一般用于声明符,§6.7.6.2 用于数组声明符,在我看来,restrict必须出现在数组维度的第一个组件内。在您的上下文中,它应该是:void foo7(int n, double a[ARRAY_RESTRICT n], const double b[ARRAY_RESTRICT n], const double c[ARRAY_RESTRICT n]),我相信(但如果没有看到标准中的示例并且您提出问题,我不会相信!)。另请注意,这适用于数组以及 VLA。
标签: c99 simd variable-length-array restrict-qualifier auto-vectorization