From 36c666f02758f416d038284f24dc16a5a6454fdb Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 4 Sep 2020 23:31:05 +0200 Subject: [PATCH] pybind11_add_module(): OPT_SIZE target --- docs/changelog.rst | 6 ++++++ docs/compiling.rst | 18 +++++++++++++++++- tools/pybind11Common.cmake | 18 ++++++++++++++++++ tools/pybind11NewTools.cmake | 8 ++++++-- tools/pybind11Tools.cmake | 9 ++++++--- 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 36381eb53..461002e3f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -11,6 +11,12 @@ v2.6.0 (IN PROGRESS) See :ref:`upgrade-guide-2.6` for help upgrading to the new version. +* ``pybind11_add_module()`` now accepts an optional ``OPT_SIZE`` flag that + switches the binding target to size-based optimization regardless global + CMake build type (except in debug mode, where optimizations remain disabled). + This reduces binary size quite substantially (~25%). + `#2463 `_ + * Keyword-only argument supported in Python 2 or 3 with ``py::kw_only()``. `#2100 `_ diff --git a/docs/compiling.rst b/docs/compiling.rst index 72b0c1eec..ca4dc756e 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -62,7 +62,7 @@ function with the following signature: .. code-block:: cmake pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] - [NO_EXTRAS] [THIN_LTO] source1 [source2 ...]) + [NO_EXTRAS] [THIN_LTO] [OPT_SIZE] source1 [source2 ...]) This function behaves very much like CMake's builtin ``add_library`` (in fact, it's a wrapper function around that command). It will add a library target @@ -96,6 +96,19 @@ regular LTO if ``-flto=thin`` is not available. If ``CMAKE_INTERPROCEDURAL_OPTIMIZATION`` is set (either ON or OFF), then that will be respected instead of the built-in flag search. +The ``OPT_SIZE`` flag enables size-based optimization equivalent to the +standard ``/Os`` or ``-Os`` compiler flags and the ``MinSizeRel`` build type, +which avoid optimizations that that can substantially increase the size of the +resulting binary. This flag is particularly useful in projects that are split +into performance-critical parts and associated bindings. In this case, we can +compile the project in release mode (and hence, optimize performance globally), +and specify ``OPT_SIZE`` for the binding target, where size might be the main +concern as performance is often less critical here. A ~25% size reduction has +been observed in practice. This flag only changes the optimization behavior at +a per-target level and takes precedence over the global CMake build type +(``Release``, ``RelWithDebInfo``) except for ``Debug`` builds, where +optimizations remain disabled. + .. _ThinLTO: http://clang.llvm.org/docs/ThinLTO.html Configuration variables @@ -245,6 +258,9 @@ available in all modes. The targets provided are: ``pybind11::windows_extras`` ``/bigobj`` and ``/mp`` for MSVC. + ``pybind11::opt_size`` + ``/Os`` for MSVC, ``-Os`` for other compilers. Does nothing for debug builds. + Two helper functions are also provided: ``pybind11_strip(target)`` diff --git a/tools/pybind11Common.cmake b/tools/pybind11Common.cmake index 8f7f57b51..96e958e64 100644 --- a/tools/pybind11Common.cmake +++ b/tools/pybind11Common.cmake @@ -10,6 +10,7 @@ Adds the following targets:: pybind11::python_link_helper - Adds link to Python libraries pybind11::python2_no_register - Avoid warning/error with Python 2 + C++14/7 pybind11::windows_extras - MSVC bigobj and mp for building multithreaded + pybind11::opt_size - avoid optimizations that increase code size Adds the following functions:: @@ -133,6 +134,23 @@ if(MSVC) endif() endif() +# ----------------------- Optimize binary size -------------------------- + +add_library(pybind11::opt_size IMPORTED INTERFACE ${optional_global}) + +if(MSVC) + set(PYBIND11_OPT_SIZE /Os) +else() + set(PYBIND11_OPT_SIZE -Os) +endif() + +set_property( + TARGET pybind11::opt_size + APPEND + PROPERTY INTERFACE_COMPILE_OPTIONS $<$:${PYBIND11_OPT_SIZE}> + $<$:${PYBIND11_OPT_SIZE}> + $<$:${PYBIND11_OPT_SIZE}>) + # ----------------------- Legacy option -------------------------- # Warn or error if old variable name used diff --git a/tools/pybind11NewTools.cmake b/tools/pybind11NewTools.cmake index bf321e5e6..812ec094a 100644 --- a/tools/pybind11NewTools.cmake +++ b/tools/pybind11NewTools.cmake @@ -138,8 +138,8 @@ endif() # WITHOUT_SOABI and WITH_SOABI will disable the custom extension handling used by pybind11. # WITH_SOABI is passed on to python_add_library. function(pybind11_add_module target_name) - cmake_parse_arguments(PARSE_ARGV 1 ARG "STATIC;SHARED;MODULE;THIN_LTO;NO_EXTRAS;WITHOUT_SOABI" - "" "") + cmake_parse_arguments(PARSE_ARGV 1 ARG + "STATIC;SHARED;MODULE;THIN_LTO;OPT_SIZE;NO_EXTRAS;WITHOUT_SOABI" "" "") if(ARG_ADD_LIBRARY_STATIC) set(type STATIC) @@ -204,6 +204,10 @@ function(pybind11_add_module target_name) if(MSVC) target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) endif() + + if(ARG_OPT_SIZE) + target_link_libraries(${target_name} PRIVATE pybind11::opt_size) + endif() endfunction() function(pybind11_extension name) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index 10f15a309..a0a3b60eb 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -1,6 +1,6 @@ # tools/pybind11Tools.cmake -- Build system for the pybind11 modules # -# Copyright (c) 2015 Wenzel Jakob +# Copyright (c) 2020 Wenzel Jakob # # All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. @@ -124,10 +124,10 @@ endfunction() # Build a Python extension module: # pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] -# [NO_EXTRAS] [THIN_LTO] source1 [source2 ...]) +# [NO_EXTRAS] [THIN_LTO] [OPT_SIZE] source1 [source2 ...]) # function(pybind11_add_module target_name) - set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS SYSTEM THIN_LTO) + set(options "MODULE;SHARED;EXCLUDE_FROM_ALL;NO_EXTRAS;SYSTEM;THIN_LTO;OPT_SIZE") cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) if(ARG_MODULE AND ARG_SHARED) @@ -185,4 +185,7 @@ function(pybind11_add_module target_name) target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) endif() + if(ARG_OPT_SIZE) + target_link_libraries(${target_name} PRIVATE pybind11::opt_size) + endif() endfunction()