The focus of most compiler optimizations is on the elimination of instructions. Such optimizations include DCE, propagation methods, CSE, static data flow analysis, and register allocation optimization. There are standard code sources for the evaluation of compiler optimizations, but there is a need for improvement.
A highly portable, modular, sufficiently complex, and intuitive measurement system was implemented. It exploited the modularity of graph algorithms and provided the following features:
- portable to any target, enabling a reliable comparison between previous and new targets,
- the same code, enabling to map measurement differences across targets to design decisions,
- modular, enabling deeper level evaluations of the supporting data structures across algorithmic contexts, with the ability to define weighted and composable objective functions,
- sufficiently complex, comprising multiple translation units for link-time optimization, while enabling to relate measurements to real applications, and
- intuitive, providing a notion of algorithmic distance between algorithmic contexts.
The system may be applied to the evaluation of design decisions in compiler optimizations, including the choice of heuristics and approximations. The potential for automation was demonstrated by compiling 1000s of executables with combinatorial composition of source files.
The C language was used for general portability. CPU manufacturers usually provide a C compiler as the first option to demonstrate their product advantages, which further motivated the choice of the C language for the implementation of the measurement system.
An original approach to in-memory alignment enabled cache-efficient type generics. A set of common graph algorithms were decomposed into small type-specific operations, while utilizing the "understanding" of memcpy by modern compilers.
The C standards were quoted when decisions were made to avoid undefined behavior. The implementation of the system complied rigorously with the C89/C90 and C99 standards, enabling to attribute errors to the implementation of these standards in a compiler.
The implementation only used integer and pointer operations (no floating point except in tests). Given parameter values within the specified ranges, the implementation provided an error message and an exit was executed if an integer overflow was attempted or an allocation was not completed due to insufficient resources. This behavior invariant held across all abstraction levels. A detailed specification accompanied the generic interface to ensure the correct usage.
graph-algorithms/dfs/README.md
graph-algorithms/dijkstra/README.md
graph-algorithms/prim/README.md
graph-algorithms/tsp/README.md
The executable size was used to estimate the number of instructions. 20888 executables were compiled to measure the effects of types in top translation units, algorithmic contexts, compilers, and optimization levels. The executable size remained mostly flat as the number of types in top translation units increased.
The combinatorial generation of top translation units is shown in the provided Makefiles. Parallel execution significantly sped up the compilation and linking of 1000s of executables within an algorithmic context, and 10000s of executables across algorithmic contexts.
Each compilation instance enabled a deeper level evaluation of the instruction footprint of the supporting data structures through object files, demonstrating the feasibility of defining weighted and composable objective functions.
To evaluate the number of instructions in multithreaded settings, two multithreaded code samples with different synchronization requirements and synchronization overhead were provided. One of the samples enabled adjusting the complexity of threads with recursion.
data-structures-pthread/ht-divchn-pthread/README.md
A multithreaded highly parametrized hash table was designed and implemented. All threads were synchronized, if hash table growth was required. Random subsets of threads were synchronized for the insert, remove, and delete operations.
utilities-pthread/mergesort-pthread/README.md
A version of the multithreaded mergesort algorithm was designed and implemented. Pairs of threads were synchronized. The complexity of threads was tuned with the user-defined depth of recursion.
The pthreads API was used, but the implementation of the thread synchronization operations was modular and customizable. The evaluation of the multithreaded code samples could also be potentially automated.
In each directory with a Makefile, run the following, unless stated otherwise:
make or optionally if available make BUILD_MODE=M32 or make BUILD_MODE=M64
./executable-name
make clean or make clean-all



