Skip to content

Foundation Classes - Optimize PLib polynomial evaluation - #953

Merged
dpasukhi merged 2 commits into
Open-Cascade-SAS:IRfrom
dpasukhi:plib_opt_cache_friendly
Dec 25, 2025
Merged

dpasukhi merged 2 commits into
Open-Cascade-SAS:IRfrom
dpasukhi:plib_opt_cache_friendly

Conversation

@dpasukhi

Copy link
Copy Markdown
Member

Refactor PLib::EvalPolynomial and PLib::NoDerivativeEvalPolynomial for improved performance:

  • Replace memcpy/memset with direct initialization loops to enable compiler register allocation for small dimensions
  • Use local stack arrays instead of writing to output during computation, reducing memory round-trips (write only at the end)
  • Fuse derivative and value update loops to read intermediate values once per iteration instead of twice
  • Replace repetitive switch-case statements (~180 lines) with constexpr dispatch tables using std::array and std::integer_sequence
  • Add optimized runtime fallback functions for dimensions > 15

Performance improvements:

  • Dimensions 1-15: Compiler can allocate local arrays in CPU registers
  • All cases: Eliminated memcpy/memset function call overhead
  • Fused loops reduce memory reads by ~50%

Refactor PLib::EvalPolynomial and PLib::NoDerivativeEvalPolynomial
for improved performance:

- Replace memcpy/memset with direct initialization loops to enable
  compiler register allocation for small dimensions
- Use local stack arrays instead of writing to output during computation,
  reducing memory round-trips (write only at the end)
- Fuse derivative and value update loops to read intermediate values
  once per iteration instead of twice
- Replace repetitive switch-case statements (~180 lines) with constexpr
  dispatch tables using std::array and std::integer_sequence
- Add optimized runtime fallback functions for dimensions > 15

Performance improvements:
- Dimensions 1-15: Compiler can allocate local arrays in CPU registers
- All cases: Eliminated memcpy/memset function call overhead
- Fused loops reduce memory reads by ~50%
@dpasukhi dpasukhi added this to the Release 8.0 milestone Dec 24, 2025
@dpasukhi
dpasukhi requested a review from Copilot December 24, 2025 18:18
@dpasukhi dpasukhi self-assigned this Dec 24, 2025
@dpasukhi dpasukhi added 2. Enhancement New feature or request 1. Foundation Classes Containers, system calls wrappers, smart pointers and other low level of OCCT code labels Dec 24, 2025

Copilot AI left a comment

Copy link
Copy Markdown

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 optimizes polynomial evaluation in the PLib package by refactoring the EvalPolynomial and NoDerivativeEvalPolynomial functions. The changes aim to improve performance through better memory access patterns and compiler optimization opportunities.

Key changes:

  • Replaced memcpy/memset calls with direct initialization loops to enable register allocation for small dimensions (1-15)
  • Introduced local stack arrays that reduce memory round-trips by writing to output only at the end of computation
  • Fused derivative and value update loops to minimize redundant memory reads

eval_step1<dim - 1>(poly, par, coef);
poly[dim] = poly[dim] * par + coef[dim];
}
double aLocal[dim];

Copilot AI Dec 24, 2025

Copy link

Choose a reason for hiding this comment

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

Variable-length arrays (VLA) are not part of standard C++ (dim is a template parameter but the array declaration syntax resembles VLA). While this code is valid because dim is a compile-time constant template parameter, consider using std::array<double, dim> for clarity and to match the modern C++ style used elsewhere in the file (e.g., dispatch tables use std::array).

Copilot uses AI. Check for mistakes.
Comment on lines +747 to +748
double aLocal0[dim];
double aLocal1[dim];

Copilot AI Dec 24, 2025

Copy link

Choose a reason for hiding this comment

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

Variable-length arrays (VLA) are not part of standard C++ (dim is a template parameter but the array declaration syntax resembles VLA). While this code is valid because dim is a compile-time constant template parameter, consider using std::array<double, dim> for clarity and consistency with modern C++ patterns used in the dispatch tables.

Copilot uses AI. Check for mistakes.
Comment on lines +783 to +785
double aLocal0[dim];
double aLocal1[dim];
double aLocal2[dim];

Copilot AI Dec 24, 2025

Copy link

Choose a reason for hiding this comment

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

Variable-length arrays (VLA) are not part of standard C++ (dim is a template parameter but the array declaration syntax resembles VLA). While this code is valid because dim is a compile-time constant template parameter, consider using std::array<double, dim> for clarity and to align with the modern C++ idioms used elsewhere in this file.

Copilot uses AI. Check for mistakes.
Comment on lines +939 to +947
static const std::array<EvalPoly0Func, THE_MAX_OPT_DIM> THE_EVAL_POLY0_TABLE =
makeDispatchTable<EvalPoly0Wrapper, EvalPoly0Func>(
std::make_integer_sequence<int, THE_MAX_OPT_DIM>{});

static const std::array<EvalPoly1Func, THE_MAX_OPT_DIM> THE_EVAL_POLY1_TABLE =
makeDispatchTable<EvalPoly1Wrapper, EvalPoly1Func>(
std::make_integer_sequence<int, THE_MAX_OPT_DIM>{});

static const std::array<EvalPoly2Func, THE_MAX_OPT_DIM> THE_EVAL_POLY2_TABLE =

Copilot AI Dec 24, 2025

Copy link

Choose a reason for hiding this comment

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

These static const dispatch tables in an anonymous namespace may cause initialization order issues across translation units if accessed before static initialization completes. Consider using constexpr instead of static const, or implement as inline constexpr functions returning local constexpr arrays to guarantee initialization at compile time and avoid potential static initialization order fiasco.

Suggested change
static const std::array<EvalPoly0Func, THE_MAX_OPT_DIM> THE_EVAL_POLY0_TABLE =
makeDispatchTable<EvalPoly0Wrapper, EvalPoly0Func>(
std::make_integer_sequence<int, THE_MAX_OPT_DIM>{});
static const std::array<EvalPoly1Func, THE_MAX_OPT_DIM> THE_EVAL_POLY1_TABLE =
makeDispatchTable<EvalPoly1Wrapper, EvalPoly1Func>(
std::make_integer_sequence<int, THE_MAX_OPT_DIM>{});
static const std::array<EvalPoly2Func, THE_MAX_OPT_DIM> THE_EVAL_POLY2_TABLE =
inline constexpr std::array<EvalPoly0Func, THE_MAX_OPT_DIM> THE_EVAL_POLY0_TABLE =
makeDispatchTable<EvalPoly0Wrapper, EvalPoly0Func>(
std::make_integer_sequence<int, THE_MAX_OPT_DIM>{});
inline constexpr std::array<EvalPoly1Func, THE_MAX_OPT_DIM> THE_EVAL_POLY1_TABLE =
makeDispatchTable<EvalPoly1Wrapper, EvalPoly1Func>(
std::make_integer_sequence<int, THE_MAX_OPT_DIM>{});
inline constexpr std::array<EvalPoly2Func, THE_MAX_OPT_DIM> THE_EVAL_POLY2_TABLE =

Copilot uses AI. Check for mistakes.
@dpasukhi
dpasukhi merged commit 0b9a5e0 into Open-Cascade-SAS:IR Dec 25, 2025
24 checks passed
@dpasukhi
dpasukhi deleted the plib_opt_cache_friendly branch December 25, 2025 22:56
@github-project-automation github-project-automation Bot moved this from Todo to Done in Maintenance Dec 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1. Foundation Classes Containers, system calls wrappers, smart pointers and other low level of OCCT code 2. Enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants