2013-02-13 3 views
6

Я работаю над тем, чтобы мой код мог быть автоматически прорисован GCC, однако, когда я включаю флаг -fopenmp, он, кажется, останавливает все попытки автоматической векторизации. Я использую ftree-vectorize -ftree-vectorizer-verbose=5 для векторизации и мониторинга.Использование OpenMP останавливается GCC auto vectorising

Если я не включаю флаг, он начинает давать мне много информации о каждом цикле, если он векторизован и почему нет. Компилятор останавливается, когда я пытаюсь использовать функцию omp_get_wtime(), так как она не может быть связана. Как только флаг включен, он просто перечисляет каждую функцию и сообщает мне, что он векторизовал в нем 0 циклов.

Я прочитал несколько других мест, о которых упоминалось выше, но на самом деле они не пришли к каким-либо решениям: http://software.intel.com/en-us/forums/topic/295858http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46032. Имеет ли OpenMP собственный способ обработки векторизации? Нужно ли мне явно говорить об этом?

+0

Я думаю, что вы можете найти разумную информацию в [ответе] (http://stackoverflow.com/a/14717689/771663) на этот вопрос. – Massimiliano

+0

Спасибо, что описывает, как использовать SIMD с OpenMP, но, похоже, не объясняет, почему уже работающая реализация SIMD перестает работать, когда я использую OpenMP. Разве нет возможности использовать их? – superbriggs

+1

Это также означает, что я могу работать только на одном и том же количестве бит, они просто разделяются между числами. Выполняя это с GCC, меня не спрашивали, сколько я хотел разделить на регистр. Поскольку я использую университетский суперкомпьютер, я предположил, что аппаратные га лишние пространства для SIMD. Как я узнаю, что это правильно? – superbriggs

ответ

9

Существует недостаток в векторе-векторе GCC, который, как представляется, был устранен в последних версиях GCC. В моем тестовом случае GCC 4.7.2 vectorises успешно следующего простого цикла:

В то же время GCC 4.6.1 не делает и он жалуется, что цикл содержит вызовы функций или ссылки на данные, которые не могут быть проанализированы. Ошибка в векторизаторе запускается путем parallel for циклов, реализуемых GCC.Когда OpenMP конструкции обрабатываются и расширены, простой код цикла превращается в нечто похожее на это:

struct omp_fn_0_s 
{ 
    int N; 
    double *a; 
    double *b; 
    double *c; 
    double d; 
}; 

void omp_fn_0(struct omp_fn_0_s *data) 
{ 
    int start, end; 
    int nthreads = omp_get_num_threads(); 
    int threadid = omp_get_thread_num(); 

    // This is just to illustrate the case - GCC uses a bit different formulas 
    start = (data->N * threadid)/nthreads; 
    end = (data->N * (threadid+1))/nthreads; 

    for (int i = start; i < end; i++) 
     data->a[i] = data->b[i] + data->c[i] * data->d; 
} 

... 

struct omp_fn_0_s omp_data_o; 

omp_data_o.N = N; 
omp_data_o.a = a; 
omp_data_o.b = b; 
omp_data_o.c = c; 
omp_data_o.d = d; 

GOMP_parallel_start(omp_fn_0, &omp_data_o, 0); 
omp_fn_0(&omp_data_o); 
GOMP_parallel_end(); 

N = omp_data_o.N; 
a = omp_data_o.a; 
b = omp_data_o.b; 
c = omp_data_o.c; 
d = omp_data_o.d; 

в НКУ векторных операций, прежде чем 4.7 не удается vectorise, что цикл. Это НЕ проблема с OpenMP. Его можно легко воспроизвести без кода OpenMP. Для подтверждения этого я написал следующий простой тест:

struct fun_s 
{ 
    double *restrict a; 
    double *restrict b; 
    double *restrict c; 
    double d; 
    int n; 
}; 

void fun1(double *restrict a, 
      double *restrict b, 
      double *restrict c, 
      double d, 
      int n) 
{ 
    int i; 
    for (i = 0; i < n; i++) 
     a[i] = b[i] + c[i] * d; 
} 

void fun2(struct fun_s *par) 
{ 
    int i; 
    for (i = 0; i < par->n; i++) 
     par->a[i] = par->b[i] + par->c[i] * par->d; 
} 

Можно было бы ожидать, что оба кода (уведомление - не OpenMP здесь!) Должны vectorise одинаково хорошо из-за restrict ключевых слов, используемых для указания того, что нет сглаживания не может произойти. К сожалению, это не относится к GCC < 4.7 - он успешно векторизовывает цикл в fun1, но не позволяет его прорисовать в fun2, ссылаясь на ту же причину, что и при компиляции кода OpenMP.

Причина этого заключается в том, что не может векторные операции, чтобы доказать, что par->d не лежит в пределах памяти, par->a, par->b и par->c пункта. Это не всегда так с fun1, где возможны два случая:

  • d передается в качестве аргумента значение в регистре;
  • d передается как аргумент значения в стеке.

В системах x64 система V ABI предусматривает передачу первых нескольких аргументов с плавающей запятой в регистры XMM (YMM на процессорах с поддержкой AVX). Вот как передается d в этом случае, и, следовательно, ни один указатель не может указывать на него - цикл становится векторизованным. В системах x86 ABI указывает, что аргументы передаются в стек, поэтому d может быть сглажен любым из трех указателей. Действительно, GCC отказывается векторизовать цикл в fun1, если ему поручено генерировать 32-разрядный код x86 с опцией -m32.

GCC 4.7 оборачивается этим, вставляя проверки во время выполнения, которые гарантируют, что ни d, ни par->d не получат псевдонимы.

Избавление от d удаляет недоказуемое не-ступенчатость и следующий код OpenMP получает vectorised от GCC 4.6.1:

#pragma omp parallel for schedule(static) 
for (int i = 0; i < N; i++) 
    a[i] = b[i] + c[i]; 
+0

Отличный ответ. Но не могли бы вы сказать больше о «Это просто для иллюстрации дела - GCC использует несколько разные формулы». Какую формулу использует GCC? –

+0

@Zboson, я мог бы вставить его здесь (уродливый), но вы скорее запускаете 'gcc -fdump-tree-all -fopenmp foo.c' и исследуете для себя AST после расширения OpenMP, обычно находящегося в' foo. c.015t.ompexp'. Разница в том, что GCC распределяет остаток от деления 'r = N% num_threads', предоставляя одну дополнительную итерацию первым потокам r. –

3

Я попытаюсь кратко ответить на ваш вопрос.

  1. Имеет ли OpenMP собственный способ обработки векторизации?

Да ... но начиная с входящего OpenMP 4.0. Приведенный выше link дает хорошее представление об этой конструкции. Нынешний OpenMP 3.1, с другой стороны, не «осознает» концепцию SIMD. Таким образом, на практике (или, по крайней мере, в моем опыте) происходит то, что механизмы автоматической векторизации запрещаются всякий раз, когда в цикле используется конструкция обхода OpenMP. Во всяком случае две концепции ортогональны, и вы все равно можете воспользоваться обоими (см. Этот другой answer).

  1. Нужно ли прямо указывать это?

Я боюсь, что да, по крайней мере, в настоящее время. Я бы начал переписывать рассматриваемые циклы таким образом, чтобы сделать векторизацию явной (т. Е. Я буду использовать встроенные средства на платформе Intel, Altivec в IBM и т. Д.).

+0

Большое спасибо. Ваша первая ссылка дает функцию 'VECTOR_ADD'. Я прочитал, что для этого используется один регистр нормального размера, поэтому разрешается только векторизация небольших чисел. Я знаю, что у моего оборудования есть определенные регистры для обработки SIMD, чтобы этого не произошло. Есть ли способ заставить OpenMP использовать этот регистр? Нужно ли использовать эти функции, учитывая, что до того, как GCC все это сделал для меня? Я не понимаю, почему OpenMP останавливает эту форму. В вашей второй ссылке говорится, что они могут работать вместе, но не так, как я бы это понял. Еще раз спасибо. – superbriggs

+0

Основная идея заключается в том, что OpenMP не должен знать SIMDization, потому что вы позаботитесь об этом в VECTOR_ADD. Я никогда не использовал 3Dnow, но на платформах Intel вы можете использовать [intrinsics] (http://software.intel.com/en-us/articles/how-to-use-intrinsics) для явного векторизации кода. Главный недостаток заключается в том, что либо вы теряете переносимость (поскольку внутренняя среда не будет работать на других платформах), либо читаемость/ремонтопригодность (из-за условной компиляции). – Massimiliano

+0

Для этого проекта ремонтопригодность и переносимость не важны. В настоящее время я не использую VECTOR_ADD, я просто помещаю его в цикл таким образом, чтобы GCC мог видеть, что происходит, и автоматически векторизовать его. – superbriggs

1

Вы спрашиваете: «Почему GCC не может выполнять векторизации при включенном OpenMP?».

кажется, что это может быть ошибка в GCC :) http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46032

В противном случае OpenMP API может ввести зависимость (либо контроль или данные), предотвращающую автоматической векторизации. Для авто-vertorize, данный код должен быть свободен от данных/контроля. Возможно, использование OpenMP может вызвать ложную зависимость.

Примечание. OpenMP (до 4.0) должен использовать параллелизм уровня нити, который ортогонален SIMD/векторизации. Программа может одновременно использовать как OpenMP, так и SIMD-параллелизм.

1

я натыкался на этот пост в поисках комментариев по поводу GCC 4.9 варианта openmp- simd, который должен активировать OpenMP 4 #pragma omp simd без активации omp parallel (threading). gcc bugzilla pr60117 (подтверждено) показывает случай, когда pragma omp предотвращает автоинтеграцию, которая произошла без прагмы.

gcc не выделяет omp параллельно даже для предложения simd (параллельные области могут автоматически векторизовать только внутренний контур, вложенный под параллель для). Я не знаю компилятора, кроме icc 14.0.2, который можно было бы рекомендовать для реализации #pragma omp parallel для simd; с другими компиляторами, для получения этого эффекта потребуется кодирование внутренней аутентификации SSE.

В моих тестах компилятор Microsoft не выполняет автоматической векторизации внутри параллельных областей, что демонстрирует явное превосходство gcc для таких случаев.

Комбинированное распараллеливание и векторизация одного цикла имеет несколько трудностей, даже при наилучшей реализации. Я редко вижу больше, чем 2x или 3x ускорение, добавляя векторию в параллельный цикл.Например, векторизация с двойным типом данных AVX эффективно сокращает размер куска в 4 раза. Типичная реализация может достигать выровненных блоков данных только для случая, когда весь массив выровнен, а куски также являются точными кратными ширине вектора , Когда куски не все выровнены, существует неизбежный дисбаланс работы из-за различных выравниваний.

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