Mail Archives: djgpp/2001/12/25/08:39:32
Hi,
cleaning up/rewriting some code from C to C++ I noticed that there was a speed degradation that, to me, was not caused by my coding. The program is I/O intensive. It counts all charaters in a file and saves their offsets and and then checks that the saved values are right.
As data feeds I use small and large text and binary files and I used VC++6 as env. , but also tested the code using deloire's djgpp to make sure it was ANSI C/C++.
In both cases code was compiled for speed; /O2 compiler option.
/nologo /MLd /W3 /Gm /GX /O2 /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"Debug/std_char_ptr.pch" /YX /Fo"Debug/" /Fd"Debug/" /FD /c
Here are my questions.
Questions:
._ I have always heard local arrays are faster then heap based ones. I tested it and found no difference whatsoever. So, what is it I don't quite understand here?
._ The same algorithm coded in plain ANSI C is 67% faster. Scott Meyers and many other C++ gurus state that there is no or very little, 5%, speed difference between C and C++ programs. What is it I am doing wrong here?
._ Is this the most optimal compiler setting for speed using Win32/VC++?
Thanks
Camilo
Code examples:
// - - - - - - - - - - - - - - - C version of program - - - - - - - - - - - - - - -
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
// __
#define FLNMLEN 13 // characters needed for file name stringing
#define FFLNML 256 // Offset and Segment Info file names length
#define ASCIIVOL 256 // ASCII Volume
#define SCANBFFR 128 // scan buffer
#define BLKLNTGH 8192 // maximal buffer load (cluster size)
#define NUMBSNM 32 // Numeric Base used for File Naming
#define TSTTP 12 // number of test files
#define UTTPLEN sizeof(unsigned int) // sizeof(unsigned int) for writing/reading
#define DEBUG_SPOT "\n File=%s, Line#=%d", __FILE__ , __LINE__
struct Chrs{
char azFFlNm[FLNMLEN]; // offset file Name
unsigned int utTmsFnd; // times character has been found
} chr[ASCIIVOL];
// __ functions signatures
void initChrArs();
unsigned int existFl(char* azIsFlThr);
void scanFl(char* azPthFlNm);
unsigned int checkFl(char* azPthFlNm);
// __
int main(void){
unsigned int i, utStrt, utTp;
unsigned long ulTtlBytes;
double elapsed_time;
time_t tm_strt, tm_end, tm_strtAllFls, tm_endAllFls;
char* azFls[TSTTP] = {"test00.txt","test04.txt","test06.txt","getty11.txt","USACONST.TXT","alice30h.htm","bible11.txt","winzip80.exe","1donq10.txt","cide.s.txt","j2sdk-1_4_0-beta-win.exe","0xhgp10.txt"};
char azPthFlNm[FFLNML];
char azDir[FFLNML];
FILE* TXTFL;
// __ Input Directory, Files and char Buffer
strcpy(azDir,"C:/CML/TestFiles/");
// __ Log file
time( &tm_strt );
utStrt = 0; utTp = TSTTP; ulTtlBytes = 0;
time( &tm_strtAllFls );
// __ array of chars
for(i=0; (i < ASCIIVOL); ++i){ itoa(i, chr[i].azFFlNm, NUMBSNM); strcat(chr[i].azFFlNm, ".bin"); }// i
time( &tm_strt);
// __
for(i=utStrt; (i < utTp); ++i){
strcpy(azPthFlNm, azDir); strcat(azPthFlNm, azFls[i]);
if(existFl(azPthFlNm)){
fprintf(stderr, "\n[ %u, %u] Checking: %s", (i - utStrt), (utTp - utStrt - 1), azPthFlNm);
scanFl(azPthFlNm);
checkFl(azPthFlNm);
// __ File Length
TXTFL = fopen(azPthFlNm, "rb"); fseek(TXTFL, 0, SEEK_END); ulTtlBytes += ftell(TXTFL);
fclose(TXTFL);
}
time( &tm_end);
elapsed_time = difftime( tm_end, tm_strt);
fprintf(stderr, "\n %u seconds to process file ", (unsigned int)elapsed_time);
tm_strt = tm_end;
}
initChrArs();
// __
time( &tm_endAllFls);
elapsed_time = difftime( tm_endAllFls, tm_strtAllFls);
fprintf(stderr, "\n Program takes %f seconds to process %u bytes in all %u files", elapsed_time, ulTtlBytes, (utTp - utStrt));
if(elapsed_time > 0){
fprintf(stderr, "\n, %f (p/s).", (ulTtlBytes/elapsed_time));
}
// __
return(0);
}// main
// __ function bodies
// __
void initChrArs(){
unsigned int i;
for(i=0; (i < ASCIIVOL); ++i){
chr[i].utTmsFnd = 0;
if(existFl(chr[i].azFFlNm)){
if(remove(chr[i].azFFlNm) == -1){ fprintf(stderr, "\n Cannot remove working file %s", chr[i].azFFlNm); fprintf(stderr, DEBUG_SPOT); exit(1); }
}
}// i
}
// __
unsigned int existFl(char* azIsFlThr){
unsigned int IsExistFl = 1;
FILE* FL;
if((FL = fopen(azIsFlThr, "rb"))==NULL){ IsExistFl = 0; }
else{ fclose(FL); }
return(IsExistFl);
}
// __
void scanFl(char* azPthFlNm){
unsigned int i, j, utCurFlLngth, utRdByts, utFfst;
unsigned int utIxChrAr, utNumBytes;
// __ DEBUG
unsigned int utChrFndCnt[ASCIIVOL];
// __ heap based arrays
unsigned int utFAB[ASCIIVOL][SCANBFFR]; // Offset array buffer
unsigned int utFABCnt[ASCIIVOL]; // Offset array buffer count
unsigned char ucBytBffr[BLKLNTGH]; // Buffer for read bytes
FILE* OFL;
FILE* TXTFL = fopen(azPthFlNm, "rb");
// __ File Length
fseek(TXTFL, 0, SEEK_END); utCurFlLngth = ftell(TXTFL); fseek(TXTFL, 0, SEEK_SET);
// __ initializing
initChrArs();
for(i = 0; (i < ASCIIVOL); ++i)utChrFndCnt[i] = 0;
for(i = 0; (i < ASCIIVOL); ++i)utFABCnt[i] = 0;
utFfst = 0;
// __
for(i = 0; (i < ((utCurFlLngth)/BLKLNTGH + 1)); ++i){
utRdByts = (i < ((utCurFlLngth)/BLKLNTGH)) ? BLKLNTGH : (utCurFlLngth % BLKLNTGH);
if(utRdByts > 0){
// __
utRdByts = fread( ucBytBffr, sizeof( char ), utRdByts, TXTFL);
// __
for(j=0; (j < utRdByts); ++j){
utIxChrAr = (unsigned int)ucBytBffr[j];
if(utFABCnt[utIxChrAr] == SCANBFFR){
// __ appending values
OFL = fopen(chr[utIxChrAr].azFFlNm, "ab");
utNumBytes = fwrite( utFAB[utIxChrAr], UTTPLEN, utFABCnt[utIxChrAr], OFL);
fclose(OFL);
utFABCnt[utIxChrAr] = 0;
}
utFAB[utIxChrAr][utFABCnt[utIxChrAr]] = utFfst;
++utFABCnt[utIxChrAr];
++chr[utIxChrAr].utTmsFnd;
++utFfst;
// __
++utChrFndCnt[utIxChrAr];
}// j
}// (itRdByts > 0)
}//i
// __ saving remaining values in buffer
for(i = 0; (i < ASCIIVOL); ++i){
if(utFABCnt[i] > 0){
OFL = fopen(chr[i].azFFlNm, "ab");
utNumBytes = fwrite( utFAB[i], UTTPLEN, utFABCnt[i], OFL);
fclose(OFL);
}
}
fclose(TXTFL);
// __
if(utFfst != utCurFlLngth){ fprintf(stderr, "\n %u =(utFfst != utCurFlLngth)= %u OK!", utFfst, utCurFlLngth); }
else{ fprintf(stderr, "\n %u =(utFfst == utCurFlLngth)= %u OK!", utFfst, utCurFlLngth); }
}
// __ checkFl
unsigned int checkFl(char* azPthFlNm){
unsigned int j, k, utCurFlLngth;
unsigned int itIsFlOK=1;
unsigned int utFnd, utRdByts;
unsigned long ulFFlL;
int itK, itC;
unsigned int itL;
unsigned int utFfst;
FILE* TXTFL = fopen(azPthFlNm, "rb");
FILE* FDStrm;
unsigned long ulBytsCnt;
fprintf(stderr, "\n Testing %s", azPthFlNm);
// __ File Length
fseek(TXTFL, 0, SEEK_END); utCurFlLngth = ftell(TXTFL); fseek(TXTFL, 0, SEEK_SET);
// __ Footing of chr[*].utTmsFnd
ulBytsCnt = 0;
for(j=0; (j < ASCIIVOL); ++j){
if(chr[j].utTmsFnd > 0){ ulBytsCnt += (unsigned long)chr[j].utTmsFnd; }
}// j
// __
if(ulBytsCnt != utCurFlLngth){
fprintf(stderr, "\n %u=(ulBytsCnt != utCurFlLngth)=%u OK!", ulBytsCnt, utCurFlLngth);
itIsFlOK = 0; goto LblEndcheckFl;
}
else{
fprintf(stderr, "\n %u=(ulBytsCnt == utCurFlLngth)=%u OK!", ulBytsCnt, utCurFlLngth);
}
// __ Checking offset diffs binary files lengths
ulBytsCnt = 0;
for(k=0; (k < ASCIIVOL); ++k){
utFnd = chr[k].utTmsFnd;
if(utFnd > 0){
FDStrm = fopen(chr[k].azFFlNm, "rb");
// __ File Length
fseek(FDStrm, 0, SEEK_END); ulFFlL = (unsigned long)ftell(FDStrm); fseek(FDStrm, 0, SEEK_SET);
if(utFnd != (unsigned int)(ulFFlL/4)){
fprintf(stderr, "\n k=%u, %u=(utFnd != (ulFFlL/4))=%u, %s\n", k, utFnd, (ulFFlL/4), chr[k].azFFlNm);
exit(1);
}
ulBytsCnt += ulFFlL;
fclose(FDStrm);
}// (utFnd > 0)
}// k
// __
if((ulBytsCnt/4) == utCurFlLngth){ fprintf(stderr, "\n All offset files lengths tested OK!"); }
else{ fprintf(stderr, "\n ~* all offset files DID NOT test OK!"); itIsFlOK = 0; goto LblEndcheckFl; }
// __ Checking all bytes
ulBytsCnt = 0;
for(itK=0; (itK < (int)ASCIIVOL); ++itK){
if(chr[itK].utTmsFnd > 0){
FDStrm = fopen(chr[itK].azFFlNm, "rb");
ulBytsCnt += (unsigned long)chr[itK].utTmsFnd;
for(itL=0; (itL < chr[itK].utTmsFnd) && (itIsFlOK == 1); ++itL){
utRdByts = fread( &utFfst, UTTPLEN, 1, FDStrm);
fseek(TXTFL, utFfst, SEEK_SET);
itC = getc(TXTFL);
if(itC != itK){
fprintf(stderr, "\n (itC=%d != %d=itK) %c, itL=%d, chr[itK].utTmsFnd=%u, utFfst=%u", itC, itK, (char)itK, itL, chr[itK].utTmsFnd, utFfst);
itIsFlOK = 0;
exit(1);
goto LblNxtLoop; // Check all of them temporarily
}// (itC != itK)
}// itL
LblNxtLoop:;
fclose(FDStrm);
}// (chr[itK].utTmsFnd > 0)
}// itK
fprintf(stderr, "\n ulBytsCnt=%u, utCurFlLngth=%u", ulBytsCnt, utCurFlLngth);
if(ulBytsCnt == utCurFlLngth){ fprintf(stderr, "\n All byte counts tested OK!"); }
else{ fprintf(stderr, "\n NOT all byte counts tested OK."); itIsFlOK = 0; }
fclose(TXTFL);
LblEndcheckFl:
return(itIsFlOK);
}
// - - - - - - - - - - - - - - - C++ version of program - - - - - - - - - - - - - - -
#include <iostream>
#include <string>
#include <fstream>
#include <ctime>
#include <cstdio>
#include <cstdlib>
using namespace std;
// __
const unsigned int FLNMLEN = 13; // characters needed for file name stringing
const unsigned int FLNMBF = 256; // file name buffer for input files. OS dependent
const unsigned int ASCIIVOL = 256; // ASCII Volume
const unsigned int SCANBFFR = 128; // scan buffer. # of values cached before batch saving onto offset files
const unsigned int BLKLNTGH = 8192; // maximal buffer for characters read at once (cluster size)
const unsigned int NUMBSNM = 32; // Numeric Base used for File Naming
const unsigned int TSTTP = 12; // number of test files
const unsigned int UTTPLEN = sizeof(unsigned int); // sizeof(unsigned int) for writing/reading
#define DEBUG_SPOT "\n File=" << __FILE__ << ", Line#=" << __LINE__ << endl
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
class FileChars{
private:
// __
struct Chrs{
char azFFlNm[FLNMLEN]; // offset file Name
unsigned int utTmsFnd; // times character has been found
} chr[ASCIIVOL];
// __
char* azPthFlNm; // Current File Name
unsigned int utCurFlLngth; // Current File Length
// __
unsigned int existFl(const char* azIsFlThr){
unsigned int IsExistFl = 1;
ifstream ifFDStrm; ifFDStrm.open(azIsFlThr, ios::in|ios::binary);
if(ifFDStrm.fail()){ IsExistFl = 0; }
else{ ifFDStrm.close(); }
return(IsExistFl);
}
// __
void initChrArs(){
unsigned int i;
for(i=0; (i < ASCIIVOL); ++i){
chr[i].utTmsFnd = 0;
if(existFl(chr[i].azFFlNm)){
if(remove(chr[i].azFFlNm) == -1){ cerr << " Cannot remove working file " << chr[i].azFFlNm << DEBUG_SPOT << endl; exit(1); }
}
}// i
}
// __
public:
// __
FileChars(){
unsigned int i;
for(i=0; (i < ASCIIVOL); ++i){ itoa(i, chr[i].azFFlNm, NUMBSNM); strcat(chr[i].azFFlNm, ".bin"); }
}// FileChars
// __
~FileChars(){ initChrArs(); }
// __
void setFl(char* azPthFlNm){ this->azPthFlNm = azPthFlNm; }
// __
unsigned int getCurFlLngth(){ return(this->utCurFlLngth); }
// __
void scanFl(){
ifstream ifTxtStrm;
ifTxtStrm.open(this->azPthFlNm, ios::in|ios::binary);
// __ File Length
ifTxtStrm.seekg(0, ios::end); this->utCurFlLngth = (unsigned int)ifTxtStrm.tellg(); ifTxtStrm.seekg(0, ios::beg);
// __
if(!ifTxtStrm.fail()){
ofstream OStrm;
unsigned int i, j, utRdByts, utFfst = 0;
unsigned int utIxChrAr;
// __ DEBUG
ifstream IStrm;
unsigned int utChrFndCnt[ASCIIVOL];
for(i = 0; (i < ASCIIVOL); ++i)utChrFndCnt[i] = 0;
// __ heap based arrays
unsigned int** utFA; // Offset array buffer
utFA = new unsigned int*[ASCIIVOL];
for(i = 0; (i < ASCIIVOL); ++i){ utFA[i] = new unsigned int[SCANBFFR]; }
unsigned int* utFABCnt = new unsigned int[ASCIIVOL]; // Offset array buffer count
unsigned char* ucBytBffr = new unsigned char[BLKLNTGH]; // Buffer for read bytes
// __ initializing
for(i = 0; (i < ASCIIVOL); ++i)utFABCnt[i] = 0;
utFfst = 0;
initChrArs();
// __
for(i = 0; (i < ((utCurFlLngth)/BLKLNTGH + 1)); ++i){
utRdByts = (i < ((utCurFlLngth)/BLKLNTGH)) ? BLKLNTGH : (utCurFlLngth % BLKLNTGH);
if(utRdByts > 0){
ifTxtStrm.read(reinterpret_cast<char*> (ucBytBffr), utRdByts);
for(j=0; (j < utRdByts); ++j){
utIxChrAr = (unsigned int)ucBytBffr[j];
if(utFABCnt[utIxChrAr] == SCANBFFR){
OStrm.open(chr[utIxChrAr].azFFlNm, ios::out|ios::binary|ios::app);
OStrm.write(reinterpret_cast<char*> (utFA[utIxChrAr]), UTTPLEN*SCANBFFR);
OStrm.close();
utFABCnt[utIxChrAr] = 0;
}
utFA[utIxChrAr][utFABCnt[utIxChrAr]] = utFfst;
++utFABCnt[utIxChrAr];
++chr[utIxChrAr].utTmsFnd;
++utFfst;
// __
++utChrFndCnt[utIxChrAr];
}// j
}// (itRdByts > 0)
}//i
// __ saving remaining values in buffer
for(i = 0; (i < ASCIIVOL); ++i){
if(utFABCnt[i] > 0){
OStrm.open(chr[i].azFFlNm, ios::out|ios::binary|ios::app);
if(!OStrm.fail()){ OStrm.write(reinterpret_cast<char*> (utFA[i]), UTTPLEN*utFABCnt[i]); OStrm.close(); }
else{ cerr << " OStrm.fail()=" << OStrm.fail() << endl; exit(1); }
}
}
ifTxtStrm.close();
// __ Cleaning after/deleting arrays
delete[] ucBytBffr;
delete[] utFABCnt;
// __ deleting two dimensional array
for(i = 0; (i < ASCIIVOL); ++i)delete[] utFA[i]; // deleting second 'unsigned int[SCANBFFR]' dimension
delete[] utFA;
if(utFfst != utCurFlLngth){
cerr << utFfst << " (utFfst != utCurFlLngth) " << utCurFlLngth << endl;
}
else{ cerr << utFfst << " (utFfst == utCurFlLngth) " << utCurFlLngth << " OK!" << endl; }
}
else{ cerr << " Could not open file: " << azPthFlNm << endl; }
}// scanFl
// __ checkFl
unsigned int checkFl(){
unsigned int j, k;
unsigned int itIsFlOK=1;
ifstream ifTxtStrm, ifFDStrm;
unsigned long ulBytsCnt;
cerr << " Testing " << azPthFlNm << endl;
ifTxtStrm.open(this->azPthFlNm, ios::in|ios::binary);
if(!ifTxtStrm.fail()){
// __ File Length
ifTxtStrm.seekg(0, ios::end);
unsigned int utCurFlLngth = (unsigned int)ifTxtStrm.tellg();
ifTxtStrm.seekg(0, ios::beg);
// __ Footing of chr[*].utTmsFnd
ulBytsCnt = 0;
for(j=0; (j < ASCIIVOL); ++j){
if(chr[j].utTmsFnd > 0){ ulBytsCnt += (unsigned long)chr[j].utTmsFnd; }
}// j
// __
if(ulBytsCnt != utCurFlLngth){
cerr << ulBytsCnt << "= (ulBytsCnt != utCurFlLngth) =" << utCurFlLngth << endl; itIsFlOK = 0; exit(1);
goto LblEndcheckFl;
}
else{ cerr << ulBytsCnt << "= (ulBytsCnt == utCurFlLngth) =" << utCurFlLngth << ", OK!" << endl; }
// __ Checking offset diffs binary files lengths
ulBytsCnt = 0;
unsigned int utFnd;
unsigned long ulFFlL;
for(k=0; (k < ASCIIVOL); ++k){
utFnd = chr[k].utTmsFnd;
if(utFnd > 0){
ifFDStrm.open(chr[k].azFFlNm,ios::in|ios::binary);
if(!ifFDStrm.fail()){
ifFDStrm.seekg(0, ios::end); ulFFlL = (unsigned long)ifFDStrm.tellg();
if(utFnd != (unsigned int)(ulFFlL/4)){
cerr << "(" << k << "," << (unsigned char)k << "), utFnd=" << utFnd << " != " << (ulFFlL/4) << "=(ulFFlL/4)" << endl;
}
ulBytsCnt += ulFFlL;
ifFDStrm.close();
}
}// (utFnd > 0)
}// k
// __
if((ulBytsCnt/4) == utCurFlLngth){ cerr << " All offset files lengths tested OK!" << endl; }
else{ cerr << " ~* all offset files DID NOT test OK!" << endl; }
// __ Checking all bytes
int itK, itC;
unsigned int itL;
unsigned int utFfst;
ulBytsCnt = 0;
for(itK=0; (itK < (int)ASCIIVOL); ++itK){
if(chr[itK].utTmsFnd > 0){
ifFDStrm.open(chr[itK].azFFlNm, ios::in|ios::binary);
ulBytsCnt += (unsigned long)chr[itK].utTmsFnd;
for(itL=0; (itL < chr[itK].utTmsFnd) && (itIsFlOK == 1); ++itL){
ifFDStrm.read(reinterpret_cast<char*> (&utFfst), UTTPLEN);
ifTxtStrm.seekg(utFfst);
itC = ifTxtStrm.get();
if(itC != itK){
cerr << "(itC=" << itC << "!=" << itK << "=itK)" << " itL=" << itL << "," << chr[itK].utTmsFnd << ", utFfst=" << utFfst << ", itC=" << itC << ", itK=" << itK << endl;
exit(1);
goto LblNxtLoop; // Check all of them temporarily
}// (itC != itK)
}// itL
LblNxtLoop:;
ifFDStrm.close();
}// (chr[itK].utTmsFnd > 0)
}// itK
cerr << " ulBytsCnt=" << ulBytsCnt << ", utCurFlLngth=" << utCurFlLngth << endl;
if(ulBytsCnt == utCurFlLngth){ cerr << " All bytes offsets tested OK!" << endl; }
else{ cerr << " NOT all bytes offsets tested OK." << endl; itIsFlOK = 0; }
ifTxtStrm.close();
}
else{ cerr << " Could not open file" << azPthFlNm << endl; itIsFlOK = 0; }
LblEndcheckFl:
return(itIsFlOK);
}
};
int main(void){
unsigned int i, utStrt, utTp;
unsigned long ulTtlBytes;
double elapsed_time;
time_t tm_strt, tm_end, tm_strtAllFls, tm_endAllFls;
// __ Input Directory, Files and char Buffer
char azDir[FLNMBF]; strcpy(azDir,"C:/CML/TestFiles/");
const char* azFls[TSTTP] = {"test00.txt","test04.txt","test06.txt","getty11.txt","USACONST.TXT","alice30h.htm","bible11.txt","winzip80.exe","1donq10.txt","cide.s.txt","j2sdk-1_4_0-beta-win.exe","0xhgp10.txt"};
char azPthFlNm[FLNMBF];
// __ Log file
time( &tm_strt );
utStrt = 0; utTp = TSTTP; ulTtlBytes = 0;
time( &tm_strtAllFls );
FileChars* FlChrs = new FileChars();
time( &tm_strt);
for(i=utStrt; (i < utTp); ++i){
strcpy(azPthFlNm, azDir); strcat(azPthFlNm, azFls[i]);
cerr << "[" << i << "," << (utTp - utStrt - 1) << "] Checking: " << azPthFlNm << endl;
FlChrs->setFl(azPthFlNm);
FlChrs->scanFl();
FlChrs->checkFl();
ulTtlBytes += FlChrs->getCurFlLngth();
time( &tm_end);
elapsed_time = difftime( tm_end, tm_strt);
cerr << elapsed_time << " seconds to process file " << endl;
tm_strt = tm_end;
}
delete FlChrs;
// __
time( &tm_endAllFls);
elapsed_time = difftime( tm_endAllFls, tm_strtAllFls);
cerr << " Program takes " << elapsed_time << " seconds to process " << ulTtlBytes << " bytes in all " << (utTp - utStrt) << " files";
if(elapsed_time > 0){
cerr << ", " << (ulTtlBytes/elapsed_time) << " (p/s)." << endl;
}
// __
return(0);
}// main
- Raw text -