From f5fae929a39b0f0894fcf14bdcfe0d90ce7e55f3 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 24 Aug 2015 15:31:24 +0200 Subject: [PATCH] avoid std::string when creating signatures, and make nicer type names. binary size reduced by ~10% --- include/pybind/cast.h | 126 ++++++++++++++++++++++++++++++------ include/pybind/common.h | 2 +- include/pybind/functional.h | 6 +- include/pybind/pybind.h | 34 +++++----- include/pybind/stl.h | 5 +- 5 files changed, 133 insertions(+), 40 deletions(-) diff --git a/include/pybind/cast.h b/include/pybind/cast.h index c59fbcfcb..38b9d7f46 100644 --- a/include/pybind/cast.h +++ b/include/pybind/cast.h @@ -17,16 +17,98 @@ NAMESPACE_BEGIN(pybind) NAMESPACE_BEGIN(detail) +#if defined(_MSC_VER) +#define NOINLINE __declspec(noinline) +#else +#define NOINLINE __attribute__ ((noinline)) +#endif + +/** Linked list descriptor type for function signatures (produces smaller binaries + * compared to a previous solution using std::string and operator +=) */ +class descr { +public: + struct entry { + const std::type_info *type = nullptr; + const char *str = nullptr; + entry *next = nullptr; + entry(const std::type_info *type) : type(type) { } + entry(const char *str) : str(str) { } + }; + + descr() { } + descr(descr &&d) : first(d.first), last(d.last) { d.first = d.last = nullptr; } + NOINLINE descr(const char *str) { first = last = new entry { str }; } + NOINLINE descr(const std::type_info &type) { first = last = new entry { &type }; } + + NOINLINE void operator+(const char *str) { + entry *next = new entry { str }; + last->next = next; + last = next; + } + + NOINLINE void operator+(const std::type_info *type) { + entry *next = new entry { type }; + last->next = next; + last = next; + } + + NOINLINE void operator+=(descr &&other) { + last->next = other.first; + while (last->next) + last = last->next; + other.first = other.last = nullptr; + } + + NOINLINE friend descr operator+(descr &&l, descr &&r) { + descr result(std::move(l)); + result += std::move(r); + return result; + } + + NOINLINE std::string str() const { + std::string result; + auto const& registered_types = get_internals().registered_types; + for (entry *it = first; it != nullptr; it = it->next) { + if (it->type) { + auto it2 = registered_types.find(it->type); + if (it2 != registered_types.end()) { + result += it2->second.type->tp_name; + } else { + std::string tname(it->type->name()); + detail::clean_type_id(tname); + result += tname; + } + } else { + result += it->str; + } + } + return result; + } + + NOINLINE ~descr() { + while (first) { + entry *tmp = first->next; + delete first; + first = tmp; + } + } + + entry *first = nullptr; + entry *last = nullptr; +}; + +#undef NOINLINE + /// Generic type caster for objects stored on the heap template class type_caster { public: typedef instance instance_type; - static std::string name() { return type_id(); } + static descr descr() { return typeid(type); } type_caster() { auto const& registered_types = get_internals().registered_types; - auto it = registered_types.find(type_id()); + auto it = registered_types.find(&typeid(type)); if (it != registered_types.end()) typeinfo = &it->second; } @@ -70,7 +152,7 @@ public: Py_INCREF(inst); return inst; } - auto it = internals.registered_types.find(type_id()); + auto it = internals.registered_types.find(&typeid(type)); if (it == internals.registered_types.end()) { std::string msg = std::string("Unregistered type : ") + type_id(); PyErr_SetString(PyExc_TypeError, msg.c_str()); @@ -129,7 +211,7 @@ protected: protected: \ type value; \ public: \ - static std::string name() { return py_name; } \ + static descr descr() { return py_name; } \ static PyObject *cast(const type *src, return_value_policy policy, PyObject *parent) { \ return cast(*src, policy, parent); \ } \ @@ -239,7 +321,7 @@ public: return PyUnicode_DecodeLatin1(str, 1, nullptr); } - static std::string name() { return "str"; } + static descr descr() { return "str"; } operator char*() { return value; } operator char() { return *value; } @@ -272,8 +354,13 @@ public: return tuple; } - static std::string name() { - return "(" + type_caster::name() + ", " + type_caster::name() + ")"; + static descr descr() { + class descr result("("); + result += std::move(type_caster::type>::descr()); + result += ", "; + result += std::move(type_caster::type>::descr()); + result += ")"; + return result; } operator type() { @@ -297,23 +384,22 @@ public: return cast(src, policy, parent, typename make_index_sequence::type()); } - static std::string name(const char **keywords = nullptr, const char **values = nullptr) { - std::array names {{ - type_caster::type>::name()... + static descr descr(const char **keywords = nullptr, const char **values = nullptr) { + std::array descrs {{ + type_caster::type>::descr()... }}; - std::string result("("); - int counter = 0; - for (auto const &name : names) { - if (keywords && keywords[counter]) { - result += keywords[counter]; + class descr result("("); + for (int i=0; i results {{ (PyTuple_GET_ITEM(src, Indices) != nullptr ? std::get(value).load(PyTuple_GET_ITEM(src, Indices), convert) : false)... }}; - (void) convert; /* avoid a warning when the tuple is empty */ + (void) convert; /* avoid a warning when the tuple is empty */ for (bool r : results) if (!r) return false; diff --git a/include/pybind/common.h b/include/pybind/common.h index 0ee266825..8a9850dd5 100644 --- a/include/pybind/common.h +++ b/include/pybind/common.h @@ -139,7 +139,7 @@ struct type_info { /// Internal data struture used to track registered instances and types struct internals { - std::unordered_map registered_types; + std::unordered_map registered_types; std::unordered_map registered_instances; }; diff --git a/include/pybind/functional.h b/include/pybind/functional.h index 1979dea7d..1eedab67f 100644 --- a/include/pybind/functional.h +++ b/include/pybind/functional.h @@ -38,7 +38,11 @@ public: return f.ptr(); } - PYBIND_TYPE_CASTER(type, "function<" + type_caster>::name() + " -> " + type_caster::type>::name() + ">"); + + PYBIND_TYPE_CASTER(type, detail::descr("function<") + + type_caster>::descr() + detail::descr(" -> ") + + type_caster::type>::descr() + + detail::descr(">")); }; NAMESPACE_END(detail) diff --git a/include/pybind/pybind.h b/include/pybind/pybind.h index ecf06dbd7..dbf12900b 100644 --- a/include/pybind/pybind.h +++ b/include/pybind/pybind.h @@ -189,11 +189,12 @@ public: std::array kw{}, def{}; process_extras(((capture *) entry->data)->extras, entry, kw.data(), def.data()); - entry->signature = cast_in::name(kw.data(), def.data()); - entry->signature += " -> "; - entry->signature += cast_out::name(); - initialize(entry, sizeof...(Arg)); + detail::descr d = cast_in::descr(kw.data(), def.data()); + d += " -> "; + d += std::move(cast_out::descr()); + + initialize(entry, d, sizeof...(Arg)); } /// Delegating helper constructor to deal with lambda functions @@ -246,11 +247,11 @@ private: std::array kw{}, def{}; process_extras(((capture *) entry->data)->extras, entry, kw.data(), def.data()); - entry->signature = cast_in::name(kw.data(), def.data()); - entry->signature += " -> "; - entry->signature += cast_out::name(); + detail::descr d = cast_in::descr(kw.data(), def.data()); + d += " -> "; + d += std::move(cast_out::descr()); - initialize(entry, sizeof...(Arg)); + initialize(entry, d, sizeof...(Arg)); } static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs ) { @@ -322,9 +323,10 @@ private: } } - void initialize(function_entry *entry, int args) { + void initialize(function_entry *entry, const detail::descr &descr, int args) { if (entry->name == nullptr) entry->name = ""; + if (entry->keywords != 0 && entry->keywords != args) throw std::runtime_error( "cpp_function(): function \"" + std::string(entry->name) + "\" takes " + @@ -332,6 +334,7 @@ private: " pybind::arg entries were specified!"); entry->is_constructor = !strcmp(entry->name, "__init__"); + entry->signature = descr.str(); if (!entry->sibling || !PyCFunction_Check(entry->sibling)) { entry->def = new PyMethodDef(); @@ -423,7 +426,7 @@ class custom_type : public object { public: PYBIND_OBJECT_DEFAULT(custom_type, object, PyType_Check) - custom_type(object &scope, const char *name_, const std::string &type_name, + custom_type(object &scope, const char *name_, const std::type_info *tinfo, size_t type_size, size_t instance_size, void (*init_holder)(PyObject *), const destructor &dealloc, PyObject *parent, const char *doc) { @@ -465,7 +468,7 @@ public: if (((module &) scope).check()) attr("__module__") = scope_name; - auto &type_info = detail::get_internals().registered_types[type_name]; + auto &type_info = detail::get_internals().registered_types[tinfo]; type_info.type = (PyTypeObject *) m_ptr; type_info.type_size = type_size; type_info.init_holder = init_holder; @@ -592,13 +595,13 @@ public: PYBIND_OBJECT(class_, detail::custom_type, PyType_Check) class_(object &scope, const char *name, const char *doc = nullptr) - : detail::custom_type(scope, name, type_id(), sizeof(type), + : detail::custom_type(scope, name, &typeid(type), sizeof(type), sizeof(instance_type), init_holder, dealloc, nullptr, doc) { } class_(object &scope, const char *name, object &parent, const char *doc = nullptr) - : detail::custom_type(scope, name, type_id(), sizeof(type), + : detail::custom_type(scope, name, &typeid(type), sizeof(type), sizeof(instance_type), init_holder, dealloc, parent.ptr(), doc) { } @@ -790,11 +793,10 @@ template void implicitly_convertible() PyErr_Clear(); return result; }; - std::string output_type_name = type_id(); auto & registered_types = detail::get_internals().registered_types; - auto it = registered_types.find(output_type_name); + auto it = registered_types.find(&typeid(OutputType)); if (it == registered_types.end()) - throw std::runtime_error("implicitly_convertible: Unable to find type " + output_type_name); + throw std::runtime_error("implicitly_convertible: Unable to find type " + type_id()); it->second.implicit_conversions.push_back(implicit_caster); } diff --git a/include/pybind/stl.h b/include/pybind/stl.h index 90c583b0a..36568a056 100644 --- a/include/pybind/stl.h +++ b/include/pybind/stl.h @@ -54,7 +54,7 @@ public: } return list; } - PYBIND_TYPE_CASTER(type, "list<" + value_conv::name() + ">"); + PYBIND_TYPE_CASTER(type, detail::descr("list<") + value_conv::descr() + detail::descr(">")); }; template struct type_caster> { @@ -96,7 +96,8 @@ public: } return dict; } - PYBIND_TYPE_CASTER(type, "dict<" + key_conv::name() + ", " + value_conv::name() + ">"); + + PYBIND_TYPE_CASTER(type, detail::descr("dict<") + key_conv::descr() + detail::descr(", ") + value_conv::descr() + detail::descr(">")); }; inline std::ostream &operator<<(std::ostream &os, const object &obj) { os << (const char *) obj.str(); return os; }