Skip to content

enh: reduce binary size growth since 1.14.2 #5292

@matejk

Description

@matejk

Summary

Binary sizes of Foundation, Net, Util, MongoDB, and other libraries have grown between 1.14.2 and main. Much of the growth is from legitimate new features, but there are actionable items that can reduce size without losing functionality.

Measured on macOS arm64, Apple Clang, RelWithDebInfo, all buildable ENABLE flags ON:

Library 1.14.2 (stripped) main (stripped) Diff Growth
Foundation 2,907 KB 3,664 KB +757 KB +26.0%
MongoDB 261 KB 434 KB +172 KB +66.0%
Util 550 KB 647 KB +98 KB +17.7%
Net 1,338 KB 1,432 KB +94 KB +7.0%
Data 2,773 KB 2,821 KB +48 KB +1.7%
Redis 154 KB 177 KB +23 KB +15.1%
Crypto 298 KB 315 KB +17 KB +5.7%
CppParser 235 KB 251 KB +17 KB +7.2%
DataSQLite 1,636 KB 1,651 KB +15 KB +0.9%
XML 632 KB 645 KB +13 KB +2.0%
Encodings 923 KB 923 KB 0 0.0%
DNSSD 126 KB 126 KB 0 0.0%
Zip 268 KB 269 KB +1 KB +0.2%
NetSSL 357 KB 358 KB +1 KB +0.3%
SevenZip 154 KB 156 KB +2 KB +1.0%
PDF 1,293 KB 1,289 KB -4 KB -0.3%
DataODBC 1,276 KB 1,269 KB -7 KB -0.5%
JWT 248 KB 229 KB -19 KB -7.6%
JSON 469 KB 467 KB -2 KB -0.3%
Prometheus 174 KB 173 KB -2 KB -0.9%

Bundled dependency changes:

Dependency In 1.14.2 __TEXT main __TEXT Delta
cpptrace (new) Foundation 0 265 KB +265 KB
v8_double_conversion Foundation 41 KB 41 KB 0
expat XML 64 KB 124 KB +60 KB
pcre2 Foundation 320 KB 356 KB +36 KB
utf8proc Foundation 340 KB 343 KB +3 KB
sqlite3 DataSQLite 983 KB 992 KB +9 KB
7zip SevenZip 41 KB 49 KB +8 KB
zlib Foundation 59 KB 58 KB -2 KB
hpdf PDF 627 KB 643 KB +16 KB
png PDF 178 KB 174 KB -4 KB

Detailed Per-Library Findings

1. Foundation: +757 KB stripped (+26.0%)

Top contributors to growth (own code __TEXT):

Source 1.14.2 main Diff Cause
FastLogger.cpp 0 290 KB +290 KB New Quill-based logger (ENABLE_FASTLOGGER=ON by default)
AsyncNotificationCenter.cpp 9 KB 21 KB +12 KB Reworked
Exception.cpp 82 KB 89 KB +7 KB
URI.cpp 23 KB 29 KB +6 KB
Path.cpp 26 KB 31 KB +5 KB
FileChannel.cpp 20 KB 25 KB +5 KB
SharedLibrary.cpp 4 KB 8 KB +5 KB
ULID.cpp + ULIDGenerator.cpp 0 5.5 KB +5.5 KB New feature
IOLock.cpp 0 0.8 KB +0.8 KB New

New bundled dependency: cpptrace (+265 KB, for stack tracing). Note: v8_double_conversion was previously embedded in Foundation/src/ and moved to dependencies/ — no size change.

Offset by Var/VarHolder shrinking by ~32 KB.

2. MongoDB: +172 KB stripped (+66.0%)

Largely new ReplicaSet/topology discovery code:

Source 1.14.2 main Diff
ReplicaSet.cpp 7 KB 57 KB +50 KB (rewritten)
ServerDescription.cpp 0 19 KB NEW
ReadPreference.cpp 0 19 KB NEW
ReplicaSetURI.cpp 0 18 KB NEW
TopologyDescription.cpp 0 12 KB NEW
ReplicaSetConnection.cpp 0 11 KB NEW
Document.cpp 29 KB 38 KB +8 KB

This is legitimate new functionality (proper MongoDB topology discovery).

3. Util: +98 KB stripped (+17.7%) — template bloat is actionable

Root cause: = default destructors in Configuration subclass headers cause 93 KB of template waste.

