Process size, part 2.


R. Clayton (rclayton@monmouth.edu)
(no date)


In a previous message I argued that executable-file size is not a good estimate
of process size because the file may (probably does) contain overhead that
isn't used during execution. In that message I also suggested that
text+data+bss might be a better estimate of process size. In this message I
take up that idea.

Is text+data+bss a good estimate of process size? At first, the answer seems
to be "no". To understand why, remember what the program was

  $ cat t.c
  #include <stdio.h>

  int main() {
    printf("hello world!\n");
    return 0;
    }

  $

Where is printf()? Is it included as part of the executable? The easiest way
to answer that question (*) is to take it out and compare sizes:

  cl cat t.c
  #include <stdio.h>

  int main() {
    "hello world!\n";
    return 0;
    }

  cl gcc -o t t.c

  cl size t
     text data bss dec hex filename
     1693 264 32 1989 7c5 t

  cl

The text (code) size decreased by 1778 - 1693 = 85 bytes. Because it's highly
unlikely that printf() can be implemented in just 85 bytes, it seems more
likely that printf() has been implemented somewhere else and needs to be linked
into the hello-world executable at run-time. This trick of linking in code at
run-time is known as dynamic binding (which, in my opinion, is an abuse of
terminology), or dynamic linking, or dynamic loading, or (ha!) dynamic
linking-loading.

Dynamic loading suggests that text+data+bss is, at best, a lower-bound estimate
on process size. It also suggests a way to get a more accurate estimate of
process size. For most compilers, dynamic loading is the default; however,
most compilers allow static loading as an option. Static loading puts
everything into the executable at compile time, so there's no need to do any
dynamic loading.

Compiling hello world with static linking gives

  $ cat t.c
  #include <stdio.h>

  int main() {
    printf("hello world!\n");
    return 0;
    }

  $ gcc -o t -static t.c

  $ ls -l t
  -rwx------ 1 rclayton faculty 376484 Nov 12 11:28 t

  $ size t
     text data bss dec hex filename
   220821 6361 3000 230182 38326 t

  $

The hello-world executable has balloned to almost a quarter meg (but the
overhead is a constant factor; it looks large here because the hello-world
program is small. A larger program (making the same calls) would incur the
same absolute overhead but the increase would be relatively smaller.)

Is the text+data+bss on a statically linked executable a good estimator of
process size (or at least a better estimator than dynamic-linked
text+data+bss)? No, for two reasons. First, in addition to supporting dynamic
loading, most modern OSs support shared libraries, which means that code and
data in the libraries are not part of a process's address space and so don't
contribute to the process's size (or don't contribute very much). The
statically-linked executable size may over-estimate process size.

The second problem with the statically-linked executable size is the "static"
part: it doesn't account for size changes during process execution behavior.
For example, if a process opens and reads in a million-byte file, you would
expect the process size to grow by a million bytes.

We can get a crude estimate of actual process size by forcing a core dump
during execution; the core file produced is a representation of all the
interesting parts of the process at the time of the core dump.

  $ cat t.c
  #include <stdio.h>

  int main() {
    printf("hello world!\n");
    abort();
    return 0;
    }

  $ gcc -o t t.c

  $ size t
     text data bss dec hex filename
     1824 264 32 2120 848 t

  $ ./t
  hello world!
  Abort(coredump)

  $ ls -l core
  -rw------- 1 rclayton faculty 88576 Nov 12 12:03 core

  $

The static executable was a quarter meg; the core-file image is much less than
that.

Unfortunately, core-file estimates aren't too good either. Although the core
file contains all the important parts of the process, it may also contain other
things, such as kernel data structures, associated with the process but not
part of the process proper. These extra bits may over-estimate process size.
The core-file format may also use tricks, such as run-length encoding, to
reduce the size of the dumped data, which could under-estimate process size.
Finally, although the core-dump interface may be standard (for example, the elf
(extended linking format) spec), the internals are not, making it difficult to
figure out what's going on in a core-dump file, and requiring that you figure
out what's going on for every different system you examine (and perhaps for
different revisions of the same system).

(*) The easiest way to answer that question

Not really. An even easier way to answer that question is to run the
executable through nm (do "man nm" for details) and grep for printf:

  $ cat t.c
  #include <stdio.h>

  int main() {
    printf("hello world!\n");
    return 0;
    }

  $ gcc -o t t.c

  $ nm t | grep print
                   U printf

  $

printf comes up undefined (U) in t, which means that printf (its text+data+bss)
is elsewhere. Grepping for ' U ' shows what else is missing from t

  $ nm t | grep ' U '
                   U _exit
                   U abort
                   U atexit
                   U exit
                   U printf

  $



This archive was generated by hypermail 2.0b3 on Fri Dec 03 2004 - 12:00:06 EST