Skip to content

Commit b664152

Browse files
committed
compiler optimizations (-march=native -Ofast -fno-plt lto pgo)
1 parent 68831f7 commit b664152

3 files changed

Lines changed: 342 additions & 0 deletions

File tree

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,53 @@ After that, build in the standard way:
6969
$ mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make -j && cd ..
7070
```
7171

72+
`Release` builds now also enable `-march=native`, `-Ofast`, `-fno-plt`, and `-flto` by default for extra performance.
73+
74+
If you want a more portable or more conservative binary, you can disable any of them individually:
75+
76+
```console
77+
$ mkdir build && cd build
78+
$ cmake .. -DCMAKE_BUILD_TYPE=Release \
79+
-DENABLE_MARCH_NATIVE=OFF \
80+
-DENABLE_OFAST=OFF \
81+
-DENABLE_FNO_PLT=OFF \
82+
-DENABLE_LTO=OFF
83+
$ make -j
84+
```
85+
86+
For profile-guided optimization, use `scripts/build_pgo.sh`. It creates a `PGO generate` build, runs one or more training solver runs to collect profile data, and then rebuilds the final `PGO use` binary. Example:
87+
88+
```console
89+
$ ./scripts/build_pgo.sh --run C1_10_1:100:60 --run RC2_10_1:20:60
90+
```
91+
92+
This means:
93+
1. configure and build an instrumented `Release` binary with `-fprofile-generate`;
94+
2. run the solver on the listed benchmark instances to collect execution profiles;
95+
3. configure and build the final `Release` binary with `-fprofile-use`.
96+
97+
If you want to collect the profile manually instead of using the script:
98+
99+
```console
100+
$ cmake -S . -B pgo-gen -DCMAKE_BUILD_TYPE=Release \
101+
-DPGO_MODE=GENERATE \
102+
-DPGO_PROFILE_DIR="$PWD/pgo-data"
103+
$ cmake --build pgo-gen --target routes -j
104+
105+
$ ./pgo-gen/routes GehringHomberger1000/C1_10_1.TXT C1_10_1.sol \
106+
--lower_bound 100 \
107+
--t_max 60 \
108+
--beta_correction \
109+
--log_level normal
110+
111+
$ cmake -S . -B pgo-use -DCMAKE_BUILD_TYPE=Release \
112+
-DPGO_MODE=USE \
113+
-DPGO_PROFILE_DIR="$PWD/pgo-data"
114+
$ cmake --build pgo-use --target routes -j
115+
```
116+
117+
The training run should be representative of the instances you care about, because the final binary is optimized using the collected execution profile.
118+
72119
Then you can run the utility:
73120
```console
74121
$ ./build/routes --help

cmake/compiler.cmake

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,38 @@
11
include(cmake/utils.cmake)
2+
include(CheckCXXCompilerFlag)
23

34
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
45
# Remove VALGRIND code and assertions in *any* type of release build.
56
add_definitions("-DNDEBUG" "-DNVALGRIND")
67
endif()
78

