3

Я изучаю, как различные реализации простых циклов в C99 авто-векторизации основаны на сигнатуре функции.с использованием ограничителя ограничений с массивами переменной длины C99 (VLA)

Вот мой код:

/* #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.

Итак, мой вопрос: нет ли способа утверждать отсутствие наложения VLA в ISO C?

Обратите внимание, что я не могу утверждать никакого сглаживания в исходном коде с использованием прагм. simd, omp simd, ivdep и т.д. - и получить автоматической векторизации, что я хочу, но это не ISO C.

В этом контексте ISO C означает, что самая последняя версия C, что, конечно, C11 от написания этого сообщения.

+0

Массивы в 'foo5()' на самом деле не VLA. –

+0

Синтаксическая ошибка появляется для foo [5678], а foo [78] - аргументы VLA. Хотя foo [56] не использует VLA, тот же вопрос применяется в отношении использования ограничителя. – Jeff

+0

Соответствует ли ИСО/МЭК 9899: 2011 Раздел 6.7.3.1 ** Формальное определение ограничения ** help: _Let 'D' является объявлением обычного идентификатора, который предоставляет средство назначения объекта« P » в качестве ограничения, квалифицированный указатель на тип 'T'._ Соответствуют ли эти массивы' a', 'b' и' c'. Признаюсь, я не уверен, но я думаю, что нет. –

ответ

3

Ваш оригинальный код не хорошо для меня с сообщениями, таких как:

void foo7(int n, double ARRAY_RESTRICT a[n], const double ARRAY_RESTRICT b[n], const double ARRAY_RESTRICT c[n]) 
^ 
restrict.c:126:1: error: invalid use of ‘restrict’ 
restrict.c:126:1: error: invalid use of ‘restrict’ 
restrict.c:145:1: error: invalid use of ‘restrict’ 

Передача отдельных частей комментариев

§6.7.6.3 функции declarators (включая прототипы) имеет Пример 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]); 

Это единственное место в стандарте, где ограничение появляется, связанное непосредственно с типами массивов. §6.7.6 относится к деклараторам в целом, а §6.7.6.2 - к объявлениям массива, и он выглядит так, как будто ограничение должно появляться внутри первого компонента измерения массива. В вашем контексте, оно должно быть:

void foo7(int n, double a[ARRAY_RESTRICT n], 
      const double b[ARRAY_RESTRICT n], 
      const double c[ARRAY_RESTRICT n]) 

я не поверил бы, что обозначения, не видя примеры в стандарте и вы задаете вопрос! Обратите внимание, что это относится к массивам, а также к VLA.

Этот пересмотренный кодекс, основанный на комментариях, компилируется под ту же опциями компиляции:

gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \ 
    -Wold-style-definition -Wold-style-declaration -Werror -c new.restrict.c 

Варианта компиляции требует предварительных деклараций нестатических функций, следовательно, заявления в верхней части файла. Я также вынудил #define ARRAY_RESTRICT restrict в исходном коде вместо того, чтобы оставить его как вариант компиляции.

Компилятор GCC 4.9.2 работает на производном Ubuntu 14.04.

Файл new.restrict.c:

/* #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 

#define ARRAY_RESTRICT restrict 

#ifndef ARRAY_RESTRICT 
#define ARRAY_RESTRICT 
#endif 

void foo1(double *restrict a, const double *restrict b, const double *restrict c); 
void foo2(double *restrict a, const double *restrict b, const double *restrict c); 
void foo3(int n, double *restrict a, const double *restrict b, const double *restrict c); 
void foo4(int n, double *restrict a, const double *restrict b, const double *restrict c); 
void foo5(double a[ARRAY_RESTRICT 2048], const double b[ARRAY_RESTRICT 2048], const double c[ARRAY_RESTRICT 2048]); 
void foo6(double a[ARRAY_RESTRICT 2048], const double b[ARRAY_RESTRICT 2048], const double c[ARRAY_RESTRICT 2048]); 
void foo7(int n, double a[ARRAY_RESTRICT n], const double b[ARRAY_RESTRICT n], const double c[ARRAY_RESTRICT n]); 
void foo8(int n, double a[ARRAY_RESTRICT n], const double b[ARRAY_RESTRICT n], const double c[ARRAY_RESTRICT n]); 

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 a[ARRAY_RESTRICT 2048], const double b[ARRAY_RESTRICT 2048], const double c[ARRAY_RESTRICT 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 a[ARRAY_RESTRICT 2048], const double b[ARRAY_RESTRICT 2048], const double c[ARRAY_RESTRICT 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 a[ARRAY_RESTRICT n], const double b[ARRAY_RESTRICT n], const double c[ARRAY_RESTRICT 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 a[ARRAY_RESTRICT n], const double b[ARRAY_RESTRICT n], const double c[ARRAY_RESTRICT 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); 
    } 
} 
+0

Я не смотрел на соображения обоснования или заседания комитета, чтобы узнать, почему нотация должна быть такой, какая есть. –

+0

У вас есть другое предложение? 'double QUALIFIER a [n]' будет означать, что QUALIFIER квалифицирует 'double', а не' a'. Как в примерах 'const' в этом коде. –

+0

@MattMcNabb: Я не уверен, что вы ищете. Вы предлагаете, чтобы это предлагаемое решение не «ограничивало» массивы, чтобы они не перекрывали память? Или вы ищете информацию для других классификаторов типов? Определение _direct declarator_ (в §6.7.6) включает варианты, такие как _direct-declarator '[' type-qualifier-list opt ​​assign-expression opt ']' _ и _direct-declarator '[' static type-qualifier-list opt присваивание-выражение ']' _ и _direct-declarator '[' статический присваивание-выражение типа-классификатора-list ']' _ ... –

3

Ни один из параметров в этом коде нет переменно-модифицированный тип. foo6 и foo7 - это точно такая же подпись функции, за исключением int n. См. Why do C and C++ compilers allow array lengths in function signatures when they're never enforced?.

Это все точно так же:

void foo8(int n, T *a); 
void foo8(int n, T a[16]); 
void foo8(int n, T a[n]); 

версия void foo8(int n, T a[]); почти то же самое, но он имеет угловую дело, что не допускается, если T неполный тип.

foo8 может быть вызван либо VLA, либо не-VLA.

Хотя описатель массив переменно-модифицированный тип, то массив из-Т к указатель на T регулировка осуществляется до того, как тип параметра отсчитывается. Таким образом, T a[n] доводится до T *a, который не изменяется с изменением; однако void foo9(int n, T a[][n]); дает модифицированный тип T (*)[n] для a.


Самый простой способ объединения restrict с описателя массива является на самом деле использовать форму указателя, здесь:

void foo8(int n, T *restrict a) { 

Попытка void foo8(int n, T restrict a[]); не работает, потому что это эквивалентно void foo8(int n, T restrict *a);. restrict является квалификатором, и он ведет себя синтаксически так же, как и другие квалификаторы, такие как const.

Как отметил Джонатан Леффлера, существует альтернативный синтаксис:

void foo8(int n, T a[restrict]) { // n is optional , as before 

В этом случае представляется излишним, чтобы то же самое должно быть указано в двух различных способов, однако there also exists static, которые могут быть использованы только с объявление объявления массива (не декларатор указателя). Если вы хотите использовать эту форму static, а также restrict то нет выбора, кроме как иметь restrict внутри квадратных скобок:

void foo8(int n, T a[restrict static n]) { 

Чтобы быть ясно, и этот последний случай еще не переменно-модифицированного типа; static является обещанием, что a, , который является указателем, указывает на первый элемент массива не менее n элементов.

Также не нужно проверять static во время компиляции, когда вызывается функция (хотя, конечно, было бы неплохо, если бы компилятор действительно применял).

Последнее примечание: restrict в прототипе probably has no effect, это имеет значение только для определения функции.

+0

Интересный x-ref о том, имеет ли 'ограничение' в прототипе какой-либо эффект. Компилятор не может принудительно применять ограничение, если он не знает, что ему нужно, поэтому наличие 'ограничения' в прототипе позволяет компилятору вывести, что' foo7 (3, & a [0], & a [1], & a [2]) '- использование функции из вопроса - это вызов, который нарушает критерий' restrict'. Независимо от того, действительно ли какой-либо компилятор делает это, это отдельное обсуждение, но если в прототипе исключается 'ограничение', такие ошибки могут быть представлены только в TU, где определена функция, а затем только в вызовах после определения. –

+0

@JonathanLeffler Да, мой другой вопрос попытался провести обсуждение в этом направлении. Ваш вывод имеет смысл. –

+0

Вы говорите, что компилятор * не разрешен * использовать информацию, содержащуюся в «a [16]», которая не содержится в «a []», когда дело касается автоматической векторизации? Моя цель - рассказать компилятору как можно больше, и я считаю, что я говорю больше, когда я использую «a [n]» vs «* a», например. – Jeff

Смежные вопросы