Skip to content

Replace std::variant/visit with function pointer table dispatch for faster compilation#426

Open
WillAyd wants to merge 1 commit intomainfrom
fast-numeric-compile
Open

Replace std::variant/visit with function pointer table dispatch for faster compilation#426
WillAyd wants to merge 1 commit intomainfrom
fast-numeric-compile

Conversation

@WillAyd
Copy link
Copy Markdown
Collaborator

@WillAyd WillAyd commented Mar 16, 2026

The previous approach created two 39-element std::variant types and used std::visit to dispatch on their cartesian product (39² = 1521 combinations per call site, 3 sites total). std::visit is notoriously compile-time heavy due to its internal dispatch table generation and type deduction.

This replaces it with integral_dispatch, which builds a std::array of function pointers for O(1) runtime dispatch without any variant machinery. Each stateless lambda converts directly to a function pointer. The nested two-level dispatch generates the same template instantiations but avoids the compile-time cost of variant construction and visit table generation.

Changes

  • numeric_gen.hpp: Replace to_integral_variant (variant + array construction) with integral_dispatch (function pointer table + direct index)
  • reader.cpp: Convert std::visit call to nested integral_dispatch calls
  • writer.cpp: Convert both std::visit calls to nested integral_dispatch calls
  • Remove <variant> includes from all three files

The previous approach created two 39-element std::variant types and used
std::visit to dispatch on their cartesian product (39² = 1521 combinations
per call site, 3 sites total). std::visit is notoriously compile-time
heavy due to its internal dispatch table generation and type deduction.

Replace with integral_dispatch, which builds a std::array of function
pointers for O(1) runtime dispatch without any variant machinery. Each
stateless lambda converts directly to a function pointer. The nested
two-level dispatch generates the same template instantiations but avoids
the compile-time cost of variant construction and visit table generation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces std::variant/std::visit-based runtime-to-compile-time dispatch for Hyper Numeric<P,S> with a function-pointer table dispatcher to significantly reduce compile-time overhead in decimal read/write paths.

Changes:

  • Introduce integral_dispatch<N> in numeric_gen.hpp to dispatch a runtime index to a compile-time std::integral_constant.
  • Update decimal handling in reader.cpp and writer.cpp to use nested integral_dispatch instead of std::visit over two variants.
  • Remove <variant> includes now that variant-based dispatch is eliminated.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/pantab/numeric_gen.hpp Adds integral_dispatch function-pointer table for integral index dispatch.
src/pantab/reader.cpp Replaces std::visit decimal conversion with nested integral_dispatch.
src/pantab/writer.cpp Replaces std::visit decimal insert logic with nested integral_dispatch in two call sites.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +15 to +23
constexpr void integral_dispatch(std::size_t n, F &&f) {
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
using FnPtr = void (*)(F &);
const std::array<FnPtr, N> table{
{static_cast<FnPtr>([](F &fn) {
fn(std::integral_constant<std::size_t, Is>{});
})...}};
table[n](f);
}(std::make_index_sequence<N>{});
Comment thread src/pantab/writer.cpp
Comment on lines +431 to +437
integral_dispatch<PrecisionLimit>(precision_, [&](auto P) {
integral_dispatch<PrecisionLimit>(scale_, [&](auto S) {
if constexpr (S() <= P()) {
InsertNull<hyperapi::Numeric<P(), S()>>();
}
});
});
Comment thread src/pantab/writer.cpp
Comment on lines +473 to +480
integral_dispatch<PrecisionLimit>(precision_, [&](auto P) {
integral_dispatch<PrecisionLimit>(scale_, [&](auto S) {
if constexpr (S() <= P()) {
const auto value = hyperapi::Numeric<P(), S()>{str};
InsertValue(std::move(value));
}
});
});
Comment thread src/pantab/reader.cpp
Comment on lines +298 to +310
const auto decimal_string = [&]() -> std::string {
std::string result;
integral_dispatch<PrecisionLimit>(precision_, [&](auto P) {
integral_dispatch<PrecisionLimit>(scale_, [&](auto S) {
if constexpr (S() <= P()) {
const auto decimal_value = value.get<hyperapi::Numeric<P(), S()>>();
auto value_string = decimal_value.toString();
std::erase(value_string, '.');
return value_string;
result = decimal_value.toString();
std::erase(result, '.');
}
throw "unreachable";
},
to_integral_variant<PrecisionLimit>(precision_),
to_integral_variant<PrecisionLimit>(scale_));
});
});
return result;
}();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants