chg: dev: Add option to compile named with static linking and LTO

Statically linking lib{isc,dns,ns,cfg,isccc} and enabling LTO shows over 10% improvements on all almost measurements in perflab. That said, we can't use Meson's option for LTO since it would result in every binary being compiled with LTO and a great increase in compile time.

To work around it, we add a configuration option that enables LTO and static linking only for the `named` binary.

Merge branch 'alessio/meson-lto-v2' into 'main'

See merge request isc-projects/bind9!10761
This commit is contained in:
Ondřej Surý 2025-09-24 13:23:21 +02:00
commit b6971fb724
16 changed files with 211 additions and 58 deletions

View file

@ -1214,7 +1214,7 @@ scan-build:
variables:
CC: "${CLANG}"
CFLAGS: "${CFLAGS_COMMON}"
EXTRA_CONFIGURE: "-Didn=enabled"
EXTRA_CONFIGURE: "-Didn=enabled --native-file ci/clang-trixie.ini"
before_script:
- *list_installed_package_versions
script:
@ -1427,7 +1427,7 @@ clang:asan:
variables:
CC: ${CLANG}
CFLAGS: "${CFLAGS_COMMON}"
EXTRA_CONFIGURE: "-Db_sanitize=address,undefined -Db_lundef=false -Didn=enabled -Djemalloc=disabled -Dtracing=disabled"
EXTRA_CONFIGURE: "-Db_sanitize=address,undefined -Db_lundef=false -Didn=enabled -Djemalloc=disabled -Dtracing=disabled --native-file ci/clang-trixie.ini"
<<: *base_image
<<: *build_job
@ -1487,7 +1487,8 @@ clang:tsan:
CC: "${CLANG}"
CFLAGS: "${CFLAGS_COMMON}"
LDFLAGS: "-Wl,--disable-new-dtags"
EXTRA_CONFIGURE: "${TSAN_CONFIGURE_FLAGS_COMMON} -Db_lundef=false"
EXTRA_CONFIGURE: "${TSAN_CONFIGURE_FLAGS_COMMON} -Db_lundef=false -Dnamed-lto=off --native-file ci/clang-trixie.ini"
<<: *build_job
system:clang:tsan:
variables:
@ -1542,6 +1543,7 @@ clang:trixie:amd64:
variables:
CC: ${CLANG}
CFLAGS: "${CFLAGS_COMMON}"
EXTRA_CONFIGURE: "--native-file ci/clang-trixie.ini"
RUN_MESON_INSTALL: 1
<<: *debian_trixie_amd64_image
<<: *build_job
@ -1801,7 +1803,7 @@ respdiff:tsan:
CC: "${CLANG}"
CFLAGS: "${CFLAGS_COMMON}"
LDFLAGS: "-Wl,--disable-new-dtags"
EXTRA_CONFIGURE: "${TSAN_CONFIGURE_FLAGS_COMMON} -Db_lundef=false"
EXTRA_CONFIGURE: "${TSAN_CONFIGURE_FLAGS_COMMON} -Dnamed-lto=off -Db_lundef=false"
MAX_DISAGREEMENTS_PERCENTAGE: "0.3"
TSAN_OPTIONS: "${TSAN_OPTIONS_DEBIAN}"
script:
@ -1928,9 +1930,13 @@ reproducible-build:
before_script:
- *list_installed_package_versions
script:
# dnstap produces an intermediate .a file, and meson considers all .a
# files to be final results independently of whether they are installed or
# not. But the content of the .a file might be unstable under LTO due to
# -ffat-lto-objects. Hence we disable dnstap for reproducibility tests.
- meson reprotest
--intermediaries
--
-Ddnstap=disabled
-Ddoc=disabled
-Doptimization=1
artifacts:

20
ci/clang-trixie.ini Normal file
View file

@ -0,0 +1,20 @@
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
#
# SPDX-License-Identifier: MPL-2.0
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
# LTO builds with clang
[binaries]
ar = 'llvm-ar-20'
c = 'clang-20'
c_ld = 'lld'
[project options]
named-lto = 'thin'

View file

@ -87,6 +87,22 @@ To improve performance, use of the ``jemalloc`` library
(https://jemalloc.net/) is strongly recommended. Version 4.0.0 or newer is
required when in use.
To further improve performance, compilation with link-time optimization is
recommended. This is enabled by default via the ``-Dnamed-lto`` option
(default: ``thin``). Link-time optimization can be disabled if needed by
using ``-Dnamed-lto=off``. The optimization level can be controlled with
``-Dnamed-lto=thin`` or ``-Dnamed-lto=full``.
Link-time optimization requires close coordination between the compiler and
the linker. Due to ``clang`` limitations, compiling ``named`` with ``clang``
and link-time optimization is only supported with the ``lld`` linker.
Meson provides an alternative way to enable link-time optimization through
the ``-Db_lto=true`` flag. However, this option is incompatible with
BIND's ``-Dnamed-lto`` option. Meson's ``-Db_lto`` may also be incompatible
with certain BIND build options and can result in lower performance and
higher compile times compared to ``-Dnamed-lto``.
To support :rfc:`DNS over HTTPS (DoH) <8484>`, the server must be linked
with ``libnghttp2`` (https://nghttp2.org/). If the library is
unavailable, ``-Ddoh=disabled`` can be used to disable DoH support.

View file

@ -9,7 +9,8 @@
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
probe_src = [dtrace_header.process('probes.d'), files('xfrin.c')]
probe_hdr = dtrace_header.process('probes-dns.d')
probe_src = [probe_hdr, files('xfrin.c')]
# dns_inc += include_directories('include')
dns_inc_p += include_directories('.')
@ -57,17 +58,20 @@ endif
dns_srcset.add(
when: 'HAVE_DTRACE',
if_true: custom_target(
'dns-probe',
input: [files('probes.d'), dns_probe_objects],
output: 'dns-probes.o',
command: [
dtrace,
'-G',
'-o', '@OUTPUT@',
'-s', '@INPUT@',
],
),
if_true: [
custom_target(
'dns-probe',
input: [files('probes-dns.d'), dns_probe_objects],
output: 'dns-probes.o',
command: [
dtrace,
'-G',
'-o', '@OUTPUT@',
'-s', '@INPUT@',
],
),
probe_hdr,
],
if_false: probe_src,
)

View file

@ -49,7 +49,7 @@
#include <dst/dst.h>
#include "probes.h"
#include "probes-dns.h"
/*
* Incoming AXFR and IXFR.

View file

@ -34,7 +34,7 @@
#include "job_p.h"
#include "loop_p.h"
#include "probes.h"
#include "probes-isc.h"
/*
* Public: #include <isc/job.h>

View file

@ -11,7 +11,8 @@
probe_src = []
probe_src += [dtrace_header.process('probes.d'), files('job.c')]
probe_hdr = dtrace_header.process('probes-isc.d')
probe_src += [probe_hdr, files('job.c')]
if config.get('USE_PTHREAD_RWLOCK') != 1
probe_src += files('rwlock.c')
endif
@ -47,17 +48,20 @@ endif
isc_srcset.add(
when: 'HAVE_DTRACE',
if_true: custom_target(
'isc-probe',
input: [files('probes.d'), isc_probe_objects],
output: 'isc-probes.o',
command: [
dtrace,
'-G',
'-o', '@OUTPUT@',
'-s', '@INPUT@',
],
),
if_true: [
custom_target(
'isc-probe',
input: [files('probes-isc.d'), isc_probe_objects],
output: 'isc-probes.o',
command: [
dtrace,
'-G',
'-o', '@OUTPUT@',
'-s', '@INPUT@',
],
),
probe_hdr,
],
if_false: probe_src,
)

View file

@ -44,8 +44,8 @@
#include <isc/uv.h>
#include "../loop_p.h"
#include "../openssl_shim.h"
#include "netmgr-int.h"
#include "openssl_shim.h"
isc__netmgr_t *isc__netmgr = NULL;

View file

@ -62,7 +62,7 @@
#include <isc/tid.h>
#include <isc/util.h>
#include "probes.h"
#include "probes-isc.h"
static atomic_uint_fast16_t isc__crwlock_workers = 128;

View file

@ -9,7 +9,8 @@
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
probe_src = [dtrace_header.process('probes.d'), files('query.c')]
probe_hdr = dtrace_header.process('probes-ns.d')
probe_src = [probe_hdr, files('query.c')]
if config.get('HAVE_DTRACE')
ns_probe_objects += static_library(
@ -25,17 +26,20 @@ endif
ns_srcset.add(
when: 'HAVE_DTRACE',
if_true: custom_target(
'ns-probe',
input: [files('probes.d'), ns_probe_objects],
output: 'ns-probes.o',
command: [
dtrace,
'-G',
'-o', '@OUTPUT@',
'-s', '@INPUT@',
],
),
if_true: [
custom_target(
'ns-probe',
input: [files('probes-ns.d'), ns_probe_objects],
output: 'ns-probes.o',
command: [
dtrace,
'-G',
'-o', '@OUTPUT@',
'-s', '@INPUT@',
],
),
probe_hdr,
],
if_false: probe_src,
)

View file

@ -75,7 +75,7 @@
#include <ns/stats.h>
#include <ns/xfrout.h>
#include "probes.h"
#include "probes-ns.h"
#if 0
/*

View file

@ -45,6 +45,7 @@ developer_mode = get_option('developer').enabled()
c_std = get_option('c_std')
optimization = get_option('optimization')
sanitizer = get_option('b_sanitize')
meson_lto = get_option('b_lto')
trace_logging = get_option('trace-logging')
rcu_flavor = get_option('rcu-flavor')
@ -64,6 +65,7 @@ leak_opt = get_option('leak-detection')
line_opt = get_option('line')
lmdb_opt = get_option('lmdb')
locktype_opt = get_option('locktype')
named_lto_opt = get_option('named-lto')
stats_json_opt = get_option('stats-json')
stats_xml_opt = get_option('stats-xml')
tracing_opt = get_option('tracing')
@ -898,6 +900,61 @@ assert(
'tracing is requested but dtrace is not found',
)
# LTO
static_lto_c_args = []
static_lto_link_args = []
if named_lto_opt == 'full'
static_lto_c_args = ['-ffat-lto-objects', '-flto']
static_lto_link_args = ['-flto']
elif named_lto_opt == 'thin' and cc.get_id() == 'clang' and cc.get_linker_id() == 'ld.lld'
# Per LLVM docs [1], -ffat-lto-objects is supported only with lld and gold,
# and gold is deprecated/unmantained.
# [1]: https://llvm.org/docs/FatLTO.html
static_lto_c_args = ['-ffat-lto-objects', '-flto=thin']
static_lto_link_args = ['-flto=thin']
elif named_lto_opt == 'thin' and cc.get_id() == 'gcc'
static_lto_c_args = ['-ffat-lto-objects', '-flto=auto']
static_lto_link_args = ['-flto=auto']
elif named_lto_opt == 'thin'
error('LTO requires clang with ld.lld, or gcc with any linker')
endif
add_project_arguments(static_lto_c_args, language: 'c')
if named_lto_opt != 'off' and cc.get_id() == 'clang' and sanitizer.contains('address')
# Needed to suppress the
# warning: Redundant instrumentation detected, with module flag:
# nosanitize_address [-Werror,-Wbackend-plugin]
# warning in address sanitizer. This warning indicates that the object file
# has been processed already by address sanitizer instrumentation pass.
# From looking at the pass code, when address sanitizer detects that
# an object file has already been instrumented, it just skips it.
# Therefore it should be safe to suppress the warning.
add_project_arguments('-Wno-backend-plugin', language: 'c')
endif
if meson_lto and named_lto_opt != 'off'
# Meson's builtin LTO settings do not set -ffat-lto-objects, which can cause
# build issues.
# Since we don't want two, possibly conflicting, sets of LTO flags, we
# error out if both are set.
error(
'''
Meson builtin -Db_lto and BIND's -Dnamed-lto options are incompatible.
Either disable named-lto with -Dnamed-lto=off, or avoid setting
-Db_lto.
Note that using -Db_lto is not a recommended configuration, might
yield reduced performance compared to -Dnamed-lto and conflict
with other build flags.
''',
)
endif
### Finalize configuration
configure_file(output: 'config.h', configuration: config)
add_project_arguments('-include', meson.project_build_root() / 'config.h', language: 'c')
@ -1158,8 +1215,6 @@ libisccfg_dep = declare_dependency(
include_directories: isccfg_inc,
)
named_srcconf = named_srcset.apply(config, strict: false)
executable(
'arpaname',
arpaname_src,
@ -1459,22 +1514,58 @@ executable(
)
executable(
'named',
named_srcconf.sources(),
export_dynamic: true,
implicit_include_directories: true,
include_directories: named_inc_p,
install: true,
install_dir: sbindir,
sources: bind_keys,
dependencies: [
named_c_args = []
named_link_args = []
named_deps = []
if named_lto_opt == 'off'
named_deps = [
libdns_dep,
libisc_dep,
libisccc_dep,
libisccfg_dep,
libns_dep,
]
named_inc = named_inc_p
named_objects = []
else
named_deps = [
dns_srcconf.dependencies(),
isc_srcconf.dependencies(),
isccc_srcconf.dependencies(),
isccfg_srcconf.dependencies(),
ns_srcconf.dependencies(),
]
named_inc = [isc_inc, dns_inc, isccc_inc, isccfg_inc, ns_inc, named_inc_p]
named_srcset.add(dns_gen_headers)
named_objects = [
libisc.extract_all_objects(recursive: true),
libdns.extract_all_objects(recursive: true),
libns.extract_all_objects(recursive: true),
libisccc.extract_all_objects(recursive: true),
libisccfg.extract_all_objects(recursive: true),
]
endif
named_srcconf = named_srcset.apply(config, strict: false)
executable(
'named',
named_srcconf.sources(),
objects: named_objects,
c_args: static_lto_c_args,
link_args: static_lto_link_args,
export_dynamic: true,
implicit_include_directories: true,
include_directories: named_inc,
install: true,
install_dir: sbindir,
sources: bind_keys,
dependencies: named_deps
+ [
openssl_dep,
cap_dep,

View file

@ -188,3 +188,11 @@ option(
value: 'disabled',
description: 'enable the memory leak detection in external libraries (libxml2, libuv, OpenSSL)',
)
option(
'named-lto',
type: 'combo',
choices: ['off', 'thin', 'full'],
value: 'thin',
description: 'Enable Link Time Optimization for named.',
)