cost of fprintf

谁说我不能喝 提交于 2019-12-01 08:10:50

问题


I am developing an embedded application in C++ for a platform with limited code/data RAM, but rather unlimited RAM for filesystem usage.

While looking for reducing the code size, I realized that excluding fprintf() lines contributed a lot to the size of generated code.

My questions are : 1. Why is the cost of fprintf so high ? 2. If I exclude the fprintf functionality, what would be the alternative to generate log files describing the occurances through the application run ?


回答1:


The answer to your first question depends on the compiler that you are using; you can only get a definitive answer by examining your compiler. As GrahamS pointed out, formatter implementation can be complicated.

Try using fputs instead of fprintf to avoid the formatter.




回答2:


In embedded systems, printf can sometimes drag in all the floating point support for format strings like %f.

More intelligent environments will make the floating point options for printf an optional thing.

But even for integers, there's a lot of general purpose code in printf and you may find it's more compact to write your own routines, tailored to your specific needs, like:

outInt (char *buff, int intVal);
outChr (char *buff, char chVal);
outStr (char *buff, char *strVal);

and so on, for writing to buffers, then outBuff (char *buff) for sending it to a file or standard output.


For example, if you control the data being used (no string overflow, 16-bit twos complement integers and such), you can use the following functions:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void outChr (char *buff, char chVal) {
    *buff++ = chVal;
    *buff = '\0';
}

void outStr (char *buff, char *strVal) {
    strcpy (buff, strVal);
}

 

void outInt (char *buff, int intVal) {
    int divisor = 10000, printing = 0;

    // Special cases.

    if (intVal == -32768) { outStr (buff, "-32768"); return; }
    if (intVal ==      0) { outChr (buff,      '0'); return; }

    // Handle negatives.

    if (intVal < 0) { outChr (buff++, '-'); intVal = -intVal; }

    // Handle non-zero positives <= 32767.

    while (divisor > 0) {
        if ((intVal >= divisor) || printing) {
            outChr (buff++, "0123456789"[intVal/divisor]);
            printing = 1;
        }
        intVal = intVal % divisor;
        divisor /= 10;
    }
}

 

int main (int argc, char *argv[]) {
    char buff[1000];
    int i;
    for (i = 1; i < argc; i++) {
        outInt (buff, atoi (argv[i]));
        printf ("[%s] -> [%s]\n", argv[i], buff);
    }
    return 0;
}

Running this with:

pax$ tstprg 32767 10000 9999 10 9 1 0 -1 -9 -10 -99 -10000 -32767 -32768

outputs:

[32767] -> [32767]
[10000] -> [10000]
[9999] -> [9999]
[10] -> [10]
[9] -> [9]
[1] -> [1]
[0] -> [0]
[-1] -> [-1]
[-9] -> [-9]
[-10] -> [-10]
[-99] -> [-99]
[-10000] -> [-10000]
[-32767] -> [-32767]
[-32768] -> [-32768]

These functions should be relatively small in size since they're targeted to specific needs rather than the far more general printf family.




回答3:


It takes a reasonable amount of code to provide the full ANSI compliant printf functionality.

Some embedded environments offer several different versions of printf that are considerably smaller, as they only offer selected functionality.

For example, the IAR C/C++ Compiler for MSP430 (PDF), offers Tiny, Small, Large and Full implementations of the printf formatter, with the Tiny version only supporting the basic specifiers (c, d, i, o, p, s, u, X, x, and %) with no support for multibytes, floats, length modifiers, width and precision.

If your environment offers this choice then select the version of printf (and scanf) that matches your needs and be aware of the limitations.

If your environment does not offer this choice then take a look at the various "tiny" alternative printf implementations available (such as this one from Kustaa Nyholm of SpareTimeLabs).




回答4:


I can think of three scenarios:

  1. Each time you remove a fprintf line the code size drops slightly, and when you remove the very last fprint it also drops slightly.
  2. When you remove the very last fprint the code size drops significantly.
  3. Every time you remove one instance of fprint the code size drops significantly.

In scenario 1, it isn't fprintf that is the culprit, but rather the string literals that you are passing to it. You need to get these strings out of your code, either by using very short, terse messages, or by storing the strings in a file and referencing them by some form of ID within the code (although this incurs performance hits)

In scenario 2, fprintf is (probably) the main culprit. It is quite a complex function that is capable of formatting all kinds of data types in all kinds of ways - so it takes quite a bit of code space. When you remove the last use of it the linker will eliminate it from the final binaries, making them smaller. Try using std::ofstream instead. If you only ever insert (for example) ints and strings to your output file, then only the code for handling ints and strings is linked in.

Scenario 3 is very unlikely - and would probably indicate that fprintf is being inlined wherever you use it.

Hope this helps



来源:https://stackoverflow.com/questions/5165654/cost-of-fprintf

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!