How can I improve / replace sprintf, which I rated is a performance access point?

Through profiling, I found that sprintf takes a lot of time here. Is there a more efficient alternative that still processes leading zeros in y / m / dh / m / s fields?

SYSTEMTIME sysTime; GetLocalTime( &sysTime ); char buf[80]; for (int i = 0; i < 100000; i++) { sprintf(buf, "%4d-%02d-%02d %02d:%02d:%02d", sysTime.wYear, sysTime.wMonth, sysTime.wDay, sysTime.wHour, sysTime.wMinute, sysTime.wSecond); } 

Note. The OP indicates in the comments that this is a stripped-down example. The "real" loop contains additional code that uses variable time values ​​from the database. Profiling revealed sprintf() as a criminal.

+6
c ++ performance c printf strftime
source share
11 answers

If you write your own function to complete the task, the string search table 0 .. 61 will avoid the need to do any arithmetic for everything except the year.

edit: Note that in order to deal with the seconds of the jump (and to match strftime() ), you must be able to print the values ​​of seconds 60 and 61.

 char LeadingZeroIntegerValues[62][] = { "00", "01", "02", ... "59", "60", "61" }; 

Alternatively, what about strftime() ? I have no idea how performance is compared (it is possible that you just call sprintf ()), but it's worth a look (and it could do the above search).

+18
source share

You can try to fill each char in the output in turn.

 buf[0] = (sysTime.wYear / 1000) % 10 + '0' ; buf[1] = (sysTime.wYear / 100) % 10 + '0'; buf[2] = (sysTime.wYear / 10) % 10 + '0'; buf[3] = sysTime.wYear % 10 + '0'; buf[4] = '-'; 

... etc.

Not really, but you get the picture. If nothing else, this can help explain why sprintf will not be so fast.

OTOH, maybe you can cache the latest result. Thus, you will only need to generate every second.

+6
source share

Printf needs to deal with many different formats. Of course, you can grab the source for printf and use it as a basis for folding your own version, which deals with the sysTime structure. Thus, you pass one argument, and it does exactly the work that needs to be done, and nothing more.

+6
source share

What do you mean by "long" time - since sprintf() is the only operator in your cycle, and the "plumbing" of the cycle (increment, comparison) is negligible, sprintf() should be time consuming.

Remember the old joke about a man who once lost an engagement ring on Third Street, but looked for it on 5th because the light was brighter there? You created an example that aims to "prove" your assumption that sprintf() inefficient.

Your results will be more accurate if you create the "actual" code containing sprintf() , in addition to all the other functions and algorithms that you use. Alternatively, try writing your own version that will handle the required null conversion.

You may be surprised at the results.

+2
source share

Looks like Jaywalker offers a very similar method (beat me in less than an hour).

In addition to the search table method already proposed (n2s [] below), how about creating a format buffer so that regular sprintf is less intensive? The code below will only have to fill in minutes and seconds every time through the cycle, unless the year / month / day / hour has changed. Obviously, if any of them has changed, you make another sprintf hit, but in general it can be no more than what you are currently observing (in combination with array search).

 static char fbuf[80]; static SYSTEMTIME lastSysTime = {0, ..., 0}; // initialize to all zeros. for (int i = 0; i < 100000; i++) { if ((lastSysTime.wHour != sysTime.wHour) || (lastSysTime.wDay != sysTime.wDay) || (lastSysTime.wMonth != sysTime.wMonth) || (lastSysTime.wYear != sysTime.wYear)) { sprintf(fbuf, "%4d-%02s-%02s %02s:%%02s:%%02s", sysTime.wYear, n2s[sysTime.wMonth], n2s[sysTime.wDay], n2s[sysTime.wHour]); lastSysTime.wHour = sysTime.wHour; lastSysTime.wDay = sysTime.wDay; lastSysTime.wMonth = sysTime.wMonth; lastSysTime.wYear = sysTime.wYear; } sprintf(buf, fbuf, n2s[sysTime.wMinute], n2s[sysTime.wSecond]); } 
+2
source share

You are likely to achieve manual performance improvements to print the numbers in the returned buf, since you could avoid parsing the format string again and not have to deal with many more complex cases of sprintf handle, I do not recommend doing this.

I would recommend trying to figure out if you can somehow reduce the amount needed to create these lines, they are optional several times, can they be cached, etc.

+1
source share

