I have heard that scanf() "costs a lot", but now I can't find any information about that.
Yes, that's true. It is slower in many cases.
In particular, reading in a file byte by byte:
int chr;
// this is much slower than ...
while (1) {
if (fscanf(xf,"%c",&chr) != 1)
break;
}
// ... this
while (1) {
chr = fgetc(xf);
if (chr == EOF)
break;
}
Side note: Believe it or not, the above comes from a real world production grade program that I encountered [for loading firmware into an FPGA]. I fixed it by using fgetc
[and read
]. I reduced the running time from 15 minutes to 90 seconds
Additional question: could you recommend some sources for that type of information about how much a function can cost to the program?
You can learn about "big O" notation and analysis.
But, you can always write a benchmark program. Here's one I created that can help you with creating your own.
In this particular case, the important thing is to remove the overhead of the I/O from the timings. So, this program creates random lines in a buffer and does either strtod
or sscanf
on the buffer.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define TSTMAX 100000
#define FNCMAX 2
typedef long long tsc_t;
tsc_t
tscget(void)
{
struct timespec ts;
tsc_t tsc;
clock_gettime(CLOCK_MONOTONIC,&ts);
tsc = ts.tv_sec;
tsc *= 1000000000;
tsc += ts.tv_nsec;
return tsc;
}
double
tscsec(tsc_t tsc)
{
double sec;
sec = tsc;
sec /= 1e9;
return sec;
}
tsc_t elap[FNCMAX][TSTMAX];
void
useit(double *arr)
{
}
void
doscanf1(char *buf)
{
double arr[4];
sscanf(buf,"%lf %lf %lf %lf",&arr[0],&arr[1],&arr[2],&arr[3]);
useit(arr);
}
void
doscanf2(char *buf)
{
double arr[4];
sscanf(buf,"%lf %lf %lf %lf",&arr[0],&arr[1],&arr[2],&arr[3]);
useit(arr);
}
void
dotok(char *buf)
{
double arr[4];
for (int idx = 0; idx < 4; ++idx)
arr[idx] = strtod(buf,&buf);
useit(arr);
}
void
dofnc(int tstidx,int fncidx,const char *src)
{
char buf[1000];
tsc_t tscbeg;
tsc_t tscend;
strcpy(buf,src);
tscbeg = tscget();
switch (fncidx) {
case 0:
dotok(buf);
break;
case 1:
doscanf1(buf);
break;
case 2:
doscanf2(buf);
break;
}
tscend = tscget();
tscend -= tscbeg;
elap[fncidx][tstidx] = tscend;
}
void
dotest(int tstidx)
{
char *bp;
char buf[1000];
bp = buf;
for (int idx = 0; idx < 4; ++idx)
bp += sprintf(bp," %.8g",drand48());
dofnc(tstidx,0,buf);
dofnc(tstidx,1,buf);
}
int
cmpfnc(const void *lhs,const void *rhs)
{
tsc_t dif = *(const tsc_t *) lhs - *(const tsc_t *) rhs;
int cmpflg;
do {
cmpflg = -1;
if (dif < 0)
break;
cmpflg = 1;
if (dif > 0)
break;
cmpflg = 0;
} while (0);
return cmpflg;
}
int
main(void)
{
tsc_t avg[FNCMAX] = { 0 };
for (int tstidx = 0; tstidx < TSTMAX; ++tstidx)
dotest(tstidx);
for (int fncidx = 0; fncidx < FNCMAX; ++fncidx)
qsort(&elap[fncidx][0],TSTMAX,sizeof(tsc_t),cmpfnc);
for (int tstidx = 0; tstidx < TSTMAX; ++tstidx) {
for (int fncidx = 0; fncidx < FNCMAX; ++fncidx) {
tsc_t tsc = elap[fncidx][tstidx];
printf(" %.9f",tscsec(tsc));
avg[fncidx] += tsc;
}
printf(" %d\n",tstidx);
}
for (int fncidx = 0; fncidx < FNCMAX; ++fncidx) {
tsc_t tsc = avg[fncidx];
printf("TOT:%.9f AVG:%.9f\n",tscsec(tsc),tscsec(tsc) / TSTMAX);
}
}
You can run the above program. Here is the summary from a run on my system:
TOT:0.090690979 AVG:0.000000907
TOT:0.157146016 AVG:0.000001571
So, using strtod
is almost 1.75x faster than sscanf