In 1.14.2, AbstractConfiguration subclasses had out-of-line destructors defined in .cpp files. In main, these were changed to = default in the headers. Because AbstractConfiguration has BasicEvent<KeyValue> members, the defaulted destructor triggers template instantiations of AbstractEvent, DefaultStrategy, SharedPtr, etc. in every translation unit that includes the subclass header.

Measured impact on the 11 Configuration subclass .o files:

  • Total __TEXT: 243 KB
  • Template code within those files: 103 KB
  • Unique template code (needed once): 10 KB
  • Duplicated template waste: 93 KB (38% of total)

In 1.14.2, these same 11 files had 0 bytes of template code because the destructors were out-of-line.

Additionally, PropertyFileConfiguration gained !include directive support (+37 KB).

4. Net: +94 KB stripped (+7.0%)

New TCPReactor framework (+27 KB in 5 new files) and feature growth in existing files (+67 KB spread across MessageHeader, FTPClientSession, OAuth10Credentials, SMTPClientSession, etc.). Legitimate feature additions.

5. Data: +48 KB (+1.7%)

Growth in RecordSet.cpp (+38 KB) and StatementImpl.cpp (+10 KB). Incremental feature additions.

6. Redis: +23 KB (+15.1%)

New RedisNotifications (+2 KB), expanded Client.cpp (+3 KB), and other incremental growth.

7. XML: +13 KB stripped (+2.0%)

Bundled expat grew from 64 KB to 124 KB (+60 KB). Own XML code also grew slightly (+15 KB across DOM/SAX classes). The expat growth is offset at the dylib level because expat was moved from compiled-into-XML to separate dependency build.

Proposed Improvements

A. Move Configuration subclass destructors back out-of-line (Util, ~93 KB savings)

Move = default destructors from headers back to .cpp files for all 11 AbstractConfiguration subclasses. This eliminates 93 KB of duplicated AbstractEvent/DefaultStrategy/SharedPtr template instantiations.

Affected classes: PropertyFileConfiguration, LoggingConfigurator, SystemConfiguration, IniFileConfiguration, MapConfiguration, ConfigurationMapper, ConfigurationView, FilesystemConfiguration, LayeredConfiguration, XMLConfiguration, LocalConfigurationView.

// Before (current - in header):
~ConfigurationMapper() = default;

// After (in header):
~ConfigurationMapper();
// In .cpp:
ConfigurationMapper::~ConfigurationMapper() = default;

B. Consider ENABLE_FASTLOGGER=OFF by default (Foundation, ~290 KB savings)

FastLogger is the single largest contributor to Foundation growth. Users who don't need the Quill-based logger shouldn't pay the cost. Making it opt-in saves ~290 KB.

C. Evaluate cpptrace bundling (Foundation, ~265 KB)

The cpptrace dependency (for stack tracing, ENABLE_TRACE=ON) adds 265 KB. If stack tracing is not commonly needed, consider making it opt-in.

D. Explicit template instantiation for BasicEvent types (Util + future-proofing)

Add explicit instantiation declarations in AbstractConfiguration.h to prevent any future re-introduction of the template bloat:

// AbstractConfiguration.h
extern template class BasicEvent<KeyValue, FastMutex>;
extern template class BasicEvent<KeyValue const, FastMutex>;
extern template class BasicEvent<std::string const, FastMutex>;

This guarantees a single instantiation regardless of how subclass destructors are defined.

Methodology

Built both versions with identical settings on macOS arm64:

cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo \
  -DENABLE_ACTIVERECORD=ON -DENABLE_CRYPTO=ON -DENABLE_DATA=ON \
  -DENABLE_DATA_ODBC=ON -DENABLE_DATA_SQLITE=ON -DENABLE_DNSSD=ON \
  -DENABLE_DNSSD_BONJOUR=ON -DENABLE_ENCODINGS=ON -DENABLE_JSON=ON \
  -DENABLE_JWT=ON -DENABLE_MONGODB=ON -DENABLE_NET=ON -DENABLE_NETSSL=ON \
  -DENABLE_PDF=ON -DENABLE_PROMETHEUS=ON -DENABLE_REDIS=ON \
  -DENABLE_SEVENZIP=ON -DENABLE_TRACE=ON -DENABLE_UTIL=ON \
  -DENABLE_XML=ON -DENABLE_ZIP=ON -DENABLE_FASTLOGGER=ON \
  -DENABLE_CPPPARSER=ON

Analysis: size (segment sizes), nm --demangle --defined-only (symbol analysis), per-object-file __TEXT comparison, template instantiation counting across TUs, address-based symbol size computation.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions