By the way, here is the C++ program using my String Class that returned the 16000 ticks. Unlike the others I posted (except the PowerBASIC ones) you won’t be able to run this because you won’t have my String Class, but this is what the program looked like. I’m not planning to post my String Class unless anyone wants it. Its kind of long and needs about three of four posts to fit it all. If I would post it you would be able to run this then …
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
|
#include "Windows.h"
#include <stdio.h>
#include "Strings.h"
enum // Exercise
{ // =======================================
NUMBER = 2000000, // 1)Create a 2MB string of dashes
LINE_LENGTH = 90, // 2)Change every 7th dash to a "P"
RIGHT_BLOCK = 4000, // 3)replace every "P" with a "PU" (hehehe)
NUM_PS = NUMBER/7+1, // 4)replace every dash with an "8"
PU_EXT_LENGTH = NUMBER+NUM_PS, // 5)Put in a CrLf every 90 characters
NUM_FULL_LINES = PU_EXT_LENGTH/LINE_LENGTH, // 6)Output last 4K to Message Box
MAX_MEM = PU_EXT_LENGTH+NUM_FULL_LINES*2
};
int main()
{
String CrLf("\r\n");
register int iCtr=0;
register int i=0;
char szTitle[64];
DWORD t1,t2;
t1=GetTickCount(), t2=t1;
puts("Processing...");
String s1(MAX_MEM);
t1=GetTickCount()-t1;
printf("After Allocating s1(MAX_MEM)... : %u\n",(unsigned)t1);
t1=t2;
s1.Make('-',NUMBER);
t1=GetTickCount()-t1;
printf("After Making 2,000,000 dashes... : %u\n",(unsigned)t1);
t1=t2;
String s2(MAX_MEM);
for(i=0; i<NUMBER; i++, iCtr++)
{
if(iCtr==7)
{
s1.SetChar(i,'P');
iCtr=0;
}
}
t1=GetTickCount()-t1;
printf("After Assigning Ps Every 7th Char... : %u\n",(unsigned)t1);
t1=t2;
s2=s1.Replace((char*)"P",(char*)"PU");
t1=GetTickCount()-t1;
printf("After Doing John's PU Thing... : %u\n",(unsigned)t1);
t1=t2;
s1=s2.Replace((char*)"-",(char*)"8");
t1=GetTickCount()-t1;
printf("After Replacing Dashes With 8s... : %u\n",(unsigned)t1);
t1=t2;
s2.SetChar(0,'\0');
for(i=0; i<PU_EXT_LENGTH; i+=LINE_LENGTH)
s2+=s1.Mid(i+1,LINE_LENGTH)+CrLf;
t1=GetTickCount()-t1;
printf("After Big Concatenation With CrLfs... : %u\n",(unsigned)t1);
t1=t2;
s1=s2.Right(RIGHT_BLOCK);
t1=GetTickCount()-t1;
printf("Finished! : %u\n",(unsigned)t1);
sprintf(szTitle,"Here's Your String In %d Ticks John!",(unsigned)t1);
MessageBox(0,s1.lpStr(),szTitle,MB_OK);
getchar();
return 0;
}
|
So the next thing I decided to do was see if I could finally learn the C++ String Class from the C++ Std. Library to see how it should really be done. But I quickly ran into problems there as I couldn’t seem to find anything there that was a usable replacement for PowerBASIC’s Replace statement, or the one I finally coded for my String Class. To this day I still can’t believe that. I keep feeling in my ignorance I just missed it. After giving it a pretty fair amount of research and work I finally came across a function that seemed to do the trick in Bruce Eckel’s “Thinking In C++” book, Volume 2. That would be that ReplaceAll() function in my previous post with the 273,000 tick count. I was happy to find that function in a book by a famous author and C++ teacher. I figured it ought to be good. Afterall, he’s a friend of Charles Petzold, a former member of the C++ Standards Committee, and a well known C++ book author, for whatever that’s worth, and I figure that ought to be worth something.
So when I hit run on that program I expected to see instant results like with the PowerBASIC program. I figurred you dumb piker you’ll see how its really done now! I figured it would likely come in faster than the PowerBASIC program. I wasn’t prepared to wait four and a half minutes! If I recall, at the time I thought my computer locked up.
But eventually, I came to see that’s what the situation was. I still couldn’t believe it. So I took the whole issue over to www.daniweb.com and eventually Vijayan wrote me that C++ program using the C++ Std. Lib’s String Class which beat all my programs and the PowerBASIC program by a handy margin – like about twice as fast. That program – as modified by me, I posted previously. It was coming in around 90 ticks, which was about twice as fast as the PowerBASIC program.
If you take the time to look at the timings of my String Class as compared to the Std. Lib’s String Class using Bruce Eckel’s ReplaceAll() function, you’ll see something really, really strange. My String Class with its Replace() member is only taking like 300 ticks out of the 16,000 total to do the substitution of Ps with PUs. The ReplaceAll() function of Eckel’s is using almost its whole 273,000 ticks stuck in that one operation. Everything else its doing almost instantaneous. And my program is using almost its entire 16,000 ticks doing operator+ calls, i.e., string concatenation. The Std. Lib’s String Class is doing the whole concatenation at the end in an unbelievable 30 ticks!!!! So as you can see the behaviour of both these string classes is radically, radically different. To this day I haven’t been able to figure out how the Std. Lib’s String Class is doing that final operator+ concatenation at the end in 30 ticks. I’m in awe of it. The conclusion I finally came to is that its just magic, pure and simple and unexplainable.
It was only after all that thrashing around that I developed the C programs I posted two days ago with timings coming in around 30 to 50 ticks that kind of pushed the limits of GetTickCount()! So the way it finally ended up is that using pointers and low level memory access with the C Std. Lib’s ‘String Primitives’, i.e., strcpy(), strcat(), etc., I was able to get equivalent timings in the 30 – 40 ms range with both PowerBASIC and C.
All kidding aside (about the magic part), I put all kinds of efforts, for many weeks, trying to figure out how to get my operator+ overloads down to times approaching the Std. Lib’s string class, but I never could put any kind of dent in it. My code is taking 16,000 ticks to do what the Std. Lib’s string class is doing in 30 ticks. Of course, my code beat the Std Lib’s string class by a factor of 16 times in that total algorithm, but the reason it did this was because of Eckel’s horrendous ReplaceAll() function.
I tried inlining my operator+ members, various uses of the const parameter modifier here and there – everything I could think of. The only thing I didn’t try was deciphering the bstring and string open source code to see how it works. The only thing I’m pretty sure about is that to do what its doing its not actually making the operator+ overload member calls. By that I mean I think the compiler is actually optimizing them all away in the same way it optimized away the original loops of the test program I first described where it factored apart a numerical sequence to arive at a predetermined answer without actually doing any loop code. Recall where I described that where an asm guru disassembled the exe to see what it was doing?
Anyway, if I’m right about that, and I think I am, then that certainly says something of the calibre of coder who put that string class together in such a way that he/she knew the algorithm would tie into compiler optimization routines that would directly feed into C or asm string primitives and completely bypass member function calls. That’s why I essentially gave up trying to get my timings down any further on my operator+ member functions.