Skip to content

Commit 5bde1c3

Browse files
committed
Split pybind11 into headers and cpp files to speedup compilation
TODO For now, only pybind11.h and options.h have been split. If maintainers feel that this is going in the right direction, I will update this commit to split all files. As discussed at #2322, the split is opt-in, and has been observed to speed up the compilation of the gem5 project by around 40% from 25 minutes to 15 minutes. If the PYBIND11_DECLARATIONS_ONLY is not defined, then the .cpp files are included in the .h files, and everything works header-only as was the case before this commit. If PYBIND11_DECLARATIONS_ONLY=1, then only declarations are made visible from, the header files, and the user must the user must compile the pybind11 .cpp files, also using PYBIND11_DECLARATIONS_ONLY=1, into objects and add link those into the final link. This commit also updates CMakeLists to automate building those object files. This commit has been tested as follows. First, the original build and pytest (without PYBIND11_DECLARATIONS_ONLY) work as before: mkdir build cmake .. make -j `nproc` make pytest TODO: if this commit gets traction, I will try to add a test for the PYBIND11_DECLARATIONS_ONLY version too, otherwise it will likely break. I'd just re-run the entire pybind with PYBIND11_DECLARATIONS_ONLY as well every time. The commit has also been tested with the following minimal manual example: class_test.cpp ``` struct ClassTest { ClassTest(const std::string &name) : name(name) {} void setName(const std::string &name_) { name = name_; } const std::string &getName() const { return name; } std::string name; }; struct ClassTestDerived : ClassTest { ClassTestDerived(const std::string &name, const std::string &name2) : ClassTest(name), name2(name2) {} std::string getName2() { return name + name2 + "2"; } std::string name2; }; namespace py = pybind11; PYBIND11_MODULE(class_test, m) { m.doc() = "pybind11 example plugin"; py::class_<ClassTest>(m, "ClassTest") .def(py::init<const std::string &>()) .def("setName", &ClassTest::setName) .def("getName", &ClassTest::getName) .def_readwrite("name", &ClassTest::name); py::class_<ClassTestDerived, ClassTest>(m, "ClassTestDerived") .def(py::init<const std::string &, const std::string &>()) .def("getName2", &ClassTestDerived::getName2) .def_readwrite("name", &ClassTestDerived::name); } ``` class_test_main.py ``` import class_test my_class_test = class_test.ClassTest('abc'); assert(my_class_test.getName() == 'abc') my_class_test.setName('012') assert(my_class_test.name == '012') my_class_test_derived = class_test.ClassTestDerived('abc', 'def'); assert(my_class_test_derived.getName2() == 'abcdef2') ``` without PYBIND11_DECLARATIONS_ONLY (`python3-config --cflags`) is opened up and hacked to point to the custom pybind11 source: ``` g++ \ -save-temps \ -I/usr/include/python3.8 \ -I/home/ciro/git/pybind11/include \ -Wno-unused-result \ -Wsign-compare \ -g \ -fdebug-prefix-map=/build/python3.8-fKk4GY/python3.8-3.8.2=. \ -specs=/usr/share/dpkg/no-pie-compile.specs \ -fstack-protector \ -Wformat \ -Werror=format-security \ -DNDEBUG \ -g \ -fwrapv \ -O3 \ -Wall \ -shared \ -std=c++11 \ -fPIC class_test.cpp \ -o class_test`python3-config --extension-suffix` \ `python3-config --libs` \ ; ./class_test_main.py ``` with PYBIND11_DECLARATIONS_ONLY: ``` g++ \ -DPYBIND11_DECLARATIONS_ONLY=1 \ -save-temps \ -I/usr/include/python3.8 \ -I/home/ciro/git/pybind11/include \ -Wno-unused-result \ -Wsign-compare \ -g \ -fdebug-prefix-map=/build/python3.8-fKk4GY/python3.8-3.8.2=. \ -specs=/usr/share/dpkg/no-pie-compile.specs \ -fstack-protector \ -Wformat \ -Werror=format-security \ -DNDEBUG \ -g \ -fwrapv \ -O3 \ -Wall \ -shared \ -std=c++11 \ -fPIC class_test.cpp \ /home/ciro/git/pybind11/build/libpybind11.a \ -o class_test`python3-config --extension-suffix` \ `python3-config --libs` \ ; ./class_test_main.py ```
1 parent 1abc4a9 commit 5bde1c3

File tree

9 files changed

+1309
-1160
lines changed

9 files changed

+1309
-1160
lines changed

CMakeLists.txt