9+
option(ENABLE_MARCH_NATIVE
10+
"Enable -march=native for release-like builds"
11+
ON)
12+
option(ENABLE_LTO
13+
"Enable link-time optimization for release-like builds"
14+
ON)
15+
option(ENABLE_FNO_PLT
16+
"Enable -fno-plt for release-like builds"
17+
ON)
18+
option(ENABLE_OFAST
19+
"Enable -Ofast for release-like builds"
20+
ON)
21+
22+
set(PGO_MODE "OFF" CACHE STRING
23+
"PGO mode for release-like builds: OFF, GENERATE, USE")
24+
set_property(CACHE PGO_MODE PROPERTY STRINGS OFF GENERATE USE)
25+
set(PGO_PROFILE_DIR "${CMAKE_BINARY_DIR}/pgo-data" CACHE PATH
26+
"Directory used to store/read PGO profile data")
27+
28+
string(TOUPPER "${PGO_MODE}" PGO_MODE)
29+
if (NOT PGO_MODE STREQUAL "OFF" AND
30+
NOT PGO_MODE STREQUAL "GENERATE" AND
31+
NOT PGO_MODE STREQUAL "USE")
32+
message(FATAL_ERROR
33+
"Unsupported PGO_MODE='${PGO_MODE}'. Expected OFF, GENERATE, or USE.")
34+
endif()
35+
836
#
937
# Perform build type specific configuration.
1038
#
@@ -22,4 +50,121 @@ set (CMAKE_CXX_FLAGS_DEBUG
2250
set (CMAKE_CXX_FLAGS_RELWITHDEBINFO
2351
"${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${CC_DEBUG_OPT} -O3")
2452

53+
macro(append_release_compile_flags langs)
54+
foreach(_lang ${langs})
55+
string(REPLACE ";" " " _flags "${ARGN}")
56+
foreach(_config RELEASE RELWITHDEBINFO)
57+
set(CMAKE_${_lang}_FLAGS_${_config}
58+
"${CMAKE_${_lang}_FLAGS_${_config}} ${_flags}")
59+
endforeach()
60+
unset(_config)
61+
unset(_flags)
62+
endforeach()
63+
unset(_lang)
64+
endmacro()
65+
66+
macro(append_release_linker_flags)
67+
string(REPLACE ";" " " _flags "${ARGN}")
68+
foreach(_kind EXE SHARED MODULE)
69+
foreach(_config RELEASE RELWITHDEBINFO)
70+
set(CMAKE_${_kind}_LINKER_FLAGS_${_config}
71+
"${CMAKE_${_kind}_LINKER_FLAGS_${_config}} ${_flags}")
72+
endforeach()
73+
unset(_config)
74+
endforeach()
75+
unset(_kind)
76+
unset(_flags)
77+
endmacro()
78+
79+
set(RELEASE_TUNING_FLAGS "")
80+
set(RELEASE_LINK_FLAGS "")
81+
82+
if (ENABLE_MARCH_NATIVE)
83+
check_c_compiler_flag("-march=native" CC_HAS_MARCH_NATIVE)
84+
check_cxx_compiler_flag("-march=native" CXX_HAS_MARCH_NATIVE)
85+
if (CC_HAS_MARCH_NATIVE AND CXX_HAS_MARCH_NATIVE)
86+
list(APPEND RELEASE_TUNING_FLAGS "-march=native")
87+
else()
88+
message(WARNING "ENABLE_MARCH_NATIVE=ON, but compiler does not support -march=native")
89+
endif()
90+
endif()
91+
92+
if (ENABLE_FNO_PLT)
93+
check_c_compiler_flag("-fno-plt" CC_HAS_FNO_PLT)
94+
check_cxx_compiler_flag("-fno-plt" CXX_HAS_FNO_PLT)
95+
if (CC_HAS_FNO_PLT AND CXX_HAS_FNO_PLT)
96+
list(APPEND RELEASE_TUNING_FLAGS "-fno-plt")
97+
else()
98+
message(WARNING "ENABLE_FNO_PLT=ON, but compiler does not support -fno-plt")
99+
endif()
100+
endif()
101+
102+
if (ENABLE_OFAST)
103+
check_c_compiler_flag("-Ofast" CC_HAS_OFAST)
104+
check_cxx_compiler_flag("-Ofast" CXX_HAS_OFAST)
105+
if (CC_HAS_OFAST AND CXX_HAS_OFAST)
106+
list(APPEND RELEASE_TUNING_FLAGS "-Ofast")
107+
else()
108+
message(WARNING "ENABLE_OFAST=ON, but compiler does not support -Ofast")
109+
endif()
110+
endif()
111+
112+
if (ENABLE_LTO)
113+
check_c_compiler_flag("-flto" CC_HAS_FLTO)
114+
check_cxx_compiler_flag("-flto" CXX_HAS_FLTO)
115+
if (CC_HAS_FLTO AND CXX_HAS_FLTO)
116+
list(APPEND RELEASE_TUNING_FLAGS "-flto")
117+
list(APPEND RELEASE_LINK_FLAGS "-flto")
118+
else()
119+
message(WARNING "ENABLE_LTO=ON, but compiler does not support -flto")
120+
endif()
121+
endif()
122+
123+
if (NOT PGO_MODE STREQUAL "OFF")
124+
if (CMAKE_C_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_ID MATCHES "GNU")
125+
file(MAKE_DIRECTORY "${PGO_PROFILE_DIR}")
126+
set(PGO_PREFIX_FLAG "-fprofile-prefix-path=${CMAKE_BINARY_DIR}")
127+
if (PGO_MODE STREQUAL "GENERATE")
128+
list(APPEND RELEASE_TUNING_FLAGS
129+
"-fprofile-generate=${PGO_PROFILE_DIR}"
130+
"${PGO_PREFIX_FLAG}")
131+
list(APPEND RELEASE_LINK_FLAGS "-fprofile-generate=${PGO_PROFILE_DIR}")
132+
elseif(PGO_MODE STREQUAL "USE")
133+
list(APPEND RELEASE_TUNING_FLAGS
134+
"-fprofile-use=${PGO_PROFILE_DIR}"
135+
"-fprofile-correction"
136+
"${PGO_PREFIX_FLAG}")
137+
list(APPEND RELEASE_LINK_FLAGS
138+
"-fprofile-use=${PGO_PROFILE_DIR}"
139+
"-fprofile-correction")
140+
endif()
141+
else()
142+
message(FATAL_ERROR
143+
"PGO_MODE currently supports only GNU C/C++ compilers in this project.")
144+
endif()
145+
endif()
146+
147+
if (RELEASE_TUNING_FLAGS)
148+
append_release_compile_flags("C;CXX" ${RELEASE_TUNING_FLAGS})
149+
endif()
150+
151+
if (RELEASE_LINK_FLAGS)
152+
append_release_linker_flags(${RELEASE_LINK_FLAGS})
153+
endif()
154+
155+
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
156+
string_join(" " RELEASE_TUNING_FLAGS_STR ${RELEASE_TUNING_FLAGS})
157+
string_join(" " RELEASE_LINK_FLAGS_STR ${RELEASE_LINK_FLAGS})
158+
if (RELEASE_TUNING_FLAGS_STR)
159+
message(STATUS "Release compile tuning flags: ${RELEASE_TUNING_FLAGS_STR}")
160+
endif()
161+
if (RELEASE_LINK_FLAGS_STR)
162+
message(STATUS "Release link tuning flags: ${RELEASE_LINK_FLAGS_STR}")
163+
endif()
164+
if (NOT PGO_MODE STREQUAL "OFF")
165+
message(STATUS "PGO mode: ${PGO_MODE}")
166+
message(STATUS "PGO profile dir: ${PGO_PROFILE_DIR}")
167+
endif()
168+
endif()
169+
25170
unset(CC_DEBUG_OPT)

scripts/build_pgo.sh

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6+
GEN_BUILD_DIR="${GEN_BUILD_DIR:-$ROOT_DIR/pgo-generate-build}"
7+
USE_BUILD_DIR="${USE_BUILD_DIR:-$ROOT_DIR/pgo-build}"
8+
PGO_PROFILE_DIR="${PGO_PROFILE_DIR:-$ROOT_DIR/pgo-data}"
9+
BENCHMARK_DIR="${BENCHMARK_DIR:-$ROOT_DIR/GehringHomberger1000}"
10+
DEFAULT_T_MAX="${DEFAULT_T_MAX:-60}"
11+
RUN_SPECS=()
12+
13+
usage() {
14+
cat <<EOF
15+
Usage: $(basename "$0") [--run INSTANCE:LOWER_BOUND[:T_MAX]]...
16+
17+
Builds the solver in two phases:
18+
1. a profile-generation build;
19+
2. a profile-use build that consumes the generated data.
20+
21+
Options:
22+
--run SPEC Training run in the form INSTANCE:LOWER_BOUND[:T_MAX].
23+
May be specified multiple times.
24+
--gen-build-dir DIR Directory for the profile-generation build.
25+
--use-build-dir DIR Directory for the final optimized PGO build.
26+
--profile-dir DIR Directory where GCC writes profile data.
27+
--benchmark-dir DIR Directory with benchmark instances.
28+
--default-t-max SEC Default time limit for runs without explicit T_MAX.
29+
-h, --help Show this help.
30+
31+
Environment overrides:
32+
GEN_BUILD_DIR, USE_BUILD_DIR, PGO_PROFILE_DIR, BENCHMARK_DIR, DEFAULT_T_MAX
33+
34+
Examples:
35+
$(basename "$0")
36+
$(basename "$0") --run C1_10_1:100:60
37+
$(basename "$0") --run C1_10_1:100:60 --run RC2_10_1:20:60
38+
EOF
39+
}
40+
41+
while [[ $# -gt 0 ]]; do
42+
case "$1" in
43+
--run)
44+
[[ $# -ge 2 ]] || { echo "missing value for --run" >&2; exit 1; }
45+
RUN_SPECS+=("$2")
46+
shift 2
47+
;;
48+
--gen-build-dir)
49+
[[ $# -ge 2 ]] || { echo "missing value for --gen-build-dir" >&2; exit 1; }
50+
GEN_BUILD_DIR="$2"
51+
shift 2
52+
;;
53+
--use-build-dir)
54+
[[ $# -ge 2 ]] || { echo "missing value for --use-build-dir" >&2; exit 1; }
55+
USE_BUILD_DIR="$2"
56+
shift 2
57+
;;
58+
--profile-dir)
59+
[[ $# -ge 2 ]] || { echo "missing value for --profile-dir" >&2; exit 1; }
60+
PGO_PROFILE_DIR="$2"
61+
shift 2
62+
;;
63+
--benchmark-dir)
64+
[[ $# -ge 2 ]] || { echo "missing value for --benchmark-dir" >&2; exit 1; }
65+
BENCHMARK_DIR="$2"
66+
shift 2
67+
;;
68+
--default-t-max)
69+
[[ $# -ge 2 ]] || { echo "missing value for --default-t-max" >&2; exit 1; }
70+
DEFAULT_T_MAX="$2"
71+
shift 2
72+
;;
73+
-h|--help)
74+
usage
75+
exit 0
76+
;;
77+
*)
78+
echo "unknown argument: $1" >&2
79+
usage >&2
80+
exit 1
81+
;;
82+
esac
83+
done
84+
85+
if [[ ${#RUN_SPECS[@]} -eq 0 ]]; then
86+
RUN_SPECS=("C1_10_1:100:${DEFAULT_T_MAX}")
87+
fi
88+
89+
require_command() {
90+
local command_name="$1"
91+
if ! command -v "$command_name" >/dev/null 2>&1; then
92+
printf 'Required command not found: %s\n' "$command_name" >&2
93+
exit 1
94+
fi
95+
}
96+
97+
require_file() {
98+
local path="$1"
99+
local description="$2"
100+
if [[ ! -f "$path" ]]; then
101+
printf 'Missing %s: %s\n' "$description" "$path" >&2
102+
exit 1
103+
fi
104+
}
105+
106+
require_command cmake
107+
108+
printf 'PGO profile dir: %s\n' "$PGO_PROFILE_DIR"
109+
rm -rf "$PGO_PROFILE_DIR"
110+
111+
cmake -S "$ROOT_DIR" -B "$GEN_BUILD_DIR" \
112+
-DCMAKE_BUILD_TYPE=Release \
113+
-DPGO_MODE=GENERATE \
114+
-DPGO_PROFILE_DIR="$PGO_PROFILE_DIR"
115+
cmake --build "$GEN_BUILD_DIR" --target routes -j
116+
117+
for run_spec in "${RUN_SPECS[@]}"; do
118+
IFS=':' read -r instance lower_bound t_max <<<"$run_spec"
119+
if [[ -z "${instance:-}" || -z "${lower_bound:-}" ]]; then
120+
printf 'Invalid --run value: %s\n' "$run_spec" >&2
121+
exit 1
122+
fi
123+
if [[ -z "${t_max:-}" ]]; then
124+
t_max="$DEFAULT_T_MAX"
125+
fi
126+
127+
problem_file="$BENCHMARK_DIR/${instance}.TXT"
128+
solution_file="$GEN_BUILD_DIR/${instance}.pgo.sol"
129+
require_file "$problem_file" "benchmark instance"
130+
131+
printf '\nTraining on %s (lower_bound=%s, t_max=%s)\n' \
132+
"$instance" "$lower_bound" "$t_max"
133+
"$GEN_BUILD_DIR/routes" \
134+
"$problem_file" \
135+
"$solution_file" \
136+
--lower_bound "$lower_bound" \
137+
--t_max "$t_max" \
138+
--beta_correction \
139+
--log_level normal
140+
rm -f "$solution_file"
141+
done
142+
143+
cmake -S "$ROOT_DIR" -B "$USE_BUILD_DIR" \
144+
-DCMAKE_BUILD_TYPE=Release \
145+
-DPGO_MODE=USE \
146+
-DPGO_PROFILE_DIR="$PGO_PROFILE_DIR"
147+
cmake --build "$USE_BUILD_DIR" --target routes -j
148+
149+
printf '\nPGO build completed.\n'
150+
printf 'Final binary: %s\n' "$USE_BUILD_DIR/routes"

0 commit comments

Comments
 (0)