How about caching results? Isn't that an opportunity? Given that this particular sprintf () call is made too often in your code, I assume that between most of these consecutive calls, the year, month, and day do not change.

So we can implement something like the following. Declare the old and current SYSTEMTIME structure:

 SYSTEMTIME sysTime, oldSysTime; 

In addition, declare separate parts for storing date and time:

 char datePart[80]; char timePart[80]; 

The first time you will need to populate both sysTime, oldSysTime, and datePart and timePart. But subsequent sprintf () can be done pretty quickly, as follows:

  sprintf (timePart, "% 02d:% 02d:% 02d", sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
 if (oldSysTime.wYear == sysTime.wYear && 
   oldSysTime.wMonth == sysTime.wMonth &&
   oldSysTime.wDay == sysTime.wDay) 
   {
      // we can reuse the date part
      strcpy (buff, datePart);
      strcat (buff, timePart);
   }
 else {
      // we need to regenerate the date part as well
      sprintf (datePart, "% 4d-% 02d-% 02d", sysTime.wYear, sysTime.wMonth, sysTime.wDay);
      strcpy (buff, datePart);
      strcat (buff, timePart);
 }

 memcpy (& oldSysTime, & sysTime, sizeof (SYSTEMTIME));

The above code has some redundancy to make the code more understandable. You can easily decompose. You can speed things up even more if you know that even the hour and minutes will not change faster than your subprogram call.

+1
source share

I would do a few things ...

  • cache the current time, so you do not need to restore the time stamp every time
  • perform time conversion manually. The slowest part of printf -family functions is parsing a format string, and it would be foolish to devote loops to this parsing each time the loop is executed.
  • try using 2-byte lookup tables for all conversions ( { "00", "01", "02", ..., "99" } ). This is because you want to avoid modular arithmetic, and a 2-byte table means that you only need to use one module per year.
+1
source share

I am working on a similar problem at the moment.

I need to write debug statements with timestamp, file name, line number, etc. into the embedded system. We already have a registrar, but when I turn the knob to “complete registration”, it eats all our proc cycles and puts our system in terrible conditions, it says that no computing device should ever experience it.

Someone said: "You cannot measure / observe something without changing what you measure / observe."

So, I am changing things to improve performance. The current state of things is that Im 2x is faster than the original function call (the bottleneck in this registration system is not in the function call, but in the log reader, which is a separate executable file, which I can drop if I write my own stack logging).

The interface I need to provide is something like void log(int channel, char *filename, int lineno, format, ...) . I need to add a channel name (which is currently doing a linear search in the list! For each individual debug statement!) And a timestamp, including a millisecond counter. Here are some of the things I do to make it faster -

  • Building the channel name so that I can strcpy instead of searching the list. Define the LOG(channel, ...etc) macro LOG(channel, ...etc) as log(#channel, ...etc) . You can use memcpy if you correct the line length by specifying LOG(channel, ...) log("...."#channel - sizeof("...."#channel) + *11*) to get fixed byte length 10 bytes long
  • Create a timestamp line a couple of times per second. You can use asctime or something else. Then a memcpy string of fixed length for each debug statement.
  • If you want to generate a timestamp string in real time, then an assignment lookup table (rather than memcpy!) Is ideal. But this only works for two-digit numbers, and possibly for a year.
  • What about three digits (milliseconds) and five digits (lineno)? I don't like itoa, and I don't like custom itoa ( digit = ((value /= value) % 10) ), because divs and mods are slow. I wrote the functions below and later found that something similar is contained in the AMD Optimization Guide (in the assembly), which gives me confidence that we are talking about the fastest C implementations.

     void itoa03(char *string, unsigned int value) { *string++ = '0' + ((value = value * 2684355) >> 28); *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); *string++ = ' ';/* null terminate here if thats what you need */ } 

    Similarly, for line numbers

     void itoa05(char *string, unsigned int value) { *string++ = ' '; *string++ = '0' + ((value = value * 26844 + 12) >> 28); *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); *string++ = ' ';/* null terminate here if thats what you need */ } 

All in all, my code is pretty fast. vsnprintf() I need to use about 91% of the time, and the rest of my code takes only 9% (whereas the rest of the code, except vsprintf() , used to take 54%)

+1
source share

StringStream is an offer I received from Google.

http://bytes.com/forum/thread132583.html

0
source share

It's hard to imagine that you will beat sprintf when formatting integers. Are you sure sprintf is your problem?

0
source share

All Articles