2014-11-15 9 views
7

Я потратил последние несколько дней на чтение autovectorization с помощью gcc 4.7. Я следил за некоторыми примерами, которые я видел в Интернете, и настройка кажется правильной. Но когда я на самом деле запускаю код и сравниваю его с вложением или отключением, во время выполнения нет заметной разницы.Автоинъекция GCC не влияет на время работы, даже когда предположительно «выгодно»

Вот код, я работаю с:

#include <string.h> 
#include <stdlib.h> 
#include <emmintrin.h> 
#include <stdio.h> 
#include <math.h> 

int main(int argc, char** argv) { 

    long b = strtol(argv[2], NULL, 0); 
    unsigned long long int i; 
    unsigned long long int n = (int)pow(2,29);                                                
    float total = 0; 

    float *__restrict__ x1; 
    float *__restrict__ y1; 

    posix_memalign((void *)&x1, 16, sizeof(float)*n); 
    posix_memalign((void *)&y1, 16, sizeof(float)*n); 


    float *__restrict__ x = __builtin_assume_aligned(x1,16); 
    float *__restrict__ y = __builtin_assume_aligned(y1,16); 

    for (i=0;i<n;i++) { 
      x[i] = i; 
      y[i] = i; 
    } 

    for (i=0; i<n; i++) { 
      y[i] += x[i]; 
    } 

    printf("y[%li]: \t\t\t\t%f\n", b,y[b]); 
    printf("correct answer: \t\t\t%f\n", (b)*2); 
    return 0; 
} 

Некоторые из этих вещей кажется излишним для меня, но было необходимо, чтобы компилятор, чтобы понять, что происходит (особенно тот факт, что данные были выровнены). Переменная «b», которая считывается из командной строки, существует только потому, что я был параноидальным относительно компилятора, полностью оптимизирующего цикл.

Вот команда компилятор, когда vectorizeration включена:

gcc47 -ftree-vectorizer-verbose=3 -msse2 -lm -O2 -finline-functions -funswitch-loops -fpredictive-commoning -fgcse-after-reload -fipa-cp-clone test.c -ftree-vectorize -o v 

В основном, это эквивалентно просто используя -O3. Я помещал флаги в себя, чтобы все, что мне нужно было сделать, это удалить «ftree-vectorize» и уметь проверять результат без векторизации.

Вот вывод флага ftree-векторизации-многословная, чтобы показать, что код на самом деле векторизуемый:

Analyzing loop at test.c:29 

29: vect_model_load_cost: aligned. 
29: vect_model_load_cost: inside_cost = 1, outside_cost = 0 . 
29: vect_model_load_cost: aligned. 
29: vect_model_load_cost: inside_cost = 1, outside_cost = 0 . 
29: vect_model_simple_cost: inside_cost = 1, outside_cost = 0 . 
29: vect_model_store_cost: aligned. 
29: vect_model_store_cost: inside_cost = 1, outside_cost = 0 . 
29: cost model: Adding cost of checks for loop versioning aliasing. 

29: Cost model analysis: 
    Vector inside of loop cost: 4 
    Vector outside of loop cost: 4 
    Scalar iteration cost: 4 
    Scalar outside cost: 1 
    prologue iterations: 0 
    epilogue iterations: 0 
    Calculated minimum iters for profitability: 2 

29: Profitability threshold = 3 


Vectorizing loop at test.c:29 

29: Profitability threshold is 3 loop iterations. 
29: created 1 versioning for alias checks. 

29: LOOP VECTORIZED. 
Analyzing loop at test.c:24 

24: vect_model_induction_cost: inside_cost = 2, outside_cost = 2 . 
24: vect_model_simple_cost: inside_cost = 2, outside_cost = 0 . 
24: not vectorized: relevant stmt not supported: D.5806_18 = (float) D.5823_58; 

test.c:7: note: vectorized 1 loops in function. 

Обратите внимание, что векторизация выгодно после 3 итераций, и я бегу с 2^29 ~ = 500 000 000 итераций. Поэтому я должен ожидать, что значительно отличается время исполнения, когда векторизация отключена, верно?

Ну, вот время автономной работы кода (я побежал его 20 раз подряд):

59.082s                                                          
79.385s 
57.557s 
57.264s 
53.588s 
54.300s 
53.645s 
69.044s 
57.238s 
59.366s 
56.314s 
55.224s 
57.308s 
57.682s 
56.083s 
369.590s 
59.963s 
55.683s 
54.979s 
62.309s 

Отбросив что странно ~ 370С останец, что дает среднее время выполнения 58.7s, с стандартное отклонение 6.0s.

Далее я буду компилировать с той же командой, как и раньше, но без -ftree-векторизации флаг:

gcc47 -ftree-vectorizer-verbose=3 -msse2 -lm -O2 -finline-functions -funswitch-loops -fpredictive-commoning -fgcse-after-reload -fipa-cp-clone test.c -o nov 

Опять же, запустившего программу 20 раз подряд дает следующие времена:

69.471s                                                          
57.134s 
56.240s 
57.040s 
55.787s 
56.530s 
60.010s 
60.187s 
324.227s 
56.377s 
55.337s 
54.110s 
56.164s 
59.919s 
493.468s 
63.876s 
57.389s 
55.553s 
54.908s 
56.828s 

Снова выбросив выбросы, это дает среднее время работы 57,9 с стандартным отклонением 3,6 с.

Таким образом, эти две версии имеют статистически неразличимые времена автономной работы.

Может ли кто-нибудь указать мне на то, что я делаю неправильно? Не порождает ли «порог прибыльности» компилятор, что я думаю, что это значит? Я очень ценю любую помощь, которую люди могут мне дать, я пытался понять это на прошлой неделе.

EDIT:

Я осуществил изменения, которые @nilspipenbrinck предложенных, и это, кажется, работает. Я вставил векторный цикл в функцию и назвал эту функцию лодкой раз. Относительное время выполнения составляет 24,0 с (сигма < 0,1 с) без векторизации против 20,8 с (сигма < 0,2 с) для векторизации или 13% -ное улучшение скорости. Не так много, как я надеялся, но по крайней мере теперь я знаю его работу!Спасибо, что нашли время, чтобы посмотреть на мой вопрос и написать ответ, я очень ценю это.

+0

Есть ли какая-либо особая причина для тестирования устаревшей версии компилятора? Какое у вас оборудование? – Drop

+1

Локализация ссылок - это большая проблема для современных процессоров. Ваша программа не имеет, ее время выполнения полностью зависит от стоимости доступа к ОЗУ. –

+0

@Drop Я планирую получить эту работу на кучу разных компьютеров в моей лаборатории, и все они используют этот компилятор. Фактически, версия, которую они используют, старше этого, gcc4.4 или ниже. Но некоторые из функций, которые я хотел поэкспериментировать, не возникали до 4.7. У меня нет возможности обновлять компиляторы, или я бы это сделал. – user2635263

ответ

4

Вы не делаете много арифметики. Поэтому время выполнения вашего тестового кода ограничено памятью. Например. вы проводите большую часть времени, перемещая данные между ЦП и памятью.

Кроме того, ваш n очень большой с 2 ​​^ 29 элементами. Поэтому вы не пользуетесь кешем первого и второго уровней.

Если вы хотите увидеть улучшения в SSE, используйте меньший n, чтобы вы касались только 8 или 16 килобайт данных. Также убедитесь, что данные «горячие», например, к нему недавно был обращен процессор. Таким образом, данные не должны перемещаться из основной памяти, но они быстрее перемещаются из кешей, что на несколько величин быстрее.

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

Подведено: Если арифметика быстрее, чем ваша система может перемещать память вокруг вас, вы не увидите никаких преимуществ. Время доступа к памяти будет узким местом, и несколько циклов, которые вы сохраняете с помощью набора инструкций SSE, будут потеряны в шуме тайминга доступа к памяти.

+0

А, это имеет большой смысл. Работая на 8 килобайтах данных, на самом деле не будет измеримого времени выполнения, но я мог бы вставить этот векторизованный цикл в функцию, а затем вызвать эту функцию миллион раз. Спасибо за совет, я собираюсь попробовать это прямо сейчас. – user2635263

+1

@ user2635263 Время выполнения можно измерить. Просто используйте счетчик циклов, который вы можете прочитать с помощью инструкции RDTSC. Google, и вы найдете базовые рамки. –

+0

Хотя правильно сказать, что это ограничение пропускной способности памяти, что не означает, что он не может быть значительно улучшен.Если ОП использует несколько потоков и невременных хранилищ, он, вероятно, по порядку улучшится в два раза. –

2

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

Время работы, которое вы отправляете, предназначено для всего кода, а не для одного цикла, так что для всего времени работы будет только столько векторизации. Если вы действительно хотите увидеть, сколько улучшений существует из векторизации, я бы предложил запустить профилировщик, такой как AMD Code XL, Intel Vtune, OProfile и т. Д., Он расскажет вам конкретно об этом цикле, сколько улучшения с точки зрения времени и производительности вы делают.

В настоящее время я работаю в оценке векторизации компиляторов, и я бы код работает в 60 раз быстрее с векторизации, в других случаях ускорение не так впечатляет, и все зависит от цикла, компилятора и архитектуру, которую вы используете.

+0

Понял. Спасибо за ответ. Я много узнал о векторизации кода, так как размещаю это! – user2635263