Слишком сложный индекс отчета ICC opt-report

Когда я компилирую данный файл с параметрами -opt-report или -vec-report в ICC, я получаю, среди прочего, это сообщение:

foo.c(226:7-226:7):VEC:function_foo:  loop was not vectorized: subscript too complex
foo.c(226): (col. 7) warning #13379: loop was not vectorized with "simd"
vectorization support: call to function absorbing_apply cannot be vectorized
loop was not vectorized: not inner loop
loop was not vectorized: unsupported loop structure
loop was not vectorized: subscript too complex

Я знаю значение этих сообщений. Что меня беспокоит, так это то, что в foo.c:226 вообще нет никакого цикла. На самом деле то, что есть, это вызов другой функции. Эта функция содержит несколько циклов, которые работают через объем и действительно корректно векторизуются, как сообщает icc. Однако все вызовы этой функции дают те же сообщения, что и те, которые я вставил.

Не попадает ли icc в беспорядок, поскольку он показывает сообщения о векторизации в местах, где вообще нет циклов? Или это я что-то не так понял?

РЕДАКТИРОВАТЬ: я частично воспроизвел проблему. На этот раз компилятор сообщает, что векторизовал строку кода, где есть вызов другой функции (в исходном случае как раз наоборот, говорит, что не может). Вот код:

 1 
 2 
 3 void foo(float *a, float *b, float *c, int n1, int n2, int n3, int ini3, int end3 ) {
 4    int i, j, k;
 5 
 6    for( i = ini3; i < end3; i++ ) {
 7       for( j = 0; j < n2; j++ ) {
 8        #pragma simd
 9        #pragma ivdep
10        for( k = 0; k < 4; k ++ ) {
11          int index = k + j*n1 + i*n1*n2;
12          a[index] = b[index] + 2* c[index];
13        }
14      }
15    }
16 
17    for( i = ini3; i < end3; i++ ) {
18       for( j = 0; j < n2; j++ ) {
19        #pragma simd
20        #pragma ivdep
21        for( k = n1-4; k < n1; k ++ ) {
22          int index = k + j*n1 + i*n1*n2;
23          a[index] = b[index] + 2* c[index];
24        }
25      }
26    }
27 
28   return;
29 }
30 int main(void){
31    int n1, n2, n3;
32    int ini3 = 20;
33    int end3 = 30;
34    n1 = n2 = n3 = 200;
35 
36    float *a = malloc( n1 * n2 * n3 * sizeof(float ));
37    float *b = malloc( n1 * n2 * n3 * sizeof(float ));
38    float *c = malloc( n1 * n2 * n3 * sizeof(float ));
39 
40    foo( a,b,c, n1, n2, n3, ini3, end3 );
41 
42    ini3 += 50;
43    end3 += 50;
44 
45    foo( a,b,c, n1, n2, n3, ini3, end3 );
46 
47    free(a); free(b); free(c);
48 
49   return 0;
50 }
51 

И кусок отчета об оптимизации, где ICC говорит, что он векторизовал строки 40 и 45:

foo.c(40:4-40:4):VEC:main:  LOOP WAS VECTORIZED
loop was not vectorized: not inner loop
loop was not vectorized: not inner loop
LOOP WAS VECTORIZED
loop was not vectorized: not inner loop
loop was not vectorized: not inner loop
foo.c(45:4-45:4):VEC:main:  LOOP WAS VECTORIZED
loop was not vectorized: not inner loop
loop was not vectorized: not inner loop

Это нормально?


person Genís    schedule 28.11.2012    source источник
comment
Боюсь, я не могу, извините. Я посмотрю, смогу ли я воспроизвести проблему на простом примере.   -  person Genís    schedule 28.11.2012


Ответы (2)


В опубликованном вами примере вызов функции foo() встроен. Циклы внутри foo() векторизуются после его встраивания.

В результате весь этот код «схлопывается» в строки 40 и 45. К тому моменту, когда векторизатор касается кода, он еще не знает, что он изначально был из другой функции.

В исходном примере, где вы говорите, что он не векторизован, применяется та же ситуация. Вызов функции встроен, но содержит не векторизуемые циклы.


Возможно, ICC могла бы сохранить информацию о линии через вызов функции. Но тогда вы будете получать дубликат отчета о векторизации каждый раз, когда функция встраивается. Кроме того, все они будут указывать на одну и ту же линию. Возможно, это было бы еще более запутанным.

person Mysticial    schedule 28.11.2012
comment
Я думаю ты прав. Я проверил это, и компилятор встраивает функцию. По-прежнему существует проблема, заключающаяся в том, что в исходном коде компилятор жалуется на невозможность векторизации при встраивании, но возможность при автономной компиляции метода. - person Genís; 28.11.2012
comment
Когда что-то встраивается, оно становится частью кода вызывающего объекта. Таким образом, окружающий код часто будет мешать оптимизации. Это может привести к различиям в любом направлении. - person Mysticial; 28.11.2012

Простой совет по кодированию — не используйте такие конструкции, как:

for( i = ini3; i < end3; i++ ) {
   for( j = 0; j < n2; j++ ) {
   #pragma simd
   #pragma ivdep
   for( k = 0; k < 4; k ++ ) {
     int index = k + j*n1 + i*n1*n2;
     a[index] = b[index] + 2* c[index];
   }
   }
}

Для вас может быть очевидно, что index увеличивается с шагом 1 на каждой итерации самого внутреннего цикла, но векторизатору компилятора может быть трудно понять это. Лучше использовать:

for( i = ini3; i < end3; i++ ) {
   for( j = 0; j < n2; j++ ) {
      int base = (j + i*n2)*n1;
      for( k = base; k < base+4; k++ ) {
         a[k] = b[k] + 2* c[k];
      }
   }
}

Теперь понятно, что происходит, и даже самый тупой векторизатор сможет это понять (и код выглядит несколько чище). Вместо использования #pragma ivdep лучше заверить компилятор, что a, b и c указывают на непересекающиеся области памяти, используя квалификатор C99 restrict:

void foo(float *restrict a, float *restrict b, float *restrict c,
         int n1, int n2, int n3, int ini3, int end3) {
person Hristo Iliev    schedule 01.12.2012
comment
Да, я обычно это учитываю, так как понял, что у компилятора есть некоторые трудности с априорно "простыми" выражениями. Однако спасибо! - person Genís; 03.12.2012