Некоторые вопросы оптимизации Содержание Упрощение алгебраических выражений Использование регистров Использование быстрых инструкций Оптимизация работы циклов Эффективное использование памяти 2 Упрощение алгебраических выражений Исходный код Оптимизированный код x = y + 0; x = y * 0; x = y / 1; x = y; x = 0; x = y; 3 Упрощение алгебраических выражений Исходный код if( a[y*3]<0 || b[y*3]>10) a[y*3] = 0; Оптимизированный код ind=y*3; if( a[ind]<0 || b[ind]>10) a[ind] = 0; 4 Упрощение алгебраических выражений Исходный код Оптимизированный код int i, j, k, m; m = i / j / k; int i, j, k, m; m = i / (j * k); 5 Упрощение алгебраических выражений Исходный код Оптимизированный код if(a[i]<a[j]) { t = a[i]; a[i] = a[j]; a[j] = t; } t1=a[i]; t2=a[j]; if(t1<t2) { t = t1; a[i] = t2; a[j] = t; } 4 обращения в память 2 обращения в память 6 Упрощение алгебраических выражений Исходный код if (a <= max && a >= min && b <= max && b >= min) Оптимизированный код if (a > max || a < min || b > max || b < min) 7 Использование регистров Исходный код Оптимизированный код int v; float t; register int v; register float t; for(v=0;v<1000;v++) ar[v] += (float)v * t; for(v=0;v<1000;v++) ar[v] += (float)v * t; 8 Использование быстрых инструкций Исходный код Оптимизированный код int a, b; b = a * 10; int a, b; b = (a<<3) + (a<<1); 9 Использование быстрых инструкций Исходный код Оптимизированный код const int b=16; scanf(“%d”,&a); // a=49; c = a/b; const int b=16; const int d=16/16; scanf(“%d”,&a); // a=49; c = (a>>4)*d; // c = (a/16)*(16/b) !!! Проблема выбора d 10 Использование быстрых инструкций Исходный код Оптимизированный код const int b=16; scanf(“%d”,&a); // a=49; c = a%b; const int b=16; const int d=-16; scanf(“%d”,&a); // a=49; c = a – (a&d); N 2 a t t a %b a % k 2 a N & 2 k k 2 11 Использование быстрых инструкций Исходный код Оптимизированный код char animalname[30]; char *p; p = animalname; if ((strlen(p) > 4) && (*p == 'y')) { ... } char animalname[30]; char *p; p = animalname; if ((*p == 'y') && (strlen(p) > 4)){ ... } !!! Сначала выполняются простые логические операции 12 Использование быстрых инструкций Исходный код double x; int i; i = x; Оптимизированный код #define DOUBLE2INT(i, d) \ {double t = ((d) + 6755399441055744.0); \ i = *((int *)(&t));} double x; int i; DOUBLE2INT(i, x); !!! 6755399441055744.0 = 2^52 + 2^51 13 Оптимизация работы циклов Исходный код Оптимизированный код for(i=0;i<10;i++) a[i]=b[i]+c[i]; for(i=0;i<10;i++) d[i]=e[i]+f[i]; for(i=0;i<10;i++) { a[i]=b[i]+c[i]; d[i]=e[i]+f[i]; } 14 Оптимизация работы циклов Исходный код Оптимизированный код for(i=0;i<1000;i++) { if(x<2) y+=i; else y -=i; } if(x<2) for(i=0;i<1000;i++) y+=i; else for(i=0;i<1000;i++) y-=i; 15 Оптимизация работы циклов Исходный код Оптимизированный код for(i=0;i<100;i++) { if(i%2) a[i]=x; else a[i]=y; } for(i=0;i<100;i+=2) { a[i]=y; a[i+1]=x; } 16 Оптимизация работы циклов Исходный код Оптимизированный код for(i=0;i<1000;i++) sum += a[i]; for(i=999;i>=0;i--) sum += a[i]; !!! Инструкции декремента автоматически устанавливают флаг zero при достижении нуля, поэтому явная проверка с нулём не требуется 17 Оптимизация работы циклов (раскрутка циклов) Исходный код // 3D-transform for (i = 0; i < 4; i++) { r[i] = 0; for (j = 0; j < 4; j++) { r[i] += m[j][i] * v[j]; } } 18 Оптимизация работы циклов (раскрутка циклов) Оптимизированный код // 3D-transform r[0] = m[0][0] * v[2] + m[3][0] r[1] = m[0][1] * v[2] + m[3][1] r[2] = m[0][2] * v[2] + m[3][2] r[3] = m[0][3] * v[2] + m[3][3] v[0] + m[1][0] * v[3]; v[0] + m[1][1] * v[3]; v[0] + m[1][2] * v[3]; v[0] + m[1][3] * v[3]; * v[1] + m[2][0] * * v[1] + m[2][1] * * v[1] + m[2][2] * * v[1] + m[2][3] * 19 Оптимизация работы циклов (раскрутка циклов) Исходный код double a[100], sum; int i; sum = 0.0; for (i = 0; i < 100; i++) { sum += a[i]; } 20 Оптимизация работы циклов (раскрутка циклов) Оптимизированный код double a[100], sum1, sum2, sum3, sum4, sum; int i; sum1 = 0.0; sum2 = 0.0; sum3 = 0.0; sum4 = 0.0; for (i = 0; i < 100; i += 4) { sum1 += a[i]; sum2 += a[i+1]; sum3 += a[i+2]; sum4 += a[i+3]; } sum = (sum4 + sum3) + (sum1 + sum2); 21 Эффективное использование памяти Исходный код double x[VECLEN], y[VECLEN], z[VECLEN]; unsigned int k; for (k = 1; k < VECLEN; k++) { x[k] = x[k-1] + y[k]; } for (k = 1; k < VECLEN; k++) { x[k] = z[k] * (y[k] - x[k-1]); } 22 Эффективное использование памяти Оптимизированный код double x[VECLEN], y[VECLEN], z[VECLEN]; unsigned int k; double t; t = x[0]; for (k = 1; k < VECLEN; k++) { t = t + y[k]; x[k] = t; } t = x[0]; for (k = 1; k < VECLEN; k++) { t = z[k] * (y[k] - t); x[k] = t;} 23 Эффективное использование памяти (выравнивание данных) Исходный код Оптимизированный код struct { char a[5]; // 5b long k; // 4b double x; // 8b } baz; struct { double x; long k; char a[5]; char pad[7]; } baz; // // // // 8b 4b 5b 7b 24 Эффективное использование памяти Исходный код Оптимизированный код float a_cmplx[500], b_cmplx[500]; struct complex { float a, b;}; complex cmplx[500]; 25 Эффективное использование памяти (выравнивание данных) Исходный код Оптимизированный код short ga, gu, gi; long foo, bar; double x, y, z[3]; char a, b; float baz; double z[3]; double x, y; long foo, bar; float baz; short ga, gu, gi; 26 Эффективное использование памяти (выравнивание данных) Исходный код short a[15]; /* 2 bytes data */ int b[15], c[15]; /* 4 bytes data */ for (i=0; i<15, i++) a[i] = b[i] + c[i]; 27 Эффективное использование памяти (выравнивание данных) Оптимизированный код int b[15], c[15]; /* 4 bytes data */ short a[15]; /* 2 bytes data */ for (i=0; i<15, i++) a[i] = b[i] + c[i]; 28 Эффективное использование памяти (предвыборка данных) Оптимизированный код for (ii = 0; ii < 100; ii++) { for (jj = 0; jj < 24; jj+=8) { /* N-1 iterations */ _mm_prefetch((char*)&a[ii][jj+8],_MM_HINT_NTA); computation(a[ii][jj]); } _mm_prefetch((char*)a[ii+1][0],_MM_HINT_NTA); computation(a[ii][jj]); /* Last iteration */ } 29 Эффективное использование памяти Исходный код float **a; a = new float*[512]; for(int i;i<512;i++) a[i]=new float[512]; a[i][j]; //a(i,j) Оптимизированный код float *a; a = new float[512*512]; a[i*512 + j]; // a(i,j) 30 Эффективное использование памяти Исходный код float *memory = new float[4*N]; for(int i=0;i<N;i++) { a = *(memory+i*4); b = *(memory+i*4+1); c = *(memory+i*4+2); *(memory+i*4+3) = func(a,b,c); } 31 Эффективное использование памяти Оптимизированный код float *a = new float[N], *b = new float[N], *c = new float[N], *d = new float[N]; for(int i=0;i<N;i++) { d[i] = func(a[i],b[i],c[i]); } !!! Может привести к КЭШ - промаху 32