I'm working on a similar problem at the moment.
I need to log debug statements with timestamp, filename, line number etc on an embedded system. We already have a logger in place but when I turn the knob to 'full logging', it eats all our proc cycles and puts our system in dire states, states that no computing device should ever have to experience.
Someone did say "You cannot measure/observe something without changing that which you are measuring/observing."
So I'm changing things to improve performance. The current state of things is that Im 2x faster than the original function call (the bottleneck in that logging system is not in the function call but in the log reader which is a separate executable, which I can discard if I write my own logging stack).
The interface I need to provide is something like- void log(int channel, char *filename, int lineno, format, ...). I need to append the channel name (which currently does a linear search within a list! For every single debug statement!) and timestamp including millisecond counter. Here are some of the things Im doing to make this faster-
- Stringify channel name so I can strcpy rather than search the list. define macro LOG(channel, ...etc) as log(#channel, ...etc). You can use memcpy if you fix the length of the string by defining LOG(channel, ...) log("...."#channel - sizeof("...."#channel) + 11) to get fixed 10 byte channel lengths
- Generate timestamp string a couple of times a second. You can use asctime or something. Then memcpy the fixed length string to every debug statement.
- If you want to generate the timestamp string in real time then a look up table with assignment (not memcpy!) is perfect. But that works only for 2 digit numbers and maybe for the year.
What about three digits (milliseconds) and five digits (lineno)? I dont like itoa and I dont like the custom itoa (digit = ((value /= value) % 10)
) either because divs and mods are slow. I wrote the functions below and later discovered that something similar is in the AMD optimization manual (in assembly) which gives me confidence that these are 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 the 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 */
}
Overall, my code is pretty fast now. The vsnprintf() I need to use takes about 91% of the time and the rest of my code takes only 9% (whereas the rest of the code i.e. except vsprintf() used to take 54% earlier)
Hope this helps somebody,
Pranav Swaroop