I made a small demo which generates artificial codebase and tests this hypothesis.
It generates 200 headers. Each header has a struct with 100 fields and a comment 5000 bytes long. 500 .c
files are used for benchmarking, each includes all the header files or forward declares all the classes.
To make it more realistic, each header is also included into it's own .c
file
The result is that using includes took me 22 seconds to compile while using forward declarations took 9 seconds.
generate.py
#!/usr/bin/env python3
import random
import string
include_template = """#ifndef FILE_{0}_{1}
#define FILE_{0}_{1}
{2}
//{3}
struct c_{0}_{1} {{
{4}}};
#endif
"""
def write_file(name, content):
f = open("./src/" + name, "w")
f.write(content)
f.close()
GROUPS = 200
FILES_PER_GROUP = 0
EXTRA_SRC_FILES = 500
COMMENT = ''.join(random.choices(string.ascii_uppercase + string.digits, k=5000))
VAR_BLOCK = "".join(["int var_{0};\n".format(k) for k in range(100)])
main_includes = ""
main_fwd = ""
for i in range(GROUPS):
include_statements = ""
for j in range(FILES_PER_GROUP):
write_file("file_{0}_{1}.h".format(i,j), include_template.format(i, j, "", COMMENT, VAR_BLOCK))
write_file("file_{0}_{1}.c".format(i,j), "#include \"file_{0}_{1}.h\"\n".format(i,j))
include_statements += "#include \"file_{0}_{1}.h\"\n".format(i, j)
main_includes += "#include \"file_{0}_{1}.h\"\n".format(i,j)
main_fwd += "struct c_{0}_{1};\n".format(i,j)
write_file("file_{0}_x.h".format(i), include_template.format(i, "x", include_statements, COMMENT, VAR_BLOCK))
write_file("file_{0}_x.c".format(i), "#include \"file_{0}_x.h\"\n".format(i))
main_includes += "#include \"file_{0}_x.h\"\n".format(i)
main_fwd += "struct c_{0}_x;\n".format(i)
main_template = """
{0}
int main(void) {{ return 0; }}
"""
for i in range(EXTRA_SRC_FILES):
write_file("extra_inc_{0}.c".format(i), main_includes)
write_file("extra_fwd_{0}.c".format(i), main_fwd)
write_file("maininc.c", main_template.format(main_includes))
write_file("mainfwd.c", main_template.format(main_fwd))
run_test.sh
#!/bin/bash
mkdir -p src
./generate.py
ls src/ | wc -l
du -h src/
gcc -v
echo src/file_*_*.c src/extra_inc_*.c src/mainfwd.c | xargs time gcc -o fwd.out
rm -rf out/*.a
echo src/file_*_*.c src/extra_fwd_*.c src/maininc.c | xargs time gcc -o inc.out
rm -rf fwd.out inc.out src
Results
$ ./run_test.sh
1402
8.2M src/
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 11.0.3 (clang-1103.0.32.29)
Target: x86_64-apple-darwin19.3.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
22.32 real 13.56 user 8.27 sys
8.51 real 4.44 user 3.78 sys