Skip to main content

C/C++ Basic Workflow

Goal

Walk you through the suggested basic workflow for using Codee, demonstrated with a C project.

Getting ready

For this demonstration, we will use a C implementation of a matrix multiplication. Start by cloning the repository:

git clone https://github.com/codee-com/codee-demos.git

Walkthrough of the workflow

Codee has multiple reports that can be useful, but in this basic workflow we will focus on the Codee checkers.

0. Compiler Invocation

Firstly navigate to the source code directory:

cd codee-demos/C/MATMUL

Before using Codee, ensure you have a working compiler invocation for the code to analyze. We will focus on the main.c file, which contains the core computational code. The file can be compiled as:

gcc main.c -c -I include/ -O3

To generate any Codee report, simply add the compiler invocation to the right of the codee invocation after a -- separator.

1. Checks Report

To apply the recommendations from the Open Catalog, the checks report identifies the precise locations in the source code where the improvement opportunities have been found. The default checks report pinpoints the file, line, and column for each opportunity:

Codee command
codee checks -- gcc main.c -c -I include/ -O3
Codee output
CHECKS REPORT

main.c:16:9 [PWR039] (level: L1): Consider loop interchange to improve the locality of reference and enable vectorization
main.c:9:9 [PWR053] (level: L1): Consider applying vectorization to forall loop
main.c:17:13 [PWR010] (level: L3): Avoid column-major array access in C/C++
main.c:18:17 [PWR048] (level: L3): Replace multiplication/addition combo with an explicit call to fused multiply-add
main.c:15:5 [PWR035] (level: L3): Avoid non-consecutive array access to improve performance
main.c:17:13 [RMK010] (level: L4): Strided memory accesses in the loop body may prevent vectorization

SUGGESTIONS

Use --verbose to get more details, e.g:
codee checks --verbose -- gcc main.c -c -I include/ -O3

Use --check-id to focus on specific subsets of checkers, e.g.:
codee checks --check-id PWR039 -- gcc main.c -c -I include/ -O3

1 file, 2 functions, 6 loops, 55 LOCs successfully analyzed (6 checkers) and 0 non-analyzed files in 110 ms

Typically, you will also use the verbose mode of the checks report to generate detailed information on how to address each improvement opportunity:

Codee command
codee checks --verbose -- gcc main.c -c -I include/ -O3
Codee output
CHECKS REPORT

main.c:16:9 [PWR039] (level: L1): Consider loop interchange to improve the locality of reference and enable vectorization
Loops to interchange:
16: for (size_t j = 0; j < n; j++) {
17: for (size_t k = 0; k < p; k++) {
Suggestion: Interchange inner and outer loops in the loop nest to improve performance
Documentation: https://github.com/codee-com/open-catalog/tree/main/Checks/PWR039
AutoFix:
codee rewrite --check-id pwr039 --in-place main.c:16:9 -- gcc main.c -c -I include/ -O3

main.c:9:9 [PWR053] (level: L1): Consider applying vectorization to forall loop
Suggestion: Use 'rewrite' to automatically optimize the code
Documentation: https://github.com/codee-com/open-catalog/tree/main/Checks/PWR053
AutoFix (choose one option):
* Using OpenMP pragmas (recommended):
codee rewrite --check-id pwr053 --variant omp --in-place main.c:9:9 -- gcc main.c -c -I include/ -O3
* Using Clang compiler pragmas:
codee rewrite --check-id pwr053 --variant clang --in-place main.c:9:9 -- gcc main.c -c -I include/ -O3
* Using GCC pragmas:
codee rewrite --check-id pwr053 --variant gcc --in-place main.c:9:9 -- gcc main.c -c -I include/ -O3
* Using ICC pragmas:
codee rewrite --check-id pwr053 --variant icc --in-place main.c:9:9 -- gcc main.c -c -I include/ -O3
* Using combined pragmas, for example (for GCC and Clang pragmas):
codee rewrite --check-id pwr053 --variant gcc,clang --in-place main.c:9:9 -- gcc main.c -c -I include/ -O3

<...>

2. Autofix

In certain scenarios, Codee can automatically apply the suggested improvements to your code. The autofix feature is closely integrated with the verbose output of the checks report, as command-line invocations will be generated for all available autofixes.

Codee command
codee rewrite --check-id pwr039 --in-place main.c:16:9 -- gcc main.c -c -I include/ -O3
Codee output
Results for file '/home/user/codee-demos/C/MATMUL/main.c':
Successfully applied AutoFix to the loop at 'main.c:16:9' [using loop interchange]:
[INFO] Loops interchanged:
- main.c:16:9
- main.c:17:13

Successfully created main_codee.c

Codee automatically marks the rewritten code with comments, allowing you to double-check the changes to ensure correctness:

git diff main.c
diff --git a/C/MATMUL/main.c b/C/MATMUL/main.c
index ecc66be..3f71fbf 100644
--- a/C/MATMUL/main.c
+++ b/C/MATMUL/main.c
@@ -13,8 +13,10 @@ void matmul(size_t m, size_t n, size_t p, double **A, double **B, double **C) {

// Accumulation
for (size_t i = 0; i < m; i++) {
- for (size_t j = 0; j < n; j++) {
- for (size_t k = 0; k < p; k++) {
+ // Codee: Loop modified by Codee (2025-04-15 10:42:53)
+ // Codee: Technique applied: loop interchange
+ for (size_t k = 0; k < p; k++) {
+ for (size_t j = 0; j < n; j++) {
C[i][j] += A[i][k] * B[k][j];
}
}

3. Compilation Database

Codee requires the compiler invocation for each file to analyze. Sometimes the compiler invocation can be a huge command and there can be also multi-file projects. The recommended approach for this is to use a compilation database to efficiently handle the compiler invocations for different source files.

The generation of the compilation database depends on the particular build system used:

  • CMake: Add -DCMAKE_EXPORT_COMPILE_COMMANDS=ON to your usual CMake invocation; for instance:

    cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G "Ninja" -B build
  • Makefiles: Since they do not natively support the generation of compilation databases, we recommend using bear. This tool is available on Linux systems through common package managers.

    To generate the compilation database, prepend bear -- to your typical make invocation; for example:

    bear -- make

MATMUL supports both CMake and make.

Any of the above commands will generate a compile_commands.json file containing the compiler invocation for each file in the project.

build/compile_commands.json
[
{
"directory": "/home/user/codee-demos/C/MATMUL/build",
"command": "/usr/bin/cc -I/home/user/codee-demos/C/MATMUL/include -fopenmp -o CMakeFiles/matmul.dir/matrix.c.o -c /home/user/codee-demos/C/MATMUL/matrix.c",
"file": "/home/user/codee-demos/C/MATMUL/matrix.c",
"output": "CMakeFiles/matmul.dir/matrix.c.o"
},
{
"directory": "/home/user/codee-demos/C/MATMUL/build",
"command": "/usr/bin/cc -I/home/user/codee-demos/C/MATMUL/include -fopenmp -o CMakeFiles/matmul.dir/clock.c.o -c /home/user/codee-demos/C/MATMUL/clock.c",
"file": "/home/user/codee-demos/C/MATMUL/clock.c",
"output": "CMakeFiles/matmul.dir/clock.c.o"
},
{
"directory": "/home/user/codee-demos/C/MATMUL/build",
"command": "/usr/bin/cc -I/home/user/codee-demos/C/MATMUL/include -fopenmp -o CMakeFiles/matmul.dir/main.c.o -c /home/user/codee-demos/C/MATMUL/main.c",
"file": "/home/user/codee-demos/C/MATMUL/main.c",
"output": "CMakeFiles/matmul.dir/main.c.o"
}
]

Now you can use the JSON generated to substitute the compilation command in the Codee invocation. For example:

Codee command
codee checks -p build/compile_commands.json