4 ms·
Out of curiosity, I looked at a basic hello world in C, and compiled it naively using gcc on Ubuntu, by default the executable produced was 15960 bytes, but whe
by rpigab 3y ago
Out of curiosity, I looked at a basic hello world in C, and compiled it naively using gcc on Ubuntu, by default the executable produced was 15960 bytes, but when adding the -Os option (from the man page: "-Os Optimize for size") to enable many optimisation flags including code size, I was able to reduce it to 15968.
#include <stdio.h>
int main() { printf("Hello, World!"); return 0;}
Of course, If I had read more than 3 words in the man page, the answer was easy to understand: "Os Optimize for size. -Os enables all -O2 optimizations except those that often increase code size", so you can't really get lower size than the default using only the optimization system, there's also "-finline-functions" included in -Os, but it won't help you in a Hello World.
- flohofwoe 3y agozig cc -Os -target x86_64-linux-musl hello.c -o hello ...which basically calls Clang under the hood, but comes with out-of-the-box cross-compilation support for Linux and MUSL creates a 5136 bytes executable.
- lifthrasiir 3y agoThis program is not same to the Rust version. A more faithful version, assuming glibc, would look like this: #include <stdio.h> #include <stdlib.h> #include <execinfo.h> #include <errno.h> void print_backtrace(void) { void *traces[50]; char **symbols; int num_traces, i; num_traces = backtrace(traces, sizeof(traces) / sizeof(*traces)); strings = backtrace_symbols(traces, num_traces); if (!strings) return; for (i = 0; i < num_traces; ++i) { fprintf("%d: %s\n", i + 1, strings[i]); } free(strings); } int main(void) { static const char FMT[] = "Hello, World!\n"; static int EXPECTED = (int) (sizeof(FMT) - 1); int ret = printf(FMT); if (ret < 0) { fprintf(stderr, "printf failed: %s\n", strerror(errno)); print_backtrace(); return 1; } if (ret != EXPECTED) { fprintf(stderr, "printf failed: only %d characters were written\n", ret); print_backtrace(); return 1; } return 0; } While this is still substantially different (for example, Rust's I/O buffering is different from C), this should be enough to demonstrate that this comparison is very unfair.
- rfoo 3y agoBuilding your code (I fixed a few typos, strings -> symbols, fprintf(" -> fprintf(stderr, ") with gcc -s -Os -fuse-ld=lld a.c && ls -al a.out leads to a 5496 bytes ELF though. Which is not much larger than just printf("Hello World!\n"), see sibling comment. I think the point is C "cheated" by including a lot of goodness (format, backtrace etc) in the shared library so they does not have to be copied to each binary.
- lifthrasiir 3y agoThank you for the actual testing (and sorry for typos...). Yes, a C version is small because it dynamically links to libc.so in this case, and I meant to compare against a statically linked version. I primarily wrote this example as a response to the claim that an equivalent---it isn't---C code with musl is very small.
- pca006132 3y agoThe problem is musl does not support execinfo. For a simple hello world, I managed to statically link with musl to get something with 7888 bytes.
- rfoo 3y ago$ gcc -Os a.c && ls -al a.out && size -rwxr-xr-x 1 user user 15952 Jan 24 17:49 a.out text data bss dec hex filename 1316 584 8 1908 774 a.out $ gcc -s -Os a.c && ls -al a.out && size -rwxr-xr-x 1 user user 14472 Jan 24 17:50 a.out text data bss dec hex filename 1316 584 8 1908 774 a.out $ gcc -s -Os -fuse-ld=lld a.c && ls -al a.out && size -rwxr-xr-x 1 user user 4552 Jan 24 17:50 a.out text data bss dec hex filename 1199 528 1 1728 6c0 a.out