+9
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,12 @@ if(NOT PYBIND11_MASTER_PROJECT)
269269
"${PYBIND11_INCLUDE_DIR}"
270270
CACHE INTERNAL "Directory where pybind11 headers are located")
271271
endif()
272+
273+
find_package (Python3 COMPONENTS Development)
274+
include_directories(${Python3_INCLUDE_DIRS})
275+
add_definitions(-DPYBIND11_DECLARATIONS_ONLY=1)
276+
file(GLOB SOURCES ${CMAKE_SOURCE_DIR}/include/pybind11/*.cpp)
277+
# Produce libpybind11.a, which contains position independent object files.
278+
# that may be used to create a shared library.
279+
add_library(pybind11 STATIC ${SOURCES})
280+
set_property(TARGET pybind11 PROPERTY POSITION_INDEPENDENT_CODE ON)

include/pybind11/attr.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ enum op_id : int;
119119
enum op_type : int;
120120
struct undefined_t;
121121
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_;
122-
inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
122+
void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
123123

124124
/// Internal data structure which holds metadata about a keyword argument
125125
struct argument_record {

include/pybind11/cast.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class loader_life_support {
8989
// Gets the cache entry for the given type, creating it if necessary. The return value is the pair
9090
// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was
9191
// just created.
92-
inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_type_info_get_cache(PyTypeObject *type);
92+
std::pair<decltype(internals::registered_types_py)::iterator, bool> all_type_info_get_cache(PyTypeObject *type);
9393

9494
// Populates a just-created cache entry.
9595
PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector<type_info *> &bases) {
@@ -481,7 +481,7 @@ inline PyThreadState *get_thread_state_unchecked() {
481481
}
482482

483483
// Forward declarations
484-
inline void keep_alive_impl(handle nurse, handle patient);
484+
void keep_alive_impl(handle nurse, handle patient);
485485
inline PyObject *make_new_instance(PyTypeObject *type);
486486

487487
class type_caster_generic {

include/pybind11/detail/common.h

+21-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,27 @@
8383
#if defined(_MSC_VER)
8484
# define PYBIND11_NOINLINE __declspec(noinline)
8585
#else
86-
# define PYBIND11_NOINLINE __attribute__ ((noinline))
86+
# if PYBIND11_DECLARATIONS_ONLY
87+
# define PYBIND11_NOINLINE __attribute__ ((noinline))
88+
# else
89+
// Otherwise GCC 9 would emit warnings for functions that are marked both
90+
// inline and noinline if we weren't enabling:
91+
// #pragma GCC diagnostic ignored "-Wattributes"
92+
// as is currently done in Pybind11. This way we remove reliance on this
93+
// disabled warning.
94+
# define PYBIND11_NOINLINE
95+
# endif
96+
#endif
97+
98+
// Must be used in every .cpp function definition:
99+
// - on header-only mode, functions must be marked as inline
100+
// to prevent multiple definitions
101+
// - on non-header-only mode, functions must not be marked as inline
102+
// so that the symbols will be visible from other objects
103+
#if PYBIND11_DECLARATIONS_ONLY
104+
# define PYBIND11_INLINE
105+
#else
106+
# define PYBIND11_INLINE inline
87107
#endif
88108

89109
#if defined(PYBIND11_CPP14)

include/pybind11/options.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include "options.h"
2+
3+
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
4+
5+
PYBIND11_INLINE options::options() : previous_state(global_state()) {}
6+
7+
PYBIND11_INLINE options::~options() {
8+
global_state() = previous_state;
9+
}
10+
11+
PYBIND11_INLINE options& options::disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; }
12+
13+
PYBIND11_INLINE options& options::enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; }
14+
15+
PYBIND11_INLINE options& options::disable_function_signatures() & { global_state().show_function_signatures = false; return *this; }
16+
17+
PYBIND11_INLINE options& options::enable_function_signatures() & { global_state().show_function_signatures = true; return *this; }
18+
19+
PYBIND11_INLINE bool options::show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; }
20+
21+
PYBIND11_INLINE bool options::show_function_signatures() { return global_state().show_function_signatures; }
22+
23+
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

include/pybind11/options.h

+12-10
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,30 @@ class options {
1717
public:
1818

1919
// Default RAII constructor, which leaves settings as they currently are.
20-
options() : previous_state(global_state()) {}
20+
options();
2121

2222
// Class is non-copyable.
2323
options(const options&) = delete;
2424
options& operator=(const options&) = delete;
2525

2626
// Destructor, which restores settings that were in effect before.
27-
~options() {
28-
global_state() = previous_state;
29-
}
27+
~options();
3028

3129
// Setter methods (affect the global state):
3230

33-
options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; }
31+
options& disable_user_defined_docstrings() &;
3432

35-
options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; }
33+
options& enable_user_defined_docstrings() &;
3634

37-
options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; }
35+
options& disable_function_signatures() &;
3836

39-
options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; }
37+
options& enable_function_signatures() &;
4038

4139
// Getter methods (return the global state):
4240

43-
static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; }
41+
static bool show_user_defined_docstrings();
4442

45-
static bool show_function_signatures() { return global_state().show_function_signatures; }
43+
static bool show_function_signatures();
4644

4745
// This type is not meant to be allocated on the heap.
4846
void* operator new(size_t) = delete;
@@ -63,3 +61,7 @@ class options {
6361
};
6462

6563
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
64+
65+
#if !PYBIND11_DECLARATIONS_ONLY
66+
#include "options.cpp"
67+
#endif

0 commit comments

Comments
 (0)