Межпроцедурные оптимизации

28
Межпроцедурные оптимизации

description

Межпроцедурные оптимизации. Межпроцедурный анализ. Как совместить хороший стиль программирования и требования к быстродействию приложения? Модульность. Читаемость кода и использование подпрограмм для повторяющихся вычислений. Принцип реализации утилит как «черного ящика». - PowerPoint PPT Presentation

Transcript of Межпроцедурные оптимизации

Page 1: Межпроцедурные оптимизации

Межпроцедурные оптимизации

Page 2: Межпроцедурные оптимизации

Межпроцедурный анализ

Как совместить хороший стиль программирования и требования к быстродействию приложения?

• Модульность.• Читаемость кода и использование подпрограмм для повторяющихся вычислений.• Принцип реализации утилит как «черного ящика».

Модульность исходного кода усложняет задачу по его оптимизации.

Обсуждаемые в предыдущих разделах оптимизации процедурного уровня:• эффективно работают только с локальными переменными;• всякий вызов функции - «черный ящик»;• неизвестны многие свойства переданных в процедуру параметров;• неизвестны свойства глобальных переменных.

Для решения этих проблем необходимо исследование программы в целом.

Page 3: Межпроцедурные оптимизации

Некоторые основные проблемы оптимизаций процедурного уровня

1) Скалярные оптимизации. Reaches(b) = U для всех предшественников (defsout(p) U (reaches(p) ∩

¬killed(p)) В случае вызова неизвестной функции в базовом блоке p необходимо все

глобальные переменные, локальные переменные и доступные функции, поместить в killed(p).

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

2) Оптимизации циклических конструкций. Для таких оптимизаций необходимо:• корректное определение объектов, которые могут ссылаться на одну

память;• знание свойств функций внутри циклов (не изменяют итерационные

переменные, не содержат выхода из программы и т.д.);• оценка количества итераций цикла.

3) Векторизация. Необходима информация о выравнивании объектов в памяти.

Page 4: Межпроцедурные оптимизации

Протяжка константы через неизвестную функцию

Рассмотрим простую программу:test.c:extern void unknown(int *a);int main(){int a,b,c;

a=5;c=a;unknown(&a);if(a==5) printf("a==5\n");b=a;printf("%d %d %d\n",a,b,c);return(1);}

Сохранится ли if утверждение в результирующем коде?

Page 5: Межпроцедурные оптимизации

Ассемблер полученный с помощью icl: icс –O2 test.c –S…call _unknown ;9.1

; LOE ebx esi

.B1.9: ; Preds .B1.8

add esp, 8 ;9.1

; LOE ebx esi

.B1.2: ; Preds .B1.9

mov edi, DWORD PTR [a.3.0.1] ;10.4

cmp edi, 5 ;10.7

jne .B1.4 ; Prob 0% ;10.7

; LOE ebx esi edi

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

Page 6: Межпроцедурные оптимизации

CSE (Удаление общих подвыражений)#include <math.h>#include <stdio.h>extern void unknown();float a,b;

int main() {float c,d;scanf_s("%f",&a);scanf_s("%f",&b);c=0;if(sqrt(a+b)>3) c=a+b;else unknown();d=sqrt(a+b)+c;printf("d=%f\n",d);return 1;} Здесь есть общее подвыражение sqrt(a+b). Будет ли CSE работать с этим

подвыражением?

Page 7: Межпроцедурные оптимизации

icl cse.c –S…

.B1.3:: ; Preds .B1.2

movss xmm0, DWORD PTR [a] ;15.9

pxor xmm14, xmm14 ;14.1

addss xmm0, DWORD PTR [b] ;15.11

cvtps2pd xmm1, xmm0 ;15.11

sqrtsd xmm1, xmm1 ;15.4

comisd xmm1, QWORD PTR [_2il0floatpacket.0] ;15.14

jbe .B1.5 ; Prob 22% ;15.14

; LOE rbx rbp rsi rdi r12 r13 r14 r15 xmm0 xmm1 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15

.B1.4:: ; Preds .B1.3

movaps xmm14, xmm0 ;16.3

jmp .B1.7 ; Prob 100% ;16.3

; LOE rbx rbp rsi rdi r12 r13 r14 r15 xmm1 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15

.B1.5:: ; Preds .B1.3

call unknown ;19.3

; LOE rbx rbp rsi rdi r12 r13 r14 r15 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15

.B1.6:: ; Preds .B1.5

movss xmm0, DWORD PTR [a] ;21.8

addss xmm0, DWORD PTR [b] ;21.10

cvtps2pd xmm1, xmm0 ;21.10

sqrtsd xmm1, xmm1 ;21.3

Page 8: Межпроцедурные оптимизации

Однопроходная и двухпроходная компиляция

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

Граф вызовов представляет взаимоотношение вызовов между процедурами в программе. Каждая вершина представляет процедуру, и каждая грань (f,g) указывает, что процедура f вызывает процедуру g.

Граф может быть статическим, вычисленным на этапе компиляции или динамическим, т.е. отражающим реальные вызовы при выполнении программы. (VTune).

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

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

Page 9: Межпроцедурные оптимизации

FE (C++/C или Fortran)

Внутреннее представление

Профилировщик

Скалярные оптимизации HPO

Генератор кода

Исходные файлы

Обьектные файлы

Временный файл или Obj с ВП

IP/IPO оптимизации

Скалярные оптимизации

HPO Генератор кода

Исполняемый файлБиблиотека

Архитектура компилятора

Page 10: Межпроцедурные оптимизации

Два вида межпроцедурных оптимизаций:• модульная • оптимизация для всей программы

Qip[-] enable(DEFAULT)/disable single-file IP optimization

within files

/Qipo[n] enable multi-file IP optimization between files

/Qipo-c generate a multi-file object file (ipo_out.obj)

/Qipo-S generate a multi-file assembly file (ipo_out.asm)

/Qipo-jobs<n>

specify the number of jobs to be executed simultaneously during the

IPO link phase

Page 11: Межпроцедурные оптимизации

Изменится ли что-либо, если мы определим некую процедуру unknown и перекомпилируем с –ipo:

#include <stdio.h>extern float a,b; void unknown() { printf("a=%f b=%f\n",a,b);}icl –Qipo test.c unknown.c –Ob0 –Qipo-S Работают ли протяжка констант и удаление общих

подвыражений?

Page 12: Межпроцедурные оптимизации

Анализ совмещений (alias analysis)

• анализ совмещения через параметры;• анализ совмещения указателей для глобальных и

статических указателей (Local point to analysis - LPT). Важная часть механизма определения зависимостей.

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

Page 13: Межпроцедурные оптимизации

Пример на LPT анализ#include <stdio.h>int p1=1,p2=2;int *a,*b;void init(int **a, int **b) {*a=&p1;*b=&p1; // <= a and b poins to p1 }

int main() {int i,ar[100];init(&a,&b);printf("*a= %d *b=%d\n",*a,*b);

for(i=0;i<100;i++) {ar[i]=i*(*a)*(*a);*b+=1; // *a is changed through *b}printf("ar[50]= %d p2=%d\n",ar[50],p2);} Можно ли осуществлять цикловые оптимизации с циклом for?

Page 14: Межпроцедурные оптимизации

Межпроцедурный анализ используется для протяжки атрибутов функций. Например, есть атрибуты no_side_effect, always_return и т.д.

IPA используется для протяжки атрибутов переменных. Например, переменные, помеченные атрибутом «адрес был взят», исключаются из многих оптимизаций. Этот атрибут для глобальных переменных устанавливает IPA.

Продвижение данных (Data promotion). Каждая переменная имеет свою область видимости (scope). IPA позволяет протягивать данные, которые используются только в определенной процедуре, на уровень этой процедуры, что сразу позволяет включить их во многие оптимизации.

Функции, используемые только в одной программе, получают атрибут static.

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

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

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

Page 15: Межпроцедурные оптимизации

Межпроцедурные оптимизации

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

Протяжка константных аргументов Если при каждом вызове процедуры f(i,j,k) в программе в качестве

аргумента i всегда передается некая константа, то это позволяет присвоить первому формальному аргументу значение этой константы в коде процедуры f().

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

протянуть это значение наружу. То же самое касается передаваемых в процедуру аргументов. Если перед

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

Page 16: Межпроцедурные оптимизации

Пример. Константа протягивается в функцию через аргумент. Константа протягивается в вызывающую функцию.

cat test.c

#include <stdio.h>

extern void known(int variant,int *var);

int main() {

int var;

int ttt;

var=2;

ttt=3;

known(var,&ttt);

printf("ttt=%i\n",ttt);

}

cat known.cvoid known(int var,int *ttt) { if(var>0) (*ttt)++; else (*ttt)--;}

icc –Ob0 test.c known.c -fast -ipo-S…known:# parameter 1: %edi# parameter 2: %rsi..B2.1: # Preds ..B2.0..___tag_value_known.8: #1.30 addl $1, (%rsi) #3.3 ret #6.1 .align 16,0x90…Таким образом, протянув константу удалось избавиться от ветвления.

Page 17: Межпроцедурные оптимизации

Подстановка (inlining) Подстановка удаляет накладные расходы, связанные с подготовкой к

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

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

Атрибут функции inline

inline int exforsys(int x1) { return 5*x1;}

Программист «рекомендует» компилятору сделать подстановку такой функции.

Page 18: Межпроцедурные оптимизации

Управление подстановкойСинтаксис:

#pragma inline[recursive]

#pragma forceinline[recursive]

#pragma noinline

Аргумент Recursive

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

Директива inline рекомендует инлайнить

noinline требует не инлайнить

forceinline требует инлайнить

Аналоги для языка Фортран

cDEC$ ATTRIBUTES INLINE :: procedure

cDEC$ ATTRIBUTES NOINLINE :: procedure

cDEC$ ATTRIBUTES FORCEINLINE :: procedure

10/17/10

Page 19: Межпроцедурные оптимизации

10/17/10

Компиляторные опции подстановки/Ob<n> control inline expansion:

n=0 disable inlining

n=1 inline functions declared with __inline, and perform C++ inlining

n=2 inline any function, at the compiler's discretion

/Qinline-min-size:<n>

set size limit for inlining small routines

/Qinline-min-size-

no size limit for inlining small routines

/Qinline-max-size:<n>

set size limit for inlining large routines

/Qinline-max-size-

no size limit for inlining large routines

/Qinline-max-total-size:<n>

maximum increase in size for inline function expansion

/Qinline-max-total-size-

no size limit for inline function expansion

Page 20: Межпроцедурные оптимизации

10/17/10

/Qinline-max-per-routine:<n>

maximum number of inline instances in any function

/Qinline-max-per-routine-

no maximum number of inline instances in any function

/Qinline-max-per-compile:<n>

maximum number of inline instances in the current compilation

/Qinline-max-per-compile-

no maximum number of inline instances in the current compilation

/Qinline-factor:<n>

set inlining upper limits by n percentage

/Qinline-factor-

do not set set inlining upper limits

/Qinline-forceinline

treat inline routines as forceinline

/Qinline-dllimport

allow(DEFAULT)/disallow functions declared __declspec(dllimport) to be inlined

/Qinline-calloc directs the compiler to inline calloc() calls as malloc()/memset()

Page 21: Межпроцедурные оптимизации

Клонирование функций

Если в функцию f(x,y,x) передается x=2 в одном случае и x=3 в другом, то возможно заменить вызов функции f на вызовы f2 и f3.

Частичная подстановка Если в функции f содержится вначале функции код,

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

Page 22: Межпроцедурные оптимизации

Девиртуализация для C++

Вызов функций через указатели дороже простого вызова. C++ - объектно-ориентированный язык, поддерживающий

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

от типа объекта времени выполнения. A => B => C Все наследуемые классы переопределяют virtual int foo()

int process(class A *a) { return(a->foo()); }

Page 23: Межпроцедурные оптимизации

#include <stdio.h>class A { virtual int foo() { return 1; }; friend int process(class A *a);};class B: public A { virtual int foo() { return 2; }; friend int process(class A *a);};int process(class A *a) { return(a->foo());};void main() { A* pA = new A; B* pB = new B;

int result1 = process(pA); int result2 = process(pB); printf("%d,%d\n",result1,result2);}

icl -S rta0.ccРассмотрим ассемблер для функции process:

?process@@YAHPAVA@@@Z PROC NEAR ; parameter 1: 4 + esp.B4.1: ; Preds .B4.0 mov ecx, DWORD PTR [4+esp] ;13.5 mov eax, DWORD PTR [ecx] ;14.9 call DWORD PTR [eax] ;14.9 .B4.2: ; Preds .B4.1 ret ;14.9 ALIGN 16 ; LOE; mark_end;?process@@YAHPAVA@@@Z ENDP

Page 24: Межпроцедурные оптимизации

Трансформации данных

Перестановка полей структур. Если несколько полей используются в программе

очень интенсивно, то, разместив их рядом, можно снизить количество промахов по памяти.

Расщепление структуры (structure splitting). Редко используемые поля выносятся в специальную

«холодную» секцию.

Без межпроцедурного анализа нельзя доказать возможность таких оптимизаций.

Page 25: Межпроцедурные оптимизации

Перестановка полей и разбиение структурыstruct.h :#ifndef PERFtypedef struct { double x; char title[40]; double y; char title2[22]; double z;} VecR;#elsetypedef struct { char title[40]; char title2[22];} ColdFields;typedef struct { double x; double y; double z; ColdFields *cold;} VecR;#endif

Page 26: Межпроцедурные оптимизации

struct.c :#include <stdlib.h>

#include <stdio.h>

#include "struct.h"

int main() {

int i, k;

VecR *array;

array=(VecR*)malloc(10000*sizeof(VecR));

#ifdef PERF

for(i=0;i<10000;i++)

array[i].cold=(ColdFields*)malloc(sizeof(ColdFields));

#endif

for (i=0;i<10000;i++){

array[i].x = 1.0; array[i].y = 2.0; array[i].z = 0.0; }

for(k=1;k<10000;k++) {

for (i=k;i<9999;i++){

array[i].x = array[i-1].y+1.0;

array[i].y = array[i+1].x+array[i+1].y;

array[i].z = (array[i-1].y - array[i-1].x)/array[i-1].y; }

}

printf("%f \n",array[100].z);

#ifdef PERF

for(i=0;i<10000;i++)

free(array[i].cold);

#endif

free(array);

}

icc struct.c -fast -o a.outicc struct.c -fast -DPERF -o b.outtime ./a.outreal 0m0.808stime ./b.outreal 0m0.566s

Page 27: Межпроцедурные оптимизации

Излишние ссылки на память (Pointer chasing) Доступ к данным через несколько ссылок – одна из самых

распространенных проблем в С++ коде. Если структуры ваших данных не помещаются в подсистеме памяти, то при разыменовании каждой ссылки вы рискуете ожидать данные из памяти.

10/17/10

Class Employers {Personal_info *p;…};;

Class personal_info {Family_info *f;…};

Class family_info {int members;…};

All_members+= employer->p->f->members;

Page 28: Межпроцедурные оптимизации

10/17/10

Спасибо за внимание!