Re: RES: [Dev-C++] Optimizing C code, what is best??
Open Source C & C++ IDE for Windows
Brought to you by:
claplace
|
From: Per W. <pw...@ia...> - 2003-05-30 22:40:58
|
It is nice that you can laugh at my code. However, I DID benchmark
the code before writing the previous mail...
Don't stare yourself blind at number of instructions in the assembler
file.
Machine 1: Pentium II, 2*400MHz, 512MB PC100, W2K Pro
Machine 2: AMD Athlon 1300MHz, 1GB PC133, WinXP Pro
Machine 3: AMD XP2600+, 1GB 400MHz CL2.5, WinXP Pro
Machine 4: Celeron 2*566MHz, 512MB PC100, GNU/Linux Debian 3.0
Test 1: Loop from original mail
Test 2: Optimization 1
Test 3: Optimization 2 - the one I sent in mail
Test 4: Loop unrolling - failed because of code size/complexity
All times are in seconds calendar time, with the test program running at
>= 98% (except of course for the two dual-processor machines).
Machine 1: 2: 3: 4: Note:
Test 1: 198 60 32 193 (compiler inlined code)
Test 2: 233 54 29 223 (compiler inlined code)
Test 3: 100 24 12 168 (compiler inlined code)
Test 4: 198 71 37 132 (compiler unrolled loop 10 times)
The big loss for Test 4 was that it wasn't inlined, and since I made 1
billion calls with vector length 7, the method call time was significant.
Machine 1-3 used gcc 3.2 with -O3, -march=pentium3
Machine 4 used gcc 2.95 with -O3, -march=pentium
---
Same tests as above, but with -O2, i.e. no inlining
Machine 1: 2: 3: 4:
Test 1: 263 84 45 298
Test 2: 303 72 39 304
Test 3: 233 68 36 255
Test 4: 199 71 37 231
---
Conclusion: Loop alternative 3 was always advantageous for all
processors, with or without inlining. With inlining off, the manual
inlining of alternative 4 gave a further improvement for the P2 and
Celeron machines, while being just a bit slower for the AMD machines.
As can be seen, the different constructs did make a significant
difference, especially when inlining. Are you really sure that the above
results are just a laugh, and that nowadays, you don't have to bother
about your code anymore? If such a quick rewrite can be made to a
function consuming 50% of the total runtime, I'd say it is work
looking at the function.
---
#include <time.h>
#include <stdio.h>
typedef unsigned __int64 DWORD64;
typedef unsigned short WORD;
class CTestClass {
public:
enum {
rows = 20,
cols = 7
};
short *sData[rows];
CTestClass();
DWORD64 computeDistance1(short *src,const WORD &wRow,const WORD
&wColFrom,const WORD &wColTo) const;
DWORD64 computeDistance2(short *src,const WORD wRow,const WORD
wColFrom,const WORD wColTo) const;
DWORD64 computeDistance3(short *src,const WORD wRow,const WORD
wColFrom,const WORD wColTo) const;
DWORD64 computeDistance4(short *src,const WORD wRow,const WORD
wColFrom,const WORD wColTo) const;
};
CTestClass::CTestClass() {
for (int r = 0; r < rows; r++) {
sData[r] = new short[cols];
for (int c = 0; c < cols; c++) {
sData[r][c] = r+c;
}
}
}
DWORD64 CTestClass::computeDistance1(short *src,const WORD &wRow,const
WORD &wColFrom,const WORD &wColTo) const {
DWORD64 qwDist = 0;
int iA;
short *sp = sData[wRow];
for (WORD wCol = wColFrom; wCol <= wColTo; ++wCol) {
iA = src[wCol] - sp[wCol];
qwDist += iA*iA;
}
return qwDist;
}
DWORD64 CTestClass::computeDistance2(short *src,const WORD wRow,WORD
wColFrom,const WORD wColTo) const {
DWORD64 qwDist = 0;
int iA;
short *sp = sData[wRow] + wColFrom;
src += wColFrom;
while (wColFrom++ <= wColTo) {
iA = *src++ - *sp++;
qwDist += iA*iA;
}
return qwDist;
}
DWORD64 CTestClass::computeDistance3(short *src,const WORD wRow,WORD
wColFrom,const WORD wColTo) const {
DWORD64 qwDist = 0;
int iA;
short *sp = sData[wRow] + wColFrom;
short *end = src + wColTo;
src += wColFrom;
while (src <= end) {
iA = *src++ - *sp++;
qwDist += iA*iA;
}
return qwDist;
}
DWORD64 CTestClass::computeDistance4(short *src,const WORD wRow,WORD
wColFrom,const WORD wColTo) const {
DWORD64 qwDist = 0;
int iA;
short *sp = sData[wRow] + wColFrom;
short *end = src + wColTo;
src += wColFrom;
while (src <= end) {
iA = *src++ - *sp++;
qwDist += iA*iA;
if (src > end) break;
iA = *src++ - *sp++;
qwDist += iA*iA;
if (src > end) break;
iA = *src++ - *sp++;
qwDist += iA*iA;
if (src > end) break;
iA = *src++ - *sp++;
qwDist += iA*iA;
}
return qwDist;
}
int main() {
CTestClass tst;
short v[CTestClass::cols] = {4,7,3,2,1,8,3};
int i;
time_t t0,t1,t2,t3,t4;
DWORD64 r1,r2,r3,r4;
t0 = time(NULL);
printf("start\n");
for (i = 0; i < 1000000000; i++) {
r1 = tst.computeDistance1(v,5,0,tst.cols-1);
}
t1 = time(NULL);
printf("1 (%u) %I64u\n",(unsigned)(t1-t0),r1);
for (i = 0; i < 1000000000; i++) {
r2 = tst.computeDistance2(v,5,0,tst.cols-1);
}
t2 = time(NULL);
printf("2 (%u) %I64u\n",(unsigned)(t2-t1),r2);
for (i = 0; i < 1000000000; i++) {
r3 = tst.computeDistance3(v,5,0,tst.cols-1);
}
t3 = time(NULL);
printf("3 (%u) %I64u\n",(unsigned)(t3-t2),r3);
for (i = 0; i < 1000000000; i++) {
r4 = tst.computeDistance4(v,5,0,tst.cols-1);
}
t4 = time(NULL);
printf("4 (%u) %I64u\n",(unsigned)(t4-t3),r4);
}
/Per W
On Fri, 30 May 2003, Daniel K. O. wrote:
>
> >From Per:
> > while (src <= end) {
> > iA = *src++ - *sp++;
> > dwDist2 += iA*iA;
> > }
>
> >From Zodiaq:
> > > for( WORD wCol = wColFrom; wCol <= wColTo; ++wCol )
> > > {
> > > iA = src[ wCol ] - sP[ wCol ];
> > > dwDist += iA * iA;
> > > }
>
>
>
>
> It was really funny those recent comments about "the best way to write C
> code for optimization".
> Yeah, some years ago most compilers were just "smart assemblers". But
> nowadays, worrying about stuffs like 'array[i++]' vs. '*pointer++' trying to
> make it "easier" to the compiler doesn't make much sense. If your compiler
> can't figure obvious stuffs - like iterating throgh an array - and make the
> best code for it, the best way is to find a better compiler.
> And GCC is a mature compiler; comments like "it can optimize in a way it
> won't work" without any test-case that proves it has a *serious* bug, is a
> little malicious.
>
> Just before writing this mail, I tried with a small sample:
>
> ---
> int foo2(int vet1[], int vet2[], int n)
> {
> int* i1 = vet1, *i2 = vet2, *j = vet1+n;
> int c;
> while(i1!=j)
> c += *i1++ + *i2++;
> return c;
> }
>
> int foo1(int vet1[], int vet2[], int n)
> {
> int i, c;
> for (i=0; i<n; i++)
> c += vet1[i] + vet2[i];
> return c;
> }
> ---
>
>
> And compiled it with:
>
> -S -march=pentium3 -O2
>
>
> The results:
> foo1 -- 30 lines
> foo2 -- 29 lines
>
> With just 1 instruction less (for this small example), I don't see the point
> in rewriting the code to make it faster. If I want to spend my time making
> it faster, better do it directly in assembly, not through C.
> So, my question: do you guys think that really worth being so "paranoic"
> about such details? I mean, obviously the algorithm design is important, but
> is really needed to even care about the different ways to write the same
> command, to see "which one is faster"?
>
>
>
>
>
>
> Daniel K. O.
>
>
>
>
>
> -------------------------------------------------------
> This SF.net email is sponsored by: eBay
> Get office equipment for less on eBay!
> http://adfarm.mediaplex.com/ad/ck/711-11697-6916-5
> _______________________________________________
> Dev-cpp-users mailing list
> Dev...@li...
> TO UNSUBSCRIBE: http://www.noicys.cjb.net/devcpp/ub.htm
> https://lists.sourceforge.net/lists/listinfo/dev-cpp-users
>
|