C++ Code Size

I’d really like some tool (ideally, g++ based) that shows me what parts of compiled/linked code are generated from what parts of C++ source code. For instance, to see whether a particular template is being instantiated for hundreds of different types (fixable via a template specialization) or whether code is being inlined excessively, or whether particular functions are larger than expected.

libgtkmm-2.4 is a bit bigger than I’d like, even stripped, as are the gtkmm examples.

Updates

I forgot to mention that using -g -Os (optimize for size, which disables function-align among other things) has no noticeable effect – it seem to actually make the .so slightly bigger.)

One obvious thing that bothers me: nm shows that stuff in anonymous namespaces are exported. But we put private code in anonymous namespaces to prevent exactly that. I’d prefer to automatically export everything and have a way to exclude some things, rather than automatically not export everything (-fvisibility=hidden)and then explicitly export each thing, hoping I’ve tagged everything.

However, I doubt that exporting/not-exporting would make much difference to the code size. It would still be in the .so because it’s needed for implementation.

More Updates

There’s obviously lots of exerimentation yet to do. The anonymous namespaces really are exported (nm shows them even when using -D. Thanks James). I can hide the standalone functions by using the static keyword, but I don’t think that classes can use the static keyword to specify that they aren’t exported.
The output from nm –demangle -D libgtkmm-2.4.so.1.0.25 (for gtkmm 2.8) is online for anyone interested.

There obviously are thousands of little functions (class methods) in gtkmm, but it doesn’t feel right that the size of the library is comparable to that of the library that it wraps.

15 thoughts on “C++ Code Size

  1. Well, this may be relatively crude (and may not be quite what you’re after), but have you tried the following?

    nm –undefined-only –demangle objectfile.o

    With the –undefined-only option it will only print the symbols that are pulled in from linked libraries instead of all of the symbols that are defined in your code, i.e. it’ll show you all of the gtkmm symbols it links in (and of course you’ll want the –demangle option to make the C++ symbol names readable).

    So I guess you could write a simple test-case, compile it with g++, and then run nm on it to see what gets pulled in. Admittedly this doesn’t really allow you to see which pieces of code pulled in which symbols, but if the test cases were simple enough, it might not be too hard to tell.

  2. oops, it automatically converted my double-dashes for option names into a single long dash, but you get the idea…

  3. Hi Murray –

    There’s one thing that I’ve been able to use on smaller programs: Compile with ‘-gdwarf-2’ (the DWARF debugging format) and use dwarfdump to view all the debuggin info. It’s OO and template aware, as well as inlineing, size of functions, et cetera. It may a overwhelming with larger programs, but that’s why there’s grep.

    http://reality.sgiweb.org/davea/dwarf.html

  4. Have you looked at the results of readelf -S libtgtkmm.so ?
    See if any section is bigger than you expect.
    The relocations might be huge if you haven’t taken care to minimize th
    (use -fvisibility=hidden or the corresponding attribute)
    Exception handling tables might be big too.
    If you have a lot of small functions there might be an overhead from
    excessive alignment (see -falign-functions).

  5. namespace {
    int foo() { return 42; }
    }

    int bar()
    {
    return foo();
    }

    Here, even if ::foo() is not exported, and even if it has been inlined in ::bar(), its standalone definition is still there. It looks like gcc produces dead code…

  6. Don’t underestimate the effect of not exporting stuff.
    For the above example the difference

    gcc -O2 -shared -fpic t.cc
    mv a.out a.out.before
    gcc -O2 -shared -fpic t.cc -Wl,-version-script -Wl,/tmp/link.script
    size -f a.out*
    text data bss dec hex filename
    852 252 4 1108 454 a.out
    1206 256 4 1466 5ba a.out.before

    ls -l a.out*
    -rwx—— 1 b users 3964 Feb 15 09:15 a.out*
    -rwx—— 1 b users 4324 Feb 15 08:47 a.out.before*
    strip a.out*
    ls -l a.out*
    -rwx—— 1 dann users 2604 Feb 15 09:27 a.out*
    -rwx—— 1 dann users 2964 Feb 15 08:50 a.out.before*

    cat /tmp/link.script
    { global:
    bar;
    local: *;
    };

    Also, are you using exceptions? If not you will find that using -fno-exceptions has a huge impact on code size.
    Here’s an example for a random file in gtkmm:
    size -f /tmp/clipboard.o .libs/clipboard.o
    text data bss dec hex filename
    10605 256 73 10934 2ab6 /tmp/clipboard.o
    6060 252 73 6385 18f1 .libs/clipboard.o

    .libs/clipboard.o was compiled with -fno-exceptions

  7. Are you checking for exported symbols using “nm” or “nm -D”? If you don’t use the “-D” flag, you’ll be seeing symbols in the debug symbol table which is not used for dynamic linking. If the functions still show up with “nm -D”, then they’re being exported.

  8. James, even with -D the anonymous namespaces still show up. That may be normal – apparently they are exported as some kind of weak symbol, though I don’t know what that means.

  9. Are all the classes in the *mm/*/private/*.h headers accessible from gtkmm applications, or they are just library internals? If the later marking them as hidden would help a lot with code size.

  10. Hi Murray –

    In response to your email — blocks with tags DW_TAG_subprogram, if compiled (not inlined) will have two attributes DW_AT_highpc and DW_AT_lowpc, which are the begin/end addresses of the function. The difference of these addresses will be the code size of the function. Someone clever with perl or awk/grep should be able to automate that. But..if all you are interested in is the size of functions you should check out ‘readelf’:

    # readelf -s

    The size of the function is the 3rd column.

  11. > blocks with tags DW_TAG_subprogram, if compiled (not inlined) will have two attributes DW_AT_highpc and DW_AT_lowpc, which are the begin/end
    > addresses of the function.

    Hmm, I don’t see any DW_AT_highpc or DW_AT_lowpc strings in the whole dwarfdump output. The huge file is online at:
    http://www.murrayc.com/misc/ in the libgtkmm_dwarfdump_av_output.txt file. The lib, built with CXXFLAGS=”-gdwarf-2″ is there too.

  12. nm has a -S switch to make it show function sizes. Even handier, there is a –size-sort switch to show and sort by size.

Comments are closed.