Modeling Data - Optimize BSplCLib interpolation and blend evaluation erformance (#1082)

Profiling identified several performance bottlenecks in the BSpline interpolation and blend surface computation pipeline. This commit addresses them through four categories of optimization:

1. Static initialization for GeomFill convertors: the monomial-to-BSpline conversion matrices in GeomFill_QuasiAngularConvertor::Init() and GeomFill_PolynomialConvertor::Init() are mathematical constants that were recomputed on every call via Convert_CompPolynomialToPoles. Now computed once via static lambda-initialized locals.

2. Stack allocation for small matrices/arrays: InterpolationMatrix in BSplCLib::Interpolate, aBSplineBasis in BuildBSpMatrix, and parameters/contact_array in Convert_CompPolynomialToPoles::Perform now use stack buffers when sizes fit, avoiding heap allocation.

3. Raw pointer access in hot loops: replaced multi-layer accessor chains (math_Matrix::Value -> math_DoubleTab::Value -> NCollection_Array2::Value -> NCollection_Array1::at with bounds checks) with direct pointer arithmetic in EvalBsplineBasis, FactorBandedMatrix, BuildBSpMatrix, SolveBandedSystem, and math_VectorBase operations (Multiply, TMultiply, Multiplied, Norm, Norm2).

4. Eliminated redundant recomputation: cached AdvApprox_ApproxAFunction:: NbPoles() results in Approx_SweepApproximation, Approx_CurveOnSurface, and Approx_Curve2d instead of recomputing BSplCLib::NbPoles in inner loops. Cached math_FunctionSetRoot solver in BRepBlend_AppFuncRoot to avoid repeated construction/destruction per SearchPoint call.

Also fixed undefined behavior in BSplCLib::NbPoles where pointer arithmetic created a pointer before the array start (pmu -= f).
This commit is contained in:
Pasukhin Dmitry
2026-02-13 21:35:03 +00:00
committed by GitHub
parent a9f45dfa15
commit ea9443d154
11 changed files with 305 additions and 259 deletions
@@ -149,12 +149,13 @@ Approx_Curve2d::Approx_Curve2d(const occ::handle<Adaptor2d_Curve2d>& C2D,
if (myHasResult)
{
NCollection_Array1<gp_Pnt2d> Poles2d(1, aApprox.NbPoles());
NCollection_Array1<double> Poles1dU(1, aApprox.NbPoles());
const int aNbPoles = aApprox.NbPoles();
NCollection_Array1<gp_Pnt2d> Poles2d(1, aNbPoles);
NCollection_Array1<double> Poles1dU(1, aNbPoles);
aApprox.Poles1d(1, Poles1dU);
NCollection_Array1<double> Poles1dV(1, aApprox.NbPoles());
NCollection_Array1<double> Poles1dV(1, aNbPoles);
aApprox.Poles1d(2, Poles1dV);
for (int i = 1; i <= aApprox.NbPoles(); i++)
for (int i = 1; i <= aNbPoles; i++)
Poles2d.SetValue(i, gp_Pnt2d(Poles1dU.Value(i), Poles1dV.Value(i)));
occ::handle<NCollection_HArray1<double>> Knots = aApprox.Knots();
@@ -502,21 +502,22 @@ void Approx_CurveOnSurface::Perform(const int theMaxSegments,
occ::handle<NCollection_HArray1<int>> Mults = aApprox.Multiplicities();
int Degree = aApprox.Degree();
const int aNbPoles = aApprox.NbPoles();
if (!theOnly2d)
{
NCollection_Array1<gp_Pnt> Poles(1, aApprox.NbPoles());
NCollection_Array1<gp_Pnt> Poles(1, aNbPoles);
aApprox.Poles(1, Poles);
myCurve3d = new Geom_BSplineCurve(Poles, Knots->Array1(), Mults->Array1(), Degree);
myError3d = aApprox.MaxError(3, 1);
}
if (!theOnly3d)
{
NCollection_Array1<gp_Pnt2d> Poles2d(1, aApprox.NbPoles());
NCollection_Array1<double> Poles1dU(1, aApprox.NbPoles());
NCollection_Array1<gp_Pnt2d> Poles2d(1, aNbPoles);
NCollection_Array1<double> Poles1dU(1, aNbPoles);
aApprox.Poles1d(1, Poles1dU);
NCollection_Array1<double> Poles1dV(1, aApprox.NbPoles());
NCollection_Array1<double> Poles1dV(1, aNbPoles);
aApprox.Poles1d(2, Poles1dV);
for (int i = 1; i <= aApprox.NbPoles(); i++)
for (int i = 1; i <= aNbPoles; i++)
Poles2d.SetValue(i, gp_Pnt2d(Poles1dU.Value(i), Poles1dV.Value(i)));
myCurve2d = new Geom2d_BSplineCurve(Poles2d, Knots->Array1(), Mults->Array1(), Degree);
@@ -303,12 +303,13 @@ void Approx_SweepApproximation::Approximation(
// --> Fill Champs of the surface ----
int ii, jj;
vdeg = Approx.Degree();
vdeg = Approx.Degree();
const int aNbPoles = Approx.NbPoles();
// Unfortunately Adv_Approx stores the transposition of the required
// so, writing tabPoles = Approx.Poles() will give an erroneous result
// It is only possible to allocate and recopy term by term...
tabPoles = new (NCollection_HArray2<gp_Pnt>)(1, Num3DSS, 1, Approx.NbPoles());
tabWeights = new (NCollection_HArray2<double>)(1, Num3DSS, 1, Approx.NbPoles());
tabPoles = new (NCollection_HArray2<gp_Pnt>)(1, Num3DSS, 1, aNbPoles);
tabWeights = new (NCollection_HArray2<double>)(1, Num3DSS, 1, aNbPoles);
if (Num1DSS == Num3DSS)
{
@@ -316,7 +317,7 @@ void Approx_SweepApproximation::Approximation(
gp_Pnt P;
for (ii = 1; ii <= Num3DSS; ii++)
{
for (jj = 1; jj <= Approx.NbPoles(); jj++)
for (jj = 1; jj <= aNbPoles; jj++)
{
P = Approx.Poles()->Value(jj, ii);
wpoid = Approx.Poles1d()->Value(jj, ii);
@@ -332,7 +333,7 @@ void Approx_SweepApproximation::Approximation(
tabWeights->Init(1);
for (ii = 1; ii <= Num3DSS; ii++)
{
for (jj = 1; jj <= Approx.NbPoles(); jj++)
for (jj = 1; jj <= aNbPoles; jj++)
{
tabPoles->SetValue(ii, jj, Approx.Poles()->Value(jj, ii));
}
@@ -355,10 +356,10 @@ void Approx_SweepApproximation::Approximation(
{
TrsfInv = AAffin->Value(ii).Inverted();
occ::handle<NCollection_HArray1<gp_Pnt2d>> P2d =
new (NCollection_HArray1<gp_Pnt2d>)(1, Approx.NbPoles());
new (NCollection_HArray1<gp_Pnt2d>)(1, aNbPoles);
Approx.Poles2d(ii, P2d->ChangeArray1());
// do not forget to apply inverted homothety.
for (jj = 1; jj <= Approx.NbPoles(); jj++)
for (jj = 1; jj <= aNbPoles; jj++)
{
TrsfInv.Transforms(P2d->ChangeValue(jj).ChangeCoord());
}