Programınız için performans ipuçları.(x86)

Genel amaçlı üretilmiş mimariler üzerinde sinyal işleme, görüntü işleme gibi ağır matematiksel işlemler gerektiren uygulamalar yapıyorsanız optimizasyon çok önemli bir hale gelir. Bilgisayar tasarımı çalışırken bir zamanlar Türkiye Görüntü İşleme mail grubuna gönderilen bir yazı aklıma geldi. Faydalı bir yazı olduğunu düşündüm ve blogumda paylaşmak istedim. Yazı 17 Şubat 2008 tarihinde Mustafa Sakar tarafından AMD x86 Code Optimization Guide tan yararlanılarak yazılmış.
Özeti biraz daha özetleyip bilgimiz olan yerlere burnumuzu sokarsak ;)
1- 32-bit data tiplerini kullanın. (Neden çünkü mimari bunun üzerine.
Bütün aritmatik işlem birimleri 32 bit için design edilmiş. Daha büyük veri tipleri bölünüp yapılır.
Küçük olanlarda 32 bitlik registerlara taşınır işaretli ise işaret genişletilir ekstra işlemler...)
2- Integer işlemlerinin işaretine önceden karar verin. Bölüm ve kalan hesaplarken, döngü sayaçları,
array indexleri için unsigned tipler, Integer-to-float dönüşümleri için signed tipler kullanın.
Kötü örnek:
double x; // MOV [tmep+4],0
unsigned int i; // MOV EAX, i
// MOV [temp], EAX
x = i; // FILD QWORD PTR [temp]
// FSTP QWORD PTR [x}
İyi örnek:
double x; // FILD DWORD PTR [i]
int i; // FSTP QWORD PTR [x]
x = i;
3- Bölme işlemlerini işaretsiz tiplerle yapmak daha hızlıdır.
Kötü örnek:
int i;
i = i / 4; // MOV EAX,i
// CDQ
// AND EDX,3
// ADD EAX, EDX
// SAR EAX, 2
// MOV i, EAX
İyi Örnek:
unsigned i;
i = i / 4; // SHR i,2
4- Pointer-style kod yerine Array-style kod kullan. //Bu çok önemli..
Kötü örnek:
typedef struct {
float x,y,z,w;
} VERTEX;
typedef struct {
float m[4][4];
} MATRIX;
void XForm (float *res, const float *v, const float *m, int
numverts) {
float dp;
int i;
const VERTEX* vv = (VERTEX *)v;
for (i = 0; i < dp =" vv-">x * *m++;
dp += vv->y * *m++;
dp += vv->z * *m++;
dp += vv->w * *m++;
*res++ = dp; /* write transformed x */
dp = vv->x * *m++;
dp += vv->y * *m++;
dp += vv->z * *m++;
dp += vv->w * *m++;
*res++ = dp; /* write transformed y */
dp = vv->x * *m++;
dp += vv->y * *m++;
dp += vv->z * *m++;
dp += vv->w * *m++;
*res++ = dp; /* write transformed z */
dp = vv->x * *m++;
dp += vv->y * *m++;
dp += vv->z * *m++;
dp += vv->w * *m++;
*res++ = dp; /* write transformed w */
++vv; /* next input vertex */
m -= 16; /* reset to start of transform matrix */
}
}
iyi Örnek:
typedef struct {
float x,y,z,w;
} VERTEX;
typedef struct {
float m[4][4];
} MATRIX;
void XForm (float *res, const float *v, const float *m, int
numverts) {
int i;
const VERTEX* vv = (VERTEX *)v;
const MATRIX* mm = (MATRIX *)m;
VERTEX* rr = (VERTEX *)res;
for (i = 0; i <>x = vv->x*mm->m[0][0] + vv->y*mm->m[0][1] +
vv->z*<div id=":1l" class="ii gt"><wbr>mm->m[0][2] + vv->w*mm->m[0][3];
rr->y = vv->x*mm->m[1][0] + vv->y*mm->m[1][1] +
vv->z*<wbr>mm->m[1][2] + vv->w*mm->m[1][3];
rr->z = vv->x*mm->m[2][0] + vv->y*mm->m[2][1] +
vv->z*<wbr>mm->m[2][2] + vv->w*mm->m[2][3];
rr->w = vv->x*mm->m[3][0] + vv->y*mm->m[3][1] +
vv->z*<wbr>mm->m[3][2] + vv->w*mm->m[3][3];
}
}
5- Kısa donguleri tamamen kaldırmalısınız. Kötü örnek:
// 3D-transform: multiply vector V by 4x4 transform matrix M
for (i=0; i<4; i++) {
r[i] = 0;
for (j=0; j<4; j++) {
r[i] += M[j][i]*V[j];
}
}
İyi Örnek:
// 3D-transform: multiply vector V by 4x4 transform matrix M
r[0] = M[0][0]*V[0] + M[1][0]*V[1] + M[2][0]*V[2] +
M[3][0]*V[3];
r[1] = M[0][1]*V[0] + M[1][1]*V[1] + M[2][1]*V[2] +
M[3][1]*V[3];
r[2] = M[0][2]*V[0] + M[1][2]*V[1] + M[2][2]*V[2] +
M[3][2]*V[3];
r[3] = M[0][3]*V[0] + M[1][3]*V[1] + M[2][3]*V[2] +
M[3][3]*v[3];
6- Gereksiz Store-to-Load hesaplamalardan kacınmalısınız. Kötü Örnek: for (k = 1; k < VECLEN; k++) { x[k] = x[k-1] + y[k];
//Buralar facia durmadan memory e başvur. registera load et
sonucu geri registerdan memory'e store et.. }
for (k = 1; k < VECLEN; k++) { x[k] = z[k] * (y[k] - x[k-1]); }
İyi örnek:
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;
}
7 - Çarpma işlemi bölmeden hızlıdır.
Kötü örnek:
double a,b,c,e,f;
e = a/c;
f = b/c;
İyi örnek:
double a,b,c,e,f,t;
t = 1/c;
e = a*t
f = b*t;
Kötü örnek:
int i,j,k,m;
m = i / j / k;
İyi örnek:
int i,j,k,l;
m = i / (j * k);
8 - Hızlı Floating-Point-to-Integer cevrimi kullanın.
Yavaş Örnek:
double x;
int i;
i = x;
Hızlı Örnek:
#define DOUBLE2INT(i,d) \
{double t = ((d)+6755399441055744.0); i=*((int *)(&t));}
double x;
int i;
DOUBLE2INT(i,x);
view raw optHints hosted with ❤ by GitHub
Kod
***Bazı maddeleri atladım. En önemli gördüklerimi seçmek istedim. Görüntü işleme ile uğraşıyor performans sıkıntısı yaşıyorsanız hem de çift çekirdekli işlemciniz varsa ilk işiniz bazı işleri paralel yaptırmak olsun. Bluekid abimize link verelim. OpenMP genel. OpenMP uygulama. İmkanınız varsa güncel compiler lar kullanın. Yeni compilerla yeni donanımlar için en iyi optimizasyon seçenekleri sağlarlar. Performans bakımından aynı mfc uygulamasının MS un VC++ 9 ile derlenmesi ile VC++ 6 ile derlenmesi arasında inanılmaz farklar gözlenmiştir. $$$* Ayrıca intel işlemci kullanıyorsanız intelin kendi compiler ı için vaatleri büyük. (Denenmemiştir.)

4 yorum - yorum yaz:

a.k. dedi ki...

Görüntü İşleme ile ilgili bir mail grubu olduğunu bilmiyordum, öğrendiğim iyi oldu :)

Volkan SALMA dedi ki...

Bu sıralar pek aktif değil ama.. :(

a.k. dedi ki...

E-posta grubu uzun zamandır aktif değil gibi görünüyor. Sağlık olsun diyelim :)

Adsız dedi ki...

kod optimizasyonu için gnu derleyicilieri kullanırken -O3 parametresi kullanılabilir ayrıca.

Yorum Gönder