-
Notifications
You must be signed in to change notification settings - Fork 267
perf: Speed up type-erased SourceLink unpacking #5829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -236,14 +236,14 @@ | |
| T& as() { | ||
| static_assert(std::is_same_v<T, std::decay_t<T>>, | ||
| "Please pass the raw type, no const or ref"); | ||
| if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) { | ||
| throw std::bad_any_cast{}; | ||
| if (!holds<T>()) { | ||
| throwBadAnyCast(); | ||
| } | ||
|
|
||
| _ACTS_ANY_VERBOSE("Get as " | ||
| << (m_handler->heapAllocated ? "heap" : "local")); | ||
|
|
||
| return *std::bit_cast<T*>(dataPtr()); | ||
| return *std::bit_cast<T*>(dataPtrFor<T>()); | ||
| } | ||
|
|
||
| /// Get const reference to stored value of specified type | ||
|
|
@@ -254,14 +254,14 @@ | |
| const T& as() const { | ||
| static_assert(std::is_same_v<T, std::decay_t<T>>, | ||
| "Please pass the raw type, no const or ref"); | ||
| if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) { | ||
| throw std::bad_any_cast{}; | ||
| if (!holds<T>()) { | ||
| throwBadAnyCast(); | ||
| } | ||
|
|
||
| _ACTS_ANY_VERBOSE("Get as " | ||
| << (m_handler->heapAllocated ? "heap" : "local")); | ||
|
|
||
| return *std::bit_cast<const T*>(dataPtr()); | ||
| return *std::bit_cast<const T*>(dataPtrFor<T>()); | ||
| } | ||
|
|
||
| /// Get pointer to stored value of specified type | ||
|
|
@@ -272,10 +272,10 @@ | |
| T* asPtr() { | ||
| static_assert(std::is_same_v<T, std::decay_t<T>>, | ||
| "Please pass the raw type, no const or ref"); | ||
| if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) { | ||
| if (!holds<T>()) { | ||
| return nullptr; | ||
| } | ||
| return std::bit_cast<T*>(dataPtr()); | ||
| return std::bit_cast<T*>(dataPtrFor<T>()); | ||
| } | ||
|
|
||
| /// Get const pointer to stored value of specified type | ||
|
|
@@ -286,10 +286,10 @@ | |
| const T* asPtr() const { | ||
| static_assert(std::is_same_v<T, std::decay_t<T>>, | ||
| "Please pass the raw type, no const or ref"); | ||
| if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) { | ||
| if (!holds<T>()) { | ||
| return nullptr; | ||
| } | ||
| return std::bit_cast<const T*>(dataPtr()); | ||
| return std::bit_cast<const T*>(dataPtrFor<T>()); | ||
| } | ||
|
|
||
| /// Move the stored value out. Leaves this Any empty. | ||
|
|
@@ -300,10 +300,10 @@ | |
| T take() { | ||
| static_assert(std::is_same_v<T, std::decay_t<T>>, | ||
| "Please pass the raw type, no const or ref"); | ||
| if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) { | ||
| throw std::bad_any_cast{}; | ||
| if (!holds<T>()) { | ||
| throwBadAnyCast(); | ||
| } | ||
| T* ptr = std::bit_cast<T*>(dataPtr()); | ||
| T* ptr = std::bit_cast<T*>(dataPtrFor<T>()); | ||
| T value = std::move(*ptr); | ||
| destroy(); | ||
| return value; | ||
|
|
@@ -452,7 +452,7 @@ | |
| bool is() const { | ||
| static_assert(std::is_same_v<T, std::decay_t<T>>, | ||
| "Please pass the raw type, no const or ref"); | ||
| return m_handler != nullptr && m_handler->typeHash == typeHash<T>(); | ||
| return holds<T>(); | ||
| } | ||
|
|
||
| // The base accessors below are member templates on a dummy @c B defaulting to | ||
|
|
@@ -527,6 +527,42 @@ | |
| } | ||
|
|
||
| private: | ||
| // The handler is a per-type singleton, so a pointer comparison settles the | ||
| // common case. The hash comparison covers handlers duplicated across shared | ||
| // objects, where the pointers differ but the type does not. | ||
| template <typename T> | ||
| bool holds() const { | ||
| if (m_handler == makeHandler<T>()) [[likely]] { | ||
| return true; | ||
| } | ||
| return m_handler != nullptr && m_handler->typeHash == typeHash<T>(); | ||
| } | ||
|
|
||
| // Cold and out-of-line to keep the accessors inlinable. | ||
| [[noreturn]] [[gnu::noinline, gnu::cold]] static void throwBadAnyCast() { | ||
| throw std::bad_any_cast{}; | ||
| } | ||
|
|
||
| // T is known statically here, so unlike dataPtr() this needs no load of | ||
| // m_handler->heapAllocated and no branch on it. | ||
| template <typename T> | ||
| void* dataPtrFor() { | ||
|
Check failure on line 549 in Core/include/Acts/Utilities/Any.hpp
|
||
| if constexpr (heapAllocated<T>()) { | ||
| return *std::bit_cast<void**>(m_data.data()); | ||
| } else { | ||
| return std::bit_cast<void*>(m_data.data()); | ||
| } | ||
| } | ||
|
|
||
| template <typename T> | ||
| const void* dataPtrFor() const { | ||
|
Check failure on line 558 in Core/include/Acts/Utilities/Any.hpp
|
||
| if constexpr (heapAllocated<T>()) { | ||
| return *std::bit_cast<void* const*>(m_data.data()); | ||
| } else { | ||
| return std::bit_cast<const void*>(m_data.data()); | ||
| } | ||
| } | ||
|
|
||
| void* dataPtr() { | ||
| if (m_handler->heapAllocated) { | ||
| return *std::bit_cast<void**>(m_data.data()); | ||
|
|
@@ -563,60 +599,55 @@ | |
| const std::type_info* typeInfo{nullptr}; | ||
| }; | ||
|
|
||
| // Constant so that the singleton below is constant-initialized and needs no | ||
| // thread-safe-static guard. | ||
| template <typename T> | ||
| static const Handler* makeHandler() { | ||
| static_assert(!std::is_base_of_v<AnyBaseAll, std::decay_t<T>>, | ||
| "Cannot wrap Any in Any"); | ||
| static const Handler static_handler = []() { | ||
| Handler h; | ||
| h.heapAllocated = heapAllocated<T>(); | ||
| if constexpr (!std::is_trivially_destructible_v<T> || | ||
| heapAllocated<T>()) { | ||
| h.destroy = &destroyImpl<T>; | ||
| } | ||
| if constexpr (!heapAllocated<T>() && | ||
| !std::is_trivially_move_constructible_v<T>) { | ||
| h.moveConstruct = &moveConstructImpl<T>; | ||
| } | ||
| if constexpr (!heapAllocated<T>() && | ||
| !std::is_trivially_move_assignable_v<T>) { | ||
| h.move = &moveImpl<T>; | ||
| } | ||
| if constexpr (std::is_copy_constructible_v<T> && | ||
| (!std::is_trivially_copy_constructible_v<T> || | ||
| heapAllocated<T>())) { | ||
| h.copyConstruct = ©ConstructImpl<T>; | ||
| } | ||
| static constexpr Handler makeHandlerValue() { | ||
| Handler h; | ||
| h.heapAllocated = heapAllocated<T>(); | ||
| if constexpr (!std::is_trivially_destructible_v<T> || heapAllocated<T>()) { | ||
| h.destroy = &destroyImpl<T>; | ||
| } | ||
| if constexpr (!heapAllocated<T>() && | ||
| !std::is_trivially_move_constructible_v<T>) { | ||
| h.moveConstruct = &moveConstructImpl<T>; | ||
| } | ||
| if constexpr (!heapAllocated<T>() && | ||
| !std::is_trivially_move_assignable_v<T>) { | ||
| h.move = &moveImpl<T>; | ||
| } | ||
| if constexpr (std::is_copy_constructible_v<T> && | ||
| (!std::is_trivially_copy_constructible_v<T> || | ||
| heapAllocated<T>())) { | ||
| h.copyConstruct = ©ConstructImpl<T>; | ||
| } | ||
|
|
||
| if constexpr (std::is_copy_assignable_v<T> && | ||
| (!std::is_trivially_copy_assignable_v<T> || | ||
| heapAllocated<T>())) { | ||
| h.copy = ©Impl<T>; | ||
| } | ||
| if constexpr (std::is_copy_assignable_v<T> && | ||
| (!std::is_trivially_copy_assignable_v<T> || | ||
| heapAllocated<T>())) { | ||
| h.copy = ©Impl<T>; | ||
| } | ||
|
|
||
| if constexpr (!std::is_void_v<Base>) { | ||
| h.upcast = [](void* p) -> Base* { | ||
| return static_cast<Base*>(static_cast<T*>(p)); | ||
| }; | ||
| h.upcastConst = [](const void* p) -> const Base* { | ||
| return static_cast<const Base*>(static_cast<const T*>(p)); | ||
| }; | ||
| } | ||
| if constexpr (!std::is_void_v<Base>) { | ||
| h.upcast = [](void* p) -> Base* { | ||
| return static_cast<Base*>(static_cast<T*>(p)); | ||
| }; | ||
| h.upcastConst = [](const void* p) -> const Base* { | ||
| return static_cast<const Base*>(static_cast<const T*>(p)); | ||
| }; | ||
| } | ||
|
|
||
| h.typeHash = typeHash<T>(); | ||
| h.typeInfo = &typeid(T); | ||
| h.typeHash = typeHash<T>(); | ||
| h.typeInfo = &typeid(T); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought typeid is not constexpr?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| _ACTS_ANY_DEBUG("Type: " << typeid(T).name()); | ||
| _ACTS_ANY_DEBUG(" -> destroy: " << h.destroy); | ||
| _ACTS_ANY_DEBUG(" -> moveConstruct: " << h.moveConstruct); | ||
| _ACTS_ANY_DEBUG(" -> move: " << h.move); | ||
| _ACTS_ANY_DEBUG(" -> copyConstruct: " << h.copyConstruct); | ||
| _ACTS_ANY_DEBUG(" -> copy: " << h.copy); | ||
| _ACTS_ANY_DEBUG( | ||
| " -> heapAllocated: " << (h.heapAllocated ? "yes" : "no")); | ||
|
andiwand marked this conversation as resolved.
|
||
| return h; | ||
| } | ||
|
|
||
| return h; | ||
| }(); | ||
| template <typename T> | ||
| static const Handler* makeHandler() { | ||
| static_assert(!std::is_base_of_v<AnyBaseAll, std::decay_t<T>>, | ||
| "Cannot wrap Any in Any"); | ||
| static constexpr Handler static_handler = makeHandlerValue<T>(); | ||
| return &static_handler; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // This file is part of the ACTS project. | ||
| // | ||
| // Copyright (C) 2016 CERN for the benefit of the ACTS project | ||
| // | ||
| // 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/. | ||
|
|
||
| #include "Acts/Utilities/HashedString.hpp" | ||
|
|
||
| namespace Acts::detail { | ||
| namespace { | ||
| struct TypeHashProbeA {}; | ||
| struct TypeHashProbeB {}; | ||
| } // namespace | ||
|
|
||
| // Fail the build on a compiler that omits the template arguments from | ||
| // std::source_location::function_name(), rather than silently collide hashes. | ||
| static_assert(typeHash<TypeHashProbeA>() != typeHash<TypeHashProbeB>(), | ||
| "typeHash<T> does not distinguish types on this compiler: " | ||
| "std::source_location::function_name() apparently omits template " | ||
| "arguments. Fall back to __PRETTY_FUNCTION__ / __FUNCSIG__."); | ||
|
|
||
| } // namespace Acts::detail |
Uh oh!
There was an error while loading. Please reload this page.