From be887d7884d4c14a3b0763c357506ae046e4e2a9 Mon Sep 17 00:00:00 2001
From: azv <azv@opencascade.com>
Date: Fri, 5 Aug 2016 15:21:34 +0300
Subject: [PATCH] 0027074: Support of multi-span cache in Geom[2d]Adaptor

The multi-span cache is stored as matrix (for surface) or vector (for curves) of caches. Each cell corresponds to a span. The cache is initialized on first call and destroyed when adaptor is destroyed.

1. Multi-span cache for curve and surfaces.
2. Revision of BSplSLib_Cache and small improvements.
3. Renew cache when loading same curve/surface into adaptor.
4. Optimizations for single-span cache
5. Remove unused constructors in BSpl*Lib_Cache classes
6. Allocate cache on first call, not when loading a curve/surface
7. Note in upgrade.md about multi-span implementation
---
 dox/dev_guides/upgrade/upgrade.md         |   8 +
 src/BSplCLib/BSplCLib_Cache.cxx           | 183 +++++------------
 src/BSplCLib/BSplCLib_Cache.hxx           | 116 ++++-------
 src/BSplCLib/BSplCLib_MultiSpanCache.gxx  | 158 +++++++++++++++
 src/BSplCLib/BSplCLib_MultiSpanCache.hxx  | 190 +++++++++++++++++
 src/BSplCLib/FILES                        |   2 +
 src/BSplSLib/BSplSLib_Cache.cxx           | 224 ++++++---------------
 src/BSplSLib/BSplSLib_Cache.hxx           |  95 ++++-----
 src/BSplSLib/BSplSLib_MultiSpanCache.cxx  | 235 ++++++++++++++++++++++
 src/BSplSLib/BSplSLib_MultiSpanCache.hxx  | 171 ++++++++++++++++
 src/BSplSLib/FILES                        |   2 +
 src/Geom2dAdaptor/Geom2dAdaptor_Curve.cxx |  94 ++++-----
 src/Geom2dAdaptor/Geom2dAdaptor_Curve.hxx |   9 +-
 src/GeomAdaptor/GeomAdaptor_Curve.cxx     |  90 ++++-----
 src/GeomAdaptor/GeomAdaptor_Curve.hxx     |   9 +-
 src/GeomAdaptor/GeomAdaptor_Surface.cxx   | 102 +++++-----
 src/GeomAdaptor/GeomAdaptor_Surface.hxx   |  12 +-
 src/QABugs/QABugs_19.cxx                  |  64 ++++++
 tests/blend/simple/Q6                     |   2 +-
 tests/bugs/modalg_6/bug25613_1            |   2 +-
 tests/bugs/modalg_6/bug25613_2            |   2 +-
 tests/de/step_2/R2                        |   4 +-
 tests/de/step_2/T1                        |   4 +-
 tests/perf/bspline/hugenurbs              |  39 ++++
 tests/perf/bspline/multispan              |  64 ++++++
 25 files changed, 1273 insertions(+), 608 deletions(-)
 create mode 100644 src/BSplCLib/BSplCLib_MultiSpanCache.gxx
 create mode 100644 src/BSplCLib/BSplCLib_MultiSpanCache.hxx
 create mode 100644 src/BSplSLib/BSplSLib_MultiSpanCache.cxx
 create mode 100644 src/BSplSLib/BSplSLib_MultiSpanCache.hxx
 create mode 100644 tests/perf/bspline/hugenurbs
 create mode 100644 tests/perf/bspline/multispan

diff --git a/dox/dev_guides/upgrade/upgrade.md b/dox/dev_guides/upgrade/upgrade.md
index 7adfb09851..c330b6997d 100644
--- a/dox/dev_guides/upgrade/upgrade.md
+++ b/dox/dev_guides/upgrade/upgrade.md
@@ -1222,3 +1222,11 @@ Method Graphic3d_CView::Update() does not redraw the view and does not re-comput
 * The classes *BOPDS_PassKey* and *BOPDS_PassKeyBoolean* are too excessive and not used any more in Boolean Operations. To replace them the new *BOPDS_Pair* class has been implemented. Thus:
   - The method *BOPDS_DS::Interferences()* now returns the *BOPDS_MapOfPair*;
   - The method *BOPDS_Iterator::Value()* takes now only two parameters - the indices of interfering sub-shapes.
+Methods *TObj_Model::SaveAs* and *TObj_Model::Load* receive *TCollection_ExtendedString* filename arguments instead of char*. This shows that the filename may be not-ASCII explicitly. Also it makes OCAF API related to this functionality more conform.
+
+@subsection upgrade_occt710_multispan Multi-span cache support in *Geom2dAdaptor_Curve, GeomAdaptor_Curve* and *GeomAdaptor_Surface*
+
+Multi-span cache has been implemented in *Geom2dAdaptor_Curve, GeomAdaptor_Curve* and *GeomAdaptor_Surface*. Cache for each span is collected first time to evaluate this span.
+
+The benefit of this change is decreased time of calculations (up to 5 times) on some cases.
+
diff --git a/src/BSplCLib/BSplCLib_Cache.cxx b/src/BSplCLib/BSplCLib_Cache.cxx
index 59efb2f8f1..39e419bacf 100644
--- a/src/BSplCLib/BSplCLib_Cache.cxx
+++ b/src/BSplCLib/BSplCLib_Cache.cxx
@@ -33,88 +33,32 @@ static Standard_Real* ConvertArray(const Handle(TColStd_HArray2OfReal)& theHArra
 
 
 BSplCLib_Cache::BSplCLib_Cache()
+  : myIsRational(Standard_False),
+    mySpanStart(0.0),
+    mySpanLength(0.0),
+    mySpanIndex(0),
+    mySpanIndexMin(0),
+    mySpanIndexMax(0),
+    myDegree(0)
 {
-  myPolesWeights.Nullify();
-  myIsRational = Standard_False;
-  mySpanStart = 0.0;
-  mySpanLength = 0.0;
-  mySpanIndex = 0;
-  myDegree = 0;
-  myFlatKnots.Nullify();
-}
-
-BSplCLib_Cache::BSplCLib_Cache(const Standard_Integer&        theDegree,
-                               const Standard_Boolean&        thePeriodic,
-                               const TColStd_Array1OfReal&    theFlatKnots,
-                               const TColgp_Array1OfPnt2d&    thePoles2d,
-                               const TColStd_Array1OfReal*    theWeights)
-{
-  Standard_Real aCacheParam = theFlatKnots.Value(theFlatKnots.Lower() + theDegree);
-  BuildCache(aCacheParam, theDegree, thePeriodic, 
-             theFlatKnots, thePoles2d, theWeights);
-}
-
-BSplCLib_Cache::BSplCLib_Cache(const Standard_Integer&        theDegree,
-                               const Standard_Boolean&        thePeriodic,
-                               const TColStd_Array1OfReal&    theFlatKnots,
-                               const TColgp_Array1OfPnt&      thePoles,
-                               const TColStd_Array1OfReal*    theWeights)
-{
-  Standard_Real aCacheParam = theFlatKnots.Value(theFlatKnots.Lower() + theDegree);
-  BuildCache(aCacheParam, theDegree, thePeriodic, 
-             theFlatKnots, thePoles, theWeights);
 }
 
 
 Standard_Boolean BSplCLib_Cache::IsCacheValid(Standard_Real theParameter) const
 {
-  Standard_Real aNewParam = theParameter;
-  if (!myFlatKnots.IsNull())
-    PeriodicNormalization(myFlatKnots->Array1(), aNewParam);
-
-  Standard_Real aDelta = aNewParam - mySpanStart;
+  Standard_Real aDelta = theParameter - mySpanStart;
   return ((aDelta >= 0.0 || mySpanIndex == mySpanIndexMin) &&
           (aDelta < mySpanLength || mySpanIndex == mySpanIndexMax));
 }
 
-void BSplCLib_Cache::PeriodicNormalization(const TColStd_Array1OfReal& theFlatKnots, 
-                                           Standard_Real& theParameter) const
-{
-  Standard_Real aPeriod = theFlatKnots.Value(theFlatKnots.Upper() - myDegree) - 
-                          theFlatKnots.Value(myDegree + 1) ;
-  if (theParameter < theFlatKnots.Value(myDegree + 1))
-  {
-    Standard_Real aScale = IntegerPart(
-        (theFlatKnots.Value(myDegree + 1) - theParameter) / aPeriod);
-    theParameter += aPeriod * (aScale + 1.0);
-  }
-  if (theParameter > theFlatKnots.Value(theFlatKnots.Upper() - myDegree))
-  {
-    Standard_Real aScale = IntegerPart(
-        (theParameter - theFlatKnots.Value(theFlatKnots.Upper() - myDegree)) / aPeriod);
-    theParameter -= aPeriod * (aScale + 1.0);
-  }
-}
-
 
-void BSplCLib_Cache::BuildCache(const Standard_Real&           theParameter,
-                                const Standard_Integer&        theDegree,
-                                const Standard_Boolean&        thePeriodic,
-                                const TColStd_Array1OfReal&    theFlatKnots,
-                                const TColgp_Array1OfPnt2d&    thePoles2d,
-                                const TColStd_Array1OfReal*    theWeights)
+void BSplCLib_Cache::BuildCache(const Standard_Integer      theDegree,
+                                const Standard_Boolean      thePeriodic,
+                                const TColStd_Array1OfReal& theFlatKnots,
+                                const Standard_Integer      theCachedSpan,
+                                const TColgp_Array1OfPnt2d& thePoles2d,
+                                const TColStd_Array1OfReal* theWeights)
 {
-  // Normalize theParameter for periodical B-splines
-  Standard_Real aNewParam = theParameter;
-  if (thePeriodic)
-  {
-    PeriodicNormalization(theFlatKnots, aNewParam);
-    myFlatKnots = new TColStd_HArray1OfReal(1, theFlatKnots.Length());
-    myFlatKnots->ChangeArray1() = theFlatKnots;
-  }
-  else if (!myFlatKnots.IsNull()) // Periodical curve became non-periodical
-    myFlatKnots.Nullify();
-
   // Change the size of cached data if needed
   myIsRational = (theWeights != NULL);
   Standard_Integer aPWColNumber = myIsRational ? 3 : 2;
@@ -122,9 +66,7 @@ void BSplCLib_Cache::BuildCache(const Standard_Real&           theParameter,
     myPolesWeights = new TColStd_HArray2OfReal(1, theDegree + 1, 1, aPWColNumber);
 
   myDegree = theDegree;
-  mySpanIndex = 0;
-  BSplCLib::LocateParameter(theDegree, theFlatKnots, BSplCLib::NoMults(), 
-                            aNewParam, thePeriodic, mySpanIndex, aNewParam);
+  mySpanIndex = theCachedSpan;
   mySpanStart  = theFlatKnots.Value(mySpanIndex);
   mySpanLength = theFlatKnots.Value(mySpanIndex + 1) - mySpanStart;
   mySpanIndexMin = thePeriodic ? 0 : myDegree + 1;
@@ -136,24 +78,13 @@ void BSplCLib_Cache::BuildCache(const Standard_Real&           theParameter,
                        myPolesWeights->ChangeArray2());
 }
 
-void BSplCLib_Cache::BuildCache(const Standard_Real&           theParameter,
-                                const Standard_Integer&        theDegree,
-                                const Standard_Boolean&        thePeriodic,
-                                const TColStd_Array1OfReal&    theFlatKnots,
-                                const TColgp_Array1OfPnt&      thePoles,
-                                const TColStd_Array1OfReal*    theWeights)
+void BSplCLib_Cache::BuildCache(const Standard_Integer      theDegree,
+                                const Standard_Boolean      thePeriodic,
+                                const TColStd_Array1OfReal& theFlatKnots,
+                                const Standard_Integer      theCachedSpan,
+                                const TColgp_Array1OfPnt&   thePoles,
+                                const TColStd_Array1OfReal* theWeights)
 {
-  // Create list of knots with repetitions and normalize theParameter for periodical B-splines
-  Standard_Real aNewParam = theParameter;
-  if (thePeriodic)
-  {
-    PeriodicNormalization(theFlatKnots, aNewParam);
-    myFlatKnots = new TColStd_HArray1OfReal(1, theFlatKnots.Length());
-    myFlatKnots->ChangeArray1() = theFlatKnots;
-  }
-  else if (!myFlatKnots.IsNull()) // Periodical curve became non-periodical
-    myFlatKnots.Nullify();
-
   // Change the size of cached data if needed
   myIsRational = (theWeights != NULL);
   Standard_Integer aPWColNumber = myIsRational ? 4 : 3;
@@ -161,9 +92,7 @@ void BSplCLib_Cache::BuildCache(const Standard_Real&           theParameter,
     myPolesWeights = new TColStd_HArray2OfReal(1, theDegree + 1, 1, aPWColNumber);
 
   myDegree = theDegree;
-  mySpanIndex = 0;
-  BSplCLib::LocateParameter(theDegree, theFlatKnots, BSplCLib::NoMults(), 
-                            aNewParam, thePeriodic, mySpanIndex, aNewParam);
+  mySpanIndex = theCachedSpan;
   mySpanStart  = theFlatKnots.Value(mySpanIndex);
   mySpanLength = theFlatKnots.Value(mySpanIndex + 1) - mySpanStart;
   mySpanIndexMin = thePeriodic ? 0 : myDegree + 1;
@@ -176,15 +105,11 @@ void BSplCLib_Cache::BuildCache(const Standard_Real&           theParameter,
 }
 
 
-void BSplCLib_Cache::CalculateDerivative(const Standard_Real&    theParameter, 
-                                         const Standard_Integer& theDerivative, 
-                                               Standard_Real&    theDerivArray) const
+void BSplCLib_Cache::CalculateDerivative(const Standard_Real    theParameter, 
+                                         const Standard_Integer theDerivative, 
+                                               Standard_Real&   theDerivArray) const
 {
-  Standard_Real aNewParameter = theParameter;
-  if (!myFlatKnots.IsNull()) // B-spline is periodical
-    PeriodicNormalization(myFlatKnots->Array1(), aNewParameter);
-  aNewParameter = (aNewParameter - mySpanStart) / mySpanLength;
-
+  Standard_Real aNewParameter = (theParameter - mySpanStart) / mySpanLength;
   Standard_Real* aPolesArray = ConvertArray(myPolesWeights);
   Standard_Integer aDimension = myPolesWeights->RowLength(); // number of columns
 
@@ -225,13 +150,9 @@ void BSplCLib_Cache::CalculateDerivative(const Standard_Real&    theParameter,
 }
 
 
-void BSplCLib_Cache::D0(const Standard_Real& theParameter, gp_Pnt2d& thePoint) const
+void BSplCLib_Cache::D0(const Standard_Real theParameter, gp_Pnt2d& thePoint) const
 {
-  Standard_Real aNewParameter = theParameter;
-  if (!myFlatKnots.IsNull()) // B-spline is periodical
-    PeriodicNormalization(myFlatKnots->Array1(), aNewParameter);
-  aNewParameter = (aNewParameter - mySpanStart) / mySpanLength;
-
+  Standard_Real aNewParameter = (theParameter - mySpanStart) / mySpanLength;
   Standard_Real* aPolesArray = ConvertArray(myPolesWeights);
   Standard_Real aPoint[4];
   Standard_Integer aDimension = myPolesWeights->RowLength(); // number of columns
@@ -245,13 +166,9 @@ void BSplCLib_Cache::D0(const Standard_Real& theParameter, gp_Pnt2d& thePoint) c
     thePoint.ChangeCoord().Divide(aPoint[2]);
 }
 
-void BSplCLib_Cache::D0(const Standard_Real& theParameter, gp_Pnt& thePoint) const
+void BSplCLib_Cache::D0(const Standard_Real theParameter, gp_Pnt& thePoint) const
 {
-  Standard_Real aNewParameter = theParameter;
-  if (!myFlatKnots.IsNull()) // B-spline is periodical
-    PeriodicNormalization(myFlatKnots->Array1(), aNewParameter);
-  aNewParameter = (aNewParameter - mySpanStart) / mySpanLength;
-
+  Standard_Real aNewParameter = (theParameter - mySpanStart) / mySpanLength;
   Standard_Real* aPolesArray = ConvertArray(myPolesWeights);
   Standard_Real aPoint[4];
   Standard_Integer aDimension = myPolesWeights->RowLength(); // number of columns
@@ -266,12 +183,12 @@ void BSplCLib_Cache::D0(const Standard_Real& theParameter, gp_Pnt& thePoint) con
 }
 
 
-void BSplCLib_Cache::D1(const Standard_Real& theParameter, gp_Pnt2d& thePoint, gp_Vec2d& theTangent) const
+void BSplCLib_Cache::D1(const Standard_Real theParameter, gp_Pnt2d& thePoint, gp_Vec2d& theTangent) const
 {
   Standard_Integer aDimension = myPolesWeights->RowLength(); // number of columns
   Standard_Real aPntDeriv[8]; // result storage (point and derivative coordinates)
 
-  this->CalculateDerivative(theParameter, 1, aPntDeriv[0]);
+  CalculateDerivative(theParameter, 1, aPntDeriv[0]);
   if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative
     aDimension -= 1;
 
@@ -279,12 +196,12 @@ void BSplCLib_Cache::D1(const Standard_Real& theParameter, gp_Pnt2d& thePoint, g
   theTangent.SetCoord(aPntDeriv[aDimension], aPntDeriv[aDimension + 1]);
 }
 
-void BSplCLib_Cache::D1(const Standard_Real& theParameter, gp_Pnt& thePoint, gp_Vec& theTangent) const
+void BSplCLib_Cache::D1(const Standard_Real theParameter, gp_Pnt& thePoint, gp_Vec& theTangent) const
 {
   Standard_Integer aDimension = myPolesWeights->RowLength(); // number of columns
   Standard_Real aPntDeriv[8]; // result storage (point and derivative coordinates)
 
-  this->CalculateDerivative(theParameter, 1, aPntDeriv[0]);
+  CalculateDerivative(theParameter, 1, aPntDeriv[0]);
   if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative
     aDimension -= 1;
 
@@ -292,12 +209,12 @@ void BSplCLib_Cache::D1(const Standard_Real& theParameter, gp_Pnt& thePoint, gp_
   theTangent.SetCoord(aPntDeriv[aDimension], aPntDeriv[aDimension + 1], aPntDeriv[aDimension + 2]);
 }
 
-void BSplCLib_Cache::D2(const Standard_Real& theParameter, gp_Pnt2d& thePoint, gp_Vec2d& theTangent, gp_Vec2d& theCurvature) const
+void BSplCLib_Cache::D2(const Standard_Real theParameter, gp_Pnt2d& thePoint, gp_Vec2d& theTangent, gp_Vec2d& theCurvature) const
 {
   Standard_Integer aDimension = myPolesWeights->RowLength(); // number of columns
   Standard_Real aPntDeriv[12]; // result storage (point and derivatives coordinates)
 
-  this->CalculateDerivative(theParameter, 2, aPntDeriv[0]);
+  CalculateDerivative(theParameter, 2, aPntDeriv[0]);
   if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative
     aDimension -= 1;
 
@@ -306,12 +223,12 @@ void BSplCLib_Cache::D2(const Standard_Real& theParameter, gp_Pnt2d& thePoint, g
   theCurvature.SetCoord(aPntDeriv[aDimension<<1], aPntDeriv[(aDimension<<1) + 1]);
 }
 
-void BSplCLib_Cache::D2(const Standard_Real& theParameter, gp_Pnt& thePoint, gp_Vec& theTangent, gp_Vec& theCurvature) const
+void BSplCLib_Cache::D2(const Standard_Real theParameter, gp_Pnt& thePoint, gp_Vec& theTangent, gp_Vec& theCurvature) const
 {
   Standard_Integer aDimension = myPolesWeights->RowLength(); // number of columns
   Standard_Real aPntDeriv[12]; // result storage (point and derivatives coordinates)
 
-  this->CalculateDerivative(theParameter, 2, aPntDeriv[0]);
+  CalculateDerivative(theParameter, 2, aPntDeriv[0]);
   if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative
     aDimension -= 1;
 
@@ -321,16 +238,16 @@ void BSplCLib_Cache::D2(const Standard_Real& theParameter, gp_Pnt& thePoint, gp_
 }
 
 
-void BSplCLib_Cache::D3(const Standard_Real& theParameter, 
-                              gp_Pnt2d&      thePoint, 
-                              gp_Vec2d&      theTangent, 
-                              gp_Vec2d&      theCurvature,
-                              gp_Vec2d&      theTorsion) const
+void BSplCLib_Cache::D3(const Standard_Real theParameter, 
+                              gp_Pnt2d&     thePoint, 
+                              gp_Vec2d&     theTangent, 
+                              gp_Vec2d&     theCurvature,
+                              gp_Vec2d&     theTorsion) const
 {
   Standard_Integer aDimension = myPolesWeights->RowLength(); // number of columns
   Standard_Real aPntDeriv[16]; // result storage (point and derivatives coordinates)
 
-  this->CalculateDerivative(theParameter, 3, aPntDeriv[0]);
+  CalculateDerivative(theParameter, 3, aPntDeriv[0]);
   if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative
     aDimension -= 1;
 
@@ -342,16 +259,16 @@ void BSplCLib_Cache::D3(const Standard_Real& theParameter,
   theTorsion.SetCoord(aPntDeriv[aShift], aPntDeriv[aShift + 1]);
 }
 
-void BSplCLib_Cache::D3(const Standard_Real& theParameter, 
-                              gp_Pnt&        thePoint, 
-                              gp_Vec&        theTangent, 
-                              gp_Vec&        theCurvature,
-                              gp_Vec&        theTorsion) const
+void BSplCLib_Cache::D3(const Standard_Real theParameter, 
+                              gp_Pnt&       thePoint, 
+                              gp_Vec&       theTangent, 
+                              gp_Vec&       theCurvature,
+                              gp_Vec&       theTorsion) const
 {
   Standard_Integer aDimension = myPolesWeights->RowLength(); // number of columns
   Standard_Real aPntDeriv[16]; // result storage (point and derivatives coordinates)
 
-  this->CalculateDerivative(theParameter, 3, aPntDeriv[0]);
+  CalculateDerivative(theParameter, 3, aPntDeriv[0]);
   if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative
     aDimension -= 1;
 
diff --git a/src/BSplCLib/BSplCLib_Cache.hxx b/src/BSplCLib/BSplCLib_Cache.hxx
index 74bcd14d00..4c5a0e0acb 100644
--- a/src/BSplCLib/BSplCLib_Cache.hxx
+++ b/src/BSplCLib/BSplCLib_Cache.hxx
@@ -40,86 +40,64 @@ class BSplCLib_Cache : public Standard_Transient
 public:
   //! Default constructor
   Standard_EXPORT BSplCLib_Cache();
-  //! Constructor for caching of 2D curves
-  //! \param theDegree     degree of the curve
-  //! \param thePeriodic   identify the curve is periodic
-  //! \param theFlatKnots  knots of Bezier/B-spline curve (with repetitions)
-  //! \param thePoles2d    array of poles of 2D curve
-  //! \param theWeights    array of weights of corresponding poles
-  Standard_EXPORT BSplCLib_Cache(const Standard_Integer&        theDegree,
-                                 const Standard_Boolean&        thePeriodic,
-                                 const TColStd_Array1OfReal&    theFlatKnots,
-                                 const TColgp_Array1OfPnt2d&    thePoles2d,
-                                 const TColStd_Array1OfReal*    theWeights = NULL);
-  //! Constructor for caching of 3D curves
-  //! \param theDegree     degree of the curve
-  //! \param thePeriodic   identify the curve is periodic
-  //! \param theFlatKnots  knots of Bezier/B-spline curve (with repetitions)
-  //! \param thePoles      array of poles of 3D curve
-  //! \param theWeights    array of weights of corresponding poles
-  Standard_EXPORT BSplCLib_Cache(const Standard_Integer&        theDegree,
-                                 const Standard_Boolean&        thePeriodic,
-                                 const TColStd_Array1OfReal&    theFlatKnots,
-                                 const TColgp_Array1OfPnt&      thePoles,
-                                 const TColStd_Array1OfReal*    theWeights = NULL);
 
   //! Verifies validity of the cache using flat parameter of the point
   //! \param theParameter parameter of the point placed in the span
   Standard_EXPORT Standard_Boolean IsCacheValid(Standard_Real theParameter) const;
 
   //! Recomputes the cache data for 2D curves. Does not verify validity of the cache
-  //! \param theParameter  the value on the knot's axis to identify the span
   //! \param theDegree     degree of the curve
   //! \param thePeriodic   identify the curve is periodic
   //! \param theFlatKnots  knots of Bezier/B-spline curve (with repetitions)
+  //! \param theCachedSpan index of span to be cached
   //! \param thePoles2d    array of poles of 2D curve
   //! \param theWeights    array of weights of corresponding poles
-  Standard_EXPORT void BuildCache(const Standard_Real&           theParameter,
-                                  const Standard_Integer&        theDegree,
-                                  const Standard_Boolean&        thePeriodic,
-                                  const TColStd_Array1OfReal&    theFlatKnots,
-                                  const TColgp_Array1OfPnt2d&    thePoles2d,
-                                  const TColStd_Array1OfReal*    theWeights = NULL);
+  Standard_EXPORT void BuildCache(const Standard_Integer      theDegree,
+                                  const Standard_Boolean      thePeriodic,
+                                  const TColStd_Array1OfReal& theFlatKnots,
+                                  const Standard_Integer      theCachedSpan,
+                                  const TColgp_Array1OfPnt2d& thePoles2d,
+                                  const TColStd_Array1OfReal* theWeights = NULL);
   //! Recomputes the cache data for 3D curves. Does not verify validity of the cache
-  //! \param theParameter  the value on the knot's axis to identify the span
   //! \param theDegree     degree of the curve
   //! \param thePeriodic   identify the curve is periodic
   //! \param theFlatKnots  knots of Bezier/B-spline curve (with repetitions)
+  //! \param theCachedSpan index of span to be cached
   //! \param thePoles      array of poles of 3D curve
   //! \param theWeights    array of weights of corresponding poles
-  Standard_EXPORT void BuildCache(const Standard_Real&           theParameter,
-                                  const Standard_Integer&        theDegree,
-                                  const Standard_Boolean&        thePeriodic,
-                                  const TColStd_Array1OfReal&    theFlatKnots,
-                                  const TColgp_Array1OfPnt&      thePoles,
-                                  const TColStd_Array1OfReal*    theWeights = NULL);
+  Standard_EXPORT void BuildCache(const Standard_Integer      theDegree,
+                                  const Standard_Boolean      thePeriodic,
+                                  const TColStd_Array1OfReal& theFlatKnots,
+                                  const Standard_Integer      theCachedSpan,
+                                  const TColgp_Array1OfPnt&   thePoles,
+                                  const TColStd_Array1OfReal* theWeights = NULL);
 
   //! Calculates the point on the curve in the specified parameter
   //! \param[in]  theParameter parameter of calculation of the value
   //! \param[out] thePoint     the result of calculation (the point on the curve)
-  Standard_EXPORT void D0(const Standard_Real& theParameter, gp_Pnt2d& thePoint) const;
-  Standard_EXPORT void D0(const Standard_Real& theParameter, gp_Pnt&   thePoint) const;
+  Standard_EXPORT void D0(const Standard_Real theParameter, gp_Pnt2d& thePoint) const;
+  Standard_EXPORT void D0(const Standard_Real theParameter, gp_Pnt&   thePoint) const;
 
   //! Calculates the point on the curve and its first derivative in the specified parameter
   //! \param[in]  theParameter parameter of calculation of the value
   //! \param[out] thePoint     the result of calculation (the point on the curve)
   //! \param[out] theTangent   tangent vector (first derivatives) for the curve in the calculated point
-  Standard_EXPORT void D1(const Standard_Real& theParameter, gp_Pnt2d& thePoint, gp_Vec2d& theTangent) const;
-  Standard_EXPORT void D1(const Standard_Real& theParameter, gp_Pnt&   thePoint, gp_Vec&   theTangent) const;
+  Standard_EXPORT void D1(const Standard_Real theParameter, gp_Pnt2d& thePoint, gp_Vec2d& theTangent) const;
+  Standard_EXPORT void D1(const Standard_Real theParameter, gp_Pnt&   thePoint, gp_Vec&   theTangent) const;
 
   //! Calculates the point on the curve and two derivatives in the specified parameter
   //! \param[in]  theParameter parameter of calculation of the value
   //! \param[out] thePoint     the result of calculation (the point on the curve)
   //! \param[out] theTangent   tangent vector (1st derivatives) for the curve in the calculated point
   //! \param[out] theCurvature curvature vector (2nd derivatives) for the curve in the calculated point
-  Standard_EXPORT void D2(const Standard_Real& theParameter, 
-                                gp_Pnt2d&      thePoint, 
-                                gp_Vec2d&      theTangent, 
-                                gp_Vec2d&      theCurvature) const;
-  Standard_EXPORT void D2(const Standard_Real& theParameter, 
-                                gp_Pnt&        thePoint, 
-                                gp_Vec&        theTangent, 
-                                gp_Vec&        theCurvature) const;
+  Standard_EXPORT void D2(const Standard_Real theParameter, 
+                                gp_Pnt2d&     thePoint, 
+                                gp_Vec2d&     theTangent, 
+                                gp_Vec2d&     theCurvature) const;
+  Standard_EXPORT void D2(const Standard_Real theParameter, 
+                                gp_Pnt&       thePoint, 
+                                gp_Vec&       theTangent, 
+                                gp_Vec&       theCurvature) const;
 
   //! Calculates the point on the curve and three derivatives in the specified parameter
   //! \param[in]  theParameter parameter of calculation of the value
@@ -127,34 +105,29 @@ public:
   //! \param[out] theTangent   tangent vector (1st derivatives) for the curve in the calculated point
   //! \param[out] theCurvature curvature vector (2nd derivatives) for the curve in the calculated point
   //! \param[out] theTorsion   second curvature vector (3rd derivatives) for the curve in the calculated point
-  Standard_EXPORT void D3(const Standard_Real& theParameter, 
-                                gp_Pnt2d&      thePoint, 
-                                gp_Vec2d&      theTangent, 
-                                gp_Vec2d&      theCurvature,
-                                gp_Vec2d&      theTorsion) const;
-  Standard_EXPORT void D3(const Standard_Real& theParameter, 
-                                gp_Pnt&        thePoint, 
-                                gp_Vec&        theTangent, 
-                                gp_Vec&        theCurvature,
-                                gp_Vec&        theTorsion) const;
+  Standard_EXPORT void D3(const Standard_Real theParameter, 
+                                gp_Pnt2d&     thePoint, 
+                                gp_Vec2d&     theTangent, 
+                                gp_Vec2d&     theCurvature,
+                                gp_Vec2d&     theTorsion) const;
+  Standard_EXPORT void D3(const Standard_Real theParameter, 
+                                gp_Pnt&       thePoint, 
+                                gp_Vec&       theTangent, 
+                                gp_Vec&       theCurvature,
+                                gp_Vec&       theTorsion) const;
 
 
   DEFINE_STANDARD_RTTIEXT(BSplCLib_Cache,Standard_Transient)
 
 protected:
-  //! Normalizes the parameter for periodical curves
-  //! \param theFlatKnots knots with repetitions
-  //! \param theParameter the value to be normalized into the knots array
-  void PeriodicNormalization(const TColStd_Array1OfReal& theFlatKnots, Standard_Real& theParameter) const;
-
   //! Fills array of derivatives in the selected point of the curve
   //! \param[in]  theParameter  parameter of the calculation
   //! \param[in]  theDerivative maximal derivative to be calculated (computes all derivatives lesser than specified)
   //! \param[out] theDerivArray result array of derivatives (with size (theDerivative+1)*(PntDim+1), 
   //!                           where PntDim = 2 or 3 is a dimension of the curve)
-  void CalculateDerivative(const Standard_Real&    theParameter, 
-                           const Standard_Integer& theDerivative, 
-                                 Standard_Real&    theDerivArray) const;
+  void CalculateDerivative(const Standard_Real    theParameter, 
+                           const Standard_Integer theDerivative, 
+                                 Standard_Real&   theDerivArray) const;
 
 private:
   Handle(TColStd_HArray2OfReal) myPolesWeights; ///< array of poles and weights of calculated cache
@@ -163,14 +136,13 @@ private:
                                                 //       x2 y2 [z2] [w2] etc
                                                 // for 2D-curves there is no z conponent, for non-rational curves there is no weight
 
-  Standard_Boolean              myIsRational; ///< identifies the rationality of Bezier/B-spline curve
-  Standard_Real                 mySpanStart;  ///< parameter for the first point of the span
-  Standard_Real                 mySpanLength; ///< length of the span
-  Standard_Integer              mySpanIndex;  ///< index of the span on Bezier/B-spline curve
+  Standard_Boolean              myIsRational;   ///< identifies the rationality of Bezier/B-spline curve
+  Standard_Real                 mySpanStart;    ///< parameter for the first point of the span
+  Standard_Real                 mySpanLength;   ///< length of the span
+  Standard_Integer              mySpanIndex;    ///< index of the span on Bezier/B-spline curve
   Standard_Integer              mySpanIndexMin; ///< minimal index of span on Bezier/B-spline curve
   Standard_Integer              mySpanIndexMax; ///< maximal number of spans on Bezier/B-spline curve
-  Standard_Integer              myDegree;     ///< degree of Bezier/B-spline
-  Handle(TColStd_HArray1OfReal) myFlatKnots;  ///< knots of Bezier/B-spline (used for periodic normalization of parameters, exists only for periodical splines)
+  Standard_Integer              myDegree;       ///< degree of Bezier/B-spline
 };
 
 DEFINE_STANDARD_HANDLE(BSplCLib_Cache, Standard_Transient)
diff --git a/src/BSplCLib/BSplCLib_MultiSpanCache.gxx b/src/BSplCLib/BSplCLib_MultiSpanCache.gxx
new file mode 100644
index 0000000000..c604f5c1a4
--- /dev/null
+++ b/src/BSplCLib/BSplCLib_MultiSpanCache.gxx
@@ -0,0 +1,158 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <BSplCLib.hxx>
+#include <ElCLib.hxx>
+
+template<class POINT_TYPE, class VEC_TYPE>
+BSplCLib_MultiSpanCacheBase<POINT_TYPE, VEC_TYPE>::BSplCLib_MultiSpanCacheBase(
+  const Standard_Integer         theDegree,
+  const Standard_Boolean         thePeriodic,
+  const Array1OfPOINT&           thePoles,
+  const TColStd_Array1OfReal*    theWeights)
+  : myDegree(theDegree),
+    myPeriodic(thePeriodic),
+    myFirstKnot(0.),
+    myLastKnot(1.),
+    myKnots(NULL),
+    myMults(NULL),
+    myFlatKnots(NULL),
+    myPoles(thePoles),
+    myWeights(theWeights),
+    mySingleSpan(Standard_True),
+    myCaches(1),
+    myLastCacheInd(0)
+{
+  // Build cache for single span
+  TColStd_Array1OfReal aFlatKnots(BSplCLib::FlatBezierKnots(myDegree), 1, (myDegree + 1) * 2);
+  myCaches[myLastCacheInd] = new BSplCLib_Cache;
+  myCaches[myLastCacheInd]->BuildCache(myDegree, myPeriodic, aFlatKnots, myDegree + 1, myPoles, myWeights);
+}
+
+template<class POINT_TYPE, class VEC_TYPE>
+BSplCLib_MultiSpanCacheBase<POINT_TYPE, VEC_TYPE>::BSplCLib_MultiSpanCacheBase(
+  const Standard_Integer         theDegree,
+  const Standard_Boolean         thePeriodic,
+  const TColStd_Array1OfReal*    theKnots,
+  const TColStd_Array1OfInteger* theMults,
+  const TColStd_Array1OfReal*    theFlatKnots,
+  const Array1OfPOINT&           thePoles,
+  const TColStd_Array1OfReal*    theWeights)
+  : myDegree(theDegree),
+    myPeriodic(thePeriodic),
+    myFirstKnot(theKnots->First()),
+    myLastKnot(theKnots->Last()),
+    myKnots(theKnots),
+    myMults(theMults),
+    myFlatKnots(theFlatKnots),
+    myPoles(thePoles),
+    myWeights(theWeights),
+    mySingleSpan(theKnots->Length() == 2),
+    myCaches(theKnots->Upper() - theKnots->Lower()),
+    myLastCacheInd(0)
+{
+}
+
+template<class POINT_TYPE, class VEC_TYPE>
+BSplCLib_MultiSpanCacheBase<POINT_TYPE, VEC_TYPE>::~BSplCLib_MultiSpanCacheBase()
+{
+  Standard_Integer aSize = (Standard_Integer)myCaches.Size();
+  for (Standard_Integer i = 0; i < aSize; ++i)
+    myCaches[i].Nullify();
+}
+
+template<class POINT_TYPE, class VEC_TYPE>
+void BSplCLib_MultiSpanCacheBase<POINT_TYPE, VEC_TYPE>::D0(const Standard_Real theParameter,
+                                                                 POINT_TYPE&   thePoint)
+{
+  Standard_Real aPar = theParameter;
+  const Handle(BSplCLib_Cache)& aCache = FindCache(aPar);
+  aCache->D0(aPar, thePoint);
+}
+
+template<class POINT_TYPE, class VEC_TYPE>
+void BSplCLib_MultiSpanCacheBase<POINT_TYPE, VEC_TYPE>::D1(const Standard_Real theParameter,
+                                                                 POINT_TYPE&   thePoint,
+                                                                 VEC_TYPE&     theTangent)
+{
+  Standard_Real aPar = theParameter;
+  const Handle(BSplCLib_Cache)& aCache = FindCache(aPar);
+  aCache->D1(aPar, thePoint, theTangent);
+}
+
+template<class POINT_TYPE, class VEC_TYPE>
+void BSplCLib_MultiSpanCacheBase<POINT_TYPE, VEC_TYPE>::D2(const Standard_Real theParameter,
+                                                                 POINT_TYPE&   thePoint,
+                                                                 VEC_TYPE&     theTangent,
+                                                                 VEC_TYPE&     theCurvature)
+{
+  Standard_Real aPar = theParameter;
+  const Handle(BSplCLib_Cache)& aCache = FindCache(aPar);
+  aCache->D2(aPar, thePoint, theTangent, theCurvature);
+}
+
+template<class POINT_TYPE, class VEC_TYPE>
+void BSplCLib_MultiSpanCacheBase<POINT_TYPE, VEC_TYPE>::D3(const Standard_Real theParameter,
+                                                                 POINT_TYPE&   thePoint,
+                                                                 VEC_TYPE&     theTangent,
+                                                                 VEC_TYPE&     theCurvature,
+                                                                 VEC_TYPE&     theTorsion)
+{
+  Standard_Real aPar = theParameter;
+  const Handle(BSplCLib_Cache)& aCache = FindCache(aPar);
+  aCache->D3(aPar, thePoint, theTangent, theCurvature, theTorsion);
+}
+
+template<class POINT_TYPE, class VEC_TYPE>
+Standard_Integer BSplCLib_MultiSpanCacheBase<POINT_TYPE, VEC_TYPE>::SpanIndex(const Standard_Real theParameter) const
+{
+  if (mySingleSpan)
+    return 1;
+
+  // Get index of a span
+  Standard_Integer aSpanIndex = 0;
+  Standard_Real aNewPar = theParameter; // unused parameter, because theParameter is already in period 
+  BSplCLib::LocateParameter(myDegree, *myKnots, myMults, theParameter, myPeriodic,
+                            aSpanIndex, aNewPar);
+  return aSpanIndex;
+}
+
+template<class POINT_TYPE, class VEC_TYPE>
+const Handle(BSplCLib_Cache)&
+BSplCLib_MultiSpanCacheBase<POINT_TYPE, VEC_TYPE>::FindCache(Standard_Real& theParameter)
+{
+  // Normalize the parameters for periodical B-splines
+  if (myPeriodic && (theParameter > myLastKnot || theParameter < myFirstKnot))
+    theParameter = ElCLib::InPeriod(theParameter, myFirstKnot, myLastKnot);
+
+  // Do not want to use LocateParameter due to lack of performance.
+  // Just check the last used cache is valid for the given parameter.
+  if (!myCaches[myLastCacheInd].IsNull() &&
+      (mySingleSpan || myCaches[myLastCacheInd]->IsCacheValid(theParameter)))
+      return myCaches[myLastCacheInd];
+
+  Standard_Integer aSpanIndex = SpanIndex(theParameter);
+  myLastCacheInd = aSpanIndex - (myKnots ? myKnots->Lower() : 1);
+  // check the cache is already built
+  Handle(BSplCLib_Cache)& aCache = myCaches[myLastCacheInd];
+  if (aCache.IsNull())
+  {
+    // calculate index of cache for the flat knots
+    aSpanIndex = BSplCLib::FlatIndex(myDegree, aSpanIndex, *myMults, myPeriodic);
+    // build new cache
+    aCache = new BSplCLib_Cache;
+    aCache->BuildCache(myDegree, myPeriodic, *myFlatKnots, aSpanIndex, myPoles, myWeights);
+  }
+
+  return aCache;
+}
diff --git a/src/BSplCLib/BSplCLib_MultiSpanCache.hxx b/src/BSplCLib/BSplCLib_MultiSpanCache.hxx
new file mode 100644
index 0000000000..c6d39c2a59
--- /dev/null
+++ b/src/BSplCLib/BSplCLib_MultiSpanCache.hxx
@@ -0,0 +1,190 @@
+// Copyright (c) 2016 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _BSplCLib_MultiSpanCache_Headerfile
+#define _BSplCLib_MultiSpanCache_Headerfile
+
+#include <BSplCLib_Cache.hxx>
+#include <NCollection_LocalArray.hxx>
+#include <TColStd_Array1OfInteger.hxx>
+
+//! \brief Base container for list of caches for Bezier and B-spline curves.
+//!
+//! Applicable template arguments:
+//!  * POINT_TYPE = gp_Pnt2d and VEC_TYPE = gp_Vec2d
+//!  * POINT_TYPE = gp_Pnt and VEC_TYPE = gp_Vec
+template<class POINT_TYPE, class VEC_TYPE>
+class BSplCLib_MultiSpanCacheBase
+{
+  typedef NCollection_Array1<POINT_TYPE> Array1OfPOINT;
+
+public:
+  //! Construct cache for Bezier curve with given parameters
+  //! \param theDegree    [in] degree of the curve
+  //! \param thePeriodic  [in] periodicity of the curve
+  //! \param thePoles     [in] array of poles of the curve
+  //! \param theWeights   [in] array of weights of corresponding poles
+  //! \param theMaxSpans  [in] maximal spans to be cached
+  BSplCLib_MultiSpanCacheBase(const Standard_Integer         theDegree,
+                              const Standard_Boolean         thePeriodic,
+                              const Array1OfPOINT&           thePoles,
+                              const TColStd_Array1OfReal*    theWeights = NULL);
+
+  //! Construct multi-span cache for B-spline curve with given parameters
+  //! \param theDegree    [in] degree of the curve
+  //! \param thePeriodic  [in] periodicity of the curve
+  //! \param theKnots     [in] knots of the curve
+  //! \param theMults     [in] multiplicities
+  //! \param theFlatKnots [in] knots of the curve (with repetition)
+  //! \param thePoles     [in] array of poles of the curve
+  //! \param theWeights   [in] array of weights of corresponding poles
+  //! \param theMaxSpans  [in] maximal spans to be cached
+  BSplCLib_MultiSpanCacheBase(const Standard_Integer         theDegree,
+                              const Standard_Boolean         thePeriodic,
+                              const TColStd_Array1OfReal*    theKnots,
+                              const TColStd_Array1OfInteger* theMults,
+                              const TColStd_Array1OfReal*    theFlatKnots,
+                              const Array1OfPOINT&           thePoles,
+                              const TColStd_Array1OfReal*    theWeights = NULL);
+
+  virtual ~BSplCLib_MultiSpanCacheBase();
+
+  //! Calculates the point on the curve for specified parameters
+  //! \param theParameter [in]  parameter on the curve
+  //! \param thePoint     [out] the result of calculation (the point on the curve)
+  Standard_EXPORT void D0(const Standard_Real theParameter, POINT_TYPE& thePoint);
+
+  //! Calculates the point on the curve and its first derivative
+  //! \param theParameter [in]  parameter on the curve
+  //! \param thePoint     [out] the result of calculation (the point on the curve)
+  //! \param theTangent   [out] tangent vector in the calculated point
+  Standard_EXPORT void D1(const Standard_Real theParameter,
+                                POINT_TYPE&   thePoint, 
+                                VEC_TYPE&     theTangent);
+
+  //! Calculates the point on the curve and derivatives till second order
+  //! \param theParameter [in]  parameter on the curve
+  //! \param thePoint     [out] the result of calculation (the point on the curve)
+  //! \param theTangent   [out] tangent vector in the calculated point
+  //! \param theCurvature [out] curvature vector (2nd derivative)
+  Standard_EXPORT void D2(const Standard_Real theParameter,
+                                POINT_TYPE&   thePoint, 
+                                VEC_TYPE&     theTangent,
+                                VEC_TYPE&     theCurvature);
+
+  //! Calculates the point on the curve and three derivatives in the specified parameter
+  //! \param theParameter [in]  parameter on the curve
+  //! \param thePoint     [out] the result of calculation (the point on the curve)
+  //! \param theTangent   [out] tangent vector (1st derivative) in the calculated point
+  //! \param theCurvature [out] curvature vector (2nd derivative)
+  //! \param theTorsion   [out] second curvature vector (3rd derivative)
+  Standard_EXPORT void D3(const Standard_Real theParameter,
+                                POINT_TYPE&   thePoint, 
+                                VEC_TYPE&     theTangent,
+                                VEC_TYPE&     theCurvature,
+                                VEC_TYPE&     theTorsion);
+
+private:
+  //! Calculate an index of a span by the parameter on the curve.
+  //! \param theParameter [in]  parameter on the curve
+  //! \return Index of the span, used to store cache in the map.
+  Standard_Integer SpanIndex(const Standard_Real theParameter) const;
+
+  //! Find cached span containing given parameter.
+  //! Parameter is adjusted to period for periodic curves.
+  //! If such span is not cached yet, prepares new cache object.
+  const Handle(BSplCLib_Cache)& FindCache(Standard_Real& theParameter);
+
+  BSplCLib_MultiSpanCacheBase(const BSplCLib_MultiSpanCacheBase&);
+  const BSplCLib_MultiSpanCacheBase& operator=(const BSplCLib_MultiSpanCacheBase&);
+
+private:
+  typedef NCollection_LocalArray<Handle(BSplCLib_Cache), 1> CacheArray;
+
+  Standard_Integer               myDegree;       ///< degree of the curve
+  Standard_Boolean               myPeriodic;     ///< periodicity of the curve
+  Standard_Real                  myFirstKnot;    ///< value of the first knot (used for periodic normalization)
+  Standard_Real                  myLastKnot;     ///< value of the lsst knot (used for periodic normalization)
+  const TColStd_Array1OfReal*    myKnots;        ///< knots
+  const TColStd_Array1OfInteger* myMults;        ///< multiplicities
+  const TColStd_Array1OfReal*    myFlatKnots;    ///< knots duplicated according to multiplicity
+  const Array1OfPOINT&           myPoles;        ///< array of poles
+  const TColStd_Array1OfReal*    myWeights;      ///< array of weights
+
+  CacheArray                     myCaches;       ///< array of caches
+  Standard_Integer               myLastCacheInd; ///< index of last used cache
+
+  Standard_Boolean               mySingleSpan;   ///< the curve consists of just one span
+};
+
+
+//! \brief A container for list of caches for 2D Bezier and B-spline curves.
+class BSplCLib_MultiSpanCache2D : public BSplCLib_MultiSpanCacheBase<gp_Pnt2d, gp_Vec2d>, public Standard_Transient
+{
+public:
+  Standard_EXPORT BSplCLib_MultiSpanCache2D(const Standard_Integer         theDegree,
+                                            const Standard_Boolean         thePeriodic,
+                                            const TColgp_Array1OfPnt2d&    thePoles,
+                                            const TColStd_Array1OfReal*    theWeights  = NULL)
+    : BSplCLib_MultiSpanCacheBase<gp_Pnt2d, gp_Vec2d>(theDegree, thePeriodic, thePoles, theWeights)
+  {
+  }
+
+  Standard_EXPORT BSplCLib_MultiSpanCache2D(const Standard_Integer         theDegree,
+                                            const Standard_Boolean         thePeriodic,
+                                            const TColStd_Array1OfReal*    theKnots,
+                                            const TColStd_Array1OfInteger* theMults,
+                                            const TColStd_Array1OfReal*    theFlatKnots,
+                                            const TColgp_Array1OfPnt2d&    thePoles,
+                                            const TColStd_Array1OfReal*    theWeights  = NULL)
+    : BSplCLib_MultiSpanCacheBase<gp_Pnt2d, gp_Vec2d>(theDegree, thePeriodic, theKnots, theMults,
+                                                      theFlatKnots, thePoles, theWeights)
+  {
+  }
+
+  DEFINE_STANDARD_RTTI_INLINE(BSplCLib_MultiSpanCache2D, Standard_Transient)
+};
+
+//! \brief A container for list of caches for 3D Bezier and B-spline curves.
+class BSplCLib_MultiSpanCache3D : public BSplCLib_MultiSpanCacheBase<gp_Pnt, gp_Vec>, public Standard_Transient
+{
+public:
+  Standard_EXPORT BSplCLib_MultiSpanCache3D(const Standard_Integer         theDegree,
+                                            const Standard_Boolean         thePeriodic,
+                                            const TColgp_Array1OfPnt&      thePoles,
+                                            const TColStd_Array1OfReal*    theWeights  = NULL)
+    : BSplCLib_MultiSpanCacheBase<gp_Pnt, gp_Vec>(theDegree, thePeriodic, thePoles, theWeights)
+  {
+  }
+
+  Standard_EXPORT BSplCLib_MultiSpanCache3D(const Standard_Integer         theDegree,
+                                            const Standard_Boolean         thePeriodic,
+                                            const TColStd_Array1OfReal*    theKnots,
+                                            const TColStd_Array1OfInteger* theMults,
+                                            const TColStd_Array1OfReal*    theFlatKnots,
+                                            const TColgp_Array1OfPnt&      thePoles,
+                                            const TColStd_Array1OfReal*    theWeights  = NULL)
+    : BSplCLib_MultiSpanCacheBase<gp_Pnt, gp_Vec>(theDegree, thePeriodic, theKnots, theMults,
+                                                  theFlatKnots, thePoles, theWeights)
+  {
+  }
+
+  DEFINE_STANDARD_RTTI_INLINE(BSplCLib_MultiSpanCache3D, Standard_Transient)
+};
+
+DEFINE_STANDARD_HANDLE(BSplCLib_MultiSpanCache2D, Standard_Transient)
+DEFINE_STANDARD_HANDLE(BSplCLib_MultiSpanCache3D, Standard_Transient)
+
+#include <BSplCLib_MultiSpanCache.gxx>
+
+#endif
diff --git a/src/BSplCLib/FILES b/src/BSplCLib/FILES
index 2975ef6145..b5f81d4281 100755
--- a/src/BSplCLib/FILES
+++ b/src/BSplCLib/FILES
@@ -11,3 +11,5 @@ BSplCLib_CurveComputation.gxx
 BSplCLib_EvaluatorFunction.hxx
 BSplCLib_KnotDistribution.hxx
 BSplCLib_MultDistribution.hxx
+BSplCLib_MultiSpanCache.gxx
+BSplCLib_MultiSpanCache.hxx
diff --git a/src/BSplSLib/BSplSLib_Cache.cxx b/src/BSplSLib/BSplSLib_Cache.cxx
index dd9ba2eaac..eb52c0532c 100644
--- a/src/BSplSLib/BSplSLib_Cache.cxx
+++ b/src/BSplSLib/BSplSLib_Cache.cxx
@@ -33,107 +33,41 @@ static Standard_Real* ConvertArray(const Handle(TColStd_HArray2OfReal)& theHArra
 
 
 BSplSLib_Cache::BSplSLib_Cache()
+  : myIsRational(Standard_False)
 {
-  myPolesWeights.Nullify();
-  myIsRational = Standard_False;
-  mySpanStart[0]  = mySpanStart[1]  = 0.0;
+  mySpanStart[0] = mySpanStart[1] = 0.0;
   mySpanLength[0] = mySpanLength[1] = 0.0;
-  mySpanIndex[0]  = mySpanIndex[1]  = 0;
-  myDegree[0]     = myDegree[1]     = 0;
-  myFlatKnots[0].Nullify();
-  myFlatKnots[1].Nullify();
-}
-
-BSplSLib_Cache::BSplSLib_Cache(const Standard_Integer&        theDegreeU,
-                               const Standard_Boolean&        thePeriodicU,
-                               const TColStd_Array1OfReal&    theFlatKnotsU,
-                               const Standard_Integer&        theDegreeV,
-                               const Standard_Boolean&        thePeriodicV,
-                               const TColStd_Array1OfReal&    theFlatKnotsV,
-                               const TColgp_Array2OfPnt&      thePoles,
-                               const TColStd_Array2OfReal*    theWeights)
-{
-  Standard_Real aU = theFlatKnotsU.Value(theFlatKnotsU.Lower() + theDegreeU);
-  Standard_Real aV = theFlatKnotsV.Value(theFlatKnotsV.Lower() + theDegreeV);
-
-  BuildCache(aU, aV, 
-             theDegreeU, thePeriodicU, theFlatKnotsU, 
-             theDegreeV, thePeriodicV, theFlatKnotsV, 
-             thePoles, theWeights);
+  mySpanLengthRec[0] = mySpanLengthRec[1] = 0.0;
+  mySpanIndex[0] = mySpanIndex[1] = 0;
+  mySpanIndexMin[0] = mySpanIndexMin[1] = 0;
+  mySpanIndexMax[0] = mySpanIndexMax[1] = 0;
+  myDegree[0] = myDegree[1] = 0;
 }
 
 
 Standard_Boolean BSplSLib_Cache::IsCacheValid(Standard_Real theParameterU,
                                               Standard_Real theParameterV) const
 {
-  Standard_Real aNewU = theParameterU;
-  Standard_Real aNewV = theParameterV;
-  if (!myFlatKnots[0].IsNull())
-    PeriodicNormalization(myDegree[0], myFlatKnots[0]->Array1(), aNewU);
-  if (!myFlatKnots[1].IsNull())
-    PeriodicNormalization(myDegree[1], myFlatKnots[1]->Array1(), aNewV);
-
-  Standard_Real aDelta0 = aNewU - mySpanStart[0];
-  Standard_Real aDelta1 = aNewV - mySpanStart[1];
+  Standard_Real aDelta0 = theParameterU - mySpanStart[0];
+  Standard_Real aDelta1 = theParameterV - mySpanStart[1];
   return ((aDelta0 >= -mySpanLength[0] || mySpanIndex[0] == mySpanIndexMin[0]) &&
-          (aDelta0 < mySpanLength[0] || mySpanIndex[0] == mySpanIndexMax[0]) &&
+          (aDelta0 <   mySpanLength[0] || mySpanIndex[0] == mySpanIndexMax[0]) &&
           (aDelta1 >= -mySpanLength[1] || mySpanIndex[1] == mySpanIndexMin[1]) &&
-          (aDelta1 < mySpanLength[1] || mySpanIndex[1] == mySpanIndexMax[1]));
-}
-
-void BSplSLib_Cache::PeriodicNormalization(const Standard_Integer& theDegree, 
-                                           const TColStd_Array1OfReal& theFlatKnots, 
-                                           Standard_Real& theParameter) const
-{
-  Standard_Real aPeriod = theFlatKnots.Value(theFlatKnots.Upper() - theDegree) - 
-                          theFlatKnots.Value(theDegree + 1) ;
-  if (theParameter < theFlatKnots.Value(theDegree + 1))
-  {
-    Standard_Real aScale = IntegerPart(
-        (theFlatKnots.Value(theDegree + 1) - theParameter) / aPeriod);
-    theParameter += aPeriod * (aScale + 1.0);
-  }
-  if (theParameter > theFlatKnots.Value(theFlatKnots.Upper() - theDegree))
-  {
-    Standard_Real aScale = IntegerPart(
-        (theParameter - theFlatKnots.Value(theFlatKnots.Upper() - theDegree)) / aPeriod);
-    theParameter -= aPeriod * (aScale + 1.0);
-  }
+          (aDelta1 <   mySpanLength[1] || mySpanIndex[1] == mySpanIndexMax[1]));
 }
 
 
-void BSplSLib_Cache::BuildCache(const Standard_Real&           theParameterU, 
-                                const Standard_Real&           theParameterV, 
-                                const Standard_Integer&        theDegreeU, 
-                                const Standard_Boolean&        thePeriodicU, 
-                                const TColStd_Array1OfReal&    theFlatKnotsU, 
-                                const Standard_Integer&        theDegreeV, 
-                                const Standard_Boolean&        thePeriodicV, 
-                                const TColStd_Array1OfReal&    theFlatKnotsV, 
-                                const TColgp_Array2OfPnt&      thePoles, 
-                                const TColStd_Array2OfReal*    theWeights)
+void BSplSLib_Cache::BuildCache(const Standard_Integer      theDegreeU, 
+                                const Standard_Boolean      thePeriodicU, 
+                                const TColStd_Array1OfReal& theFlatKnotsU, 
+                                const Standard_Integer      theCachedSpanU,
+                                const Standard_Integer      theDegreeV,
+                                const Standard_Boolean      thePeriodicV, 
+                                const TColStd_Array1OfReal& theFlatKnotsV, 
+                                const Standard_Integer      theCachedSpanV,
+                                const TColgp_Array2OfPnt&   thePoles,
+                                const TColStd_Array2OfReal* theWeights)
 {
-  // Normalize the parameters for periodical B-splines
-  Standard_Real aNewParamU = theParameterU;
-  if (thePeriodicU)
-  {
-    PeriodicNormalization(theDegreeU, theFlatKnotsU, aNewParamU);
-    myFlatKnots[0] = new TColStd_HArray1OfReal(1, theFlatKnotsU.Length());
-    myFlatKnots[0]->ChangeArray1() = theFlatKnotsU;
-  }
-  else if (!myFlatKnots[0].IsNull()) // Periodical curve became non-periodical
-    myFlatKnots[0].Nullify();
-
-  Standard_Real aNewParamV = theParameterV;
-  if (thePeriodicV)
-  {
-    PeriodicNormalization(theDegreeV, theFlatKnotsV, aNewParamV);
-    myFlatKnots[1] = new TColStd_HArray1OfReal(1, theFlatKnotsV.Length());
-    myFlatKnots[1]->ChangeArray1() = theFlatKnotsV;
-  }
-  else if (!myFlatKnots[1].IsNull()) // Periodical curve became non-periodical
-    myFlatKnots[1].Nullify();
-
   Standard_Integer aMinDegree = Min(theDegreeU, theDegreeV);
   Standard_Integer aMaxDegree = Max(theDegreeU, theDegreeV);
 
@@ -145,27 +79,17 @@ void BSplSLib_Cache::BuildCache(const Standard_Real&           theParameterU,
 
   myDegree[0] = theDegreeU;
   myDegree[1] = theDegreeV;
-  mySpanIndex[0] = mySpanIndex[1] = 0;
-  BSplCLib::LocateParameter(theDegreeU, theFlatKnotsU, BSplCLib::NoMults(), aNewParamU, 
-                            thePeriodicU, mySpanIndex[0], aNewParamU);
-  BSplCLib::LocateParameter(theDegreeV, theFlatKnotsV, BSplCLib::NoMults(), aNewParamV, 
-                            thePeriodicV, mySpanIndex[1], aNewParamV);
-
-  // Protection against Out of Range exception.
-  if (mySpanIndex[0] >= theFlatKnotsU.Length()) {
-    mySpanIndex[0] = theFlatKnotsU.Length() - 1;
-  }
+  mySpanIndex[0] = theCachedSpanU;
+  mySpanIndex[1] = theCachedSpanV;
 
   mySpanLength[0] = (theFlatKnotsU.Value(mySpanIndex[0] + 1) - theFlatKnotsU.Value(mySpanIndex[0])) * 0.5;
+  mySpanLengthRec[0] = 1.0 / mySpanLength[0];
   mySpanStart[0]  = theFlatKnotsU.Value(mySpanIndex[0]) + mySpanLength[0];
 
-  // Protection against Out of Range exception.
-  if (mySpanIndex[1] >= theFlatKnotsV.Length()) {
-    mySpanIndex[1] = theFlatKnotsV.Length() - 1;
-  }
-
   mySpanLength[1] = (theFlatKnotsV.Value(mySpanIndex[1] + 1) - theFlatKnotsV.Value(mySpanIndex[1])) * 0.5;
-  mySpanStart[1]  = theFlatKnotsV.Value(mySpanIndex[1]) + mySpanLength[1];
+  mySpanLengthRec[1] = 1.0 / mySpanLength[1];
+  mySpanStart[1] = theFlatKnotsV.Value(mySpanIndex[1]) + mySpanLength[1];
+
   mySpanIndexMin[0] = thePeriodicU ? 0 : theDegreeU + 1;
   mySpanIndexMax[0] = theFlatKnotsU.Length() - 1 - theDegreeU;
   mySpanIndexMin[1] = thePeriodicV ? 0 : theDegreeV + 1;
@@ -182,18 +106,12 @@ void BSplSLib_Cache::BuildCache(const Standard_Real&           theParameterU,
 }
 
 
-void BSplSLib_Cache::D0(const Standard_Real& theU, 
-                        const Standard_Real& theV, 
-                              gp_Pnt&        thePoint) const
+void BSplSLib_Cache::D0(const Standard_Real theU, 
+                        const Standard_Real theV, 
+                              gp_Pnt&       thePoint) const
 {
-  Standard_Real aNewU = theU;
-  Standard_Real aNewV = theV;
-  if (!myFlatKnots[0].IsNull()) // B-spline is U-periodical
-    PeriodicNormalization(myDegree[0], myFlatKnots[0]->Array1(), aNewU);
-  aNewU = (aNewU - mySpanStart[0]) / mySpanLength[0];
-  if (!myFlatKnots[1].IsNull()) // B-spline is V-periodical
-    PeriodicNormalization(myDegree[1], myFlatKnots[1]->Array1(), aNewV);
-  aNewV = (aNewV - mySpanStart[1]) / mySpanLength[1];
+  Standard_Real aNewU = (theU - mySpanStart[0]) / mySpanLength[0];
+  Standard_Real aNewV = (theV - mySpanStart[1]) / mySpanLength[1];
 
   Standard_Real* aPolesArray = ConvertArray(myPolesWeights);
   Standard_Real aPoint[4];
@@ -232,26 +150,19 @@ void BSplSLib_Cache::D0(const Standard_Real& theU,
 }
 
 
-void BSplSLib_Cache::D1(const Standard_Real& theU, 
-                        const Standard_Real& theV, 
-                              gp_Pnt&        thePoint, 
-                              gp_Vec&        theTangentU, 
-                              gp_Vec&        theTangentV) const
+void BSplSLib_Cache::D1(const Standard_Real theU, 
+                        const Standard_Real theV, 
+                              gp_Pnt&       thePoint, 
+                              gp_Vec&       theTangentU, 
+                              gp_Vec&       theTangentV) const
 {
-  Standard_Real aNewU = theU;
-  Standard_Real aNewV = theV;
-  Standard_Real anInvU = 1.0 / mySpanLength[0];
-  Standard_Real anInvV = 1.0 / mySpanLength[1];
-  if (!myFlatKnots[0].IsNull()) // B-spline is U-periodical
-    PeriodicNormalization(myDegree[0], myFlatKnots[0]->Array1(), aNewU);
-  aNewU = (aNewU - mySpanStart[0]) * anInvU;
-  if (!myFlatKnots[1].IsNull()) // B-spline is V-periodical
-    PeriodicNormalization(myDegree[1], myFlatKnots[1]->Array1(), aNewV);
-  aNewV = (aNewV - mySpanStart[1]) * anInvV;
+  Standard_Real aNewU = (theU - mySpanStart[0]) * mySpanLengthRec[0];
+  Standard_Real aNewV = (theV - mySpanStart[1]) * mySpanLengthRec[1];
 
   Standard_Real* aPolesArray = ConvertArray(myPolesWeights);
-  Standard_Real aPntDeriv[16]; // result storage (point and derivative coordinates)
-  for (Standard_Integer i = 0; i< 16; i++) aPntDeriv[i] = 0.0;
+  static const Standard_Integer PNT_DERIV_LEN = 16;
+  Standard_Real aPntDeriv[PNT_DERIV_LEN]; // result storage (point and derivative coordinates)
+  memset(aPntDeriv, 0, PNT_DERIV_LEN * sizeof(Standard_Real));
 
   Standard_Integer aDimension = myIsRational ? 4 : 3;
   Standard_Integer aCacheCols = myPolesWeights->RowLength();
@@ -284,7 +195,7 @@ void BSplSLib_Cache::D1(const Standard_Real& theU,
                                    aPntDeriv[aDimension<<1]);
 
   Standard_Real* aResult = aPntDeriv;
-  Standard_Real aTempStorage[12];
+  Standard_Real aTempStorage[PNT_DERIV_LEN];
   if (myIsRational) // calculate derivatives divided by weight's derivatives
   {
     BSplSLib::RationalDerivative(1, 1, 1, 1, aPntDeriv[0], aTempStorage[0]);
@@ -305,34 +216,27 @@ void BSplSLib_Cache::D1(const Standard_Real& theU,
     Standard_Integer aShift = aDimension<<1;
     theTangentV.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]);
   }
-  theTangentU.Multiply(anInvU);
-  theTangentV.Multiply(anInvV);
+  theTangentU.Multiply(mySpanLengthRec[0]);
+  theTangentV.Multiply(mySpanLengthRec[1]);
 }
 
 
-void BSplSLib_Cache::D2(const Standard_Real& theU, 
-                        const Standard_Real& theV, 
-                              gp_Pnt&        thePoint, 
-                              gp_Vec&        theTangentU, 
-                              gp_Vec&        theTangentV, 
-                              gp_Vec&        theCurvatureU, 
-                              gp_Vec&        theCurvatureV, 
-                              gp_Vec&        theCurvatureUV) const
+void BSplSLib_Cache::D2(const Standard_Real theU, 
+                        const Standard_Real theV, 
+                              gp_Pnt&       thePoint, 
+                              gp_Vec&       theTangentU, 
+                              gp_Vec&       theTangentV, 
+                              gp_Vec&       theCurvatureU, 
+                              gp_Vec&       theCurvatureV, 
+                              gp_Vec&       theCurvatureUV) const
 {
-  Standard_Real aNewU = theU;
-  Standard_Real aNewV = theV;
-  Standard_Real anInvU = 1.0 / mySpanLength[0];
-  Standard_Real anInvV = 1.0 / mySpanLength[1];
-  if (!myFlatKnots[0].IsNull()) // B-spline is U-periodical
-    PeriodicNormalization(myDegree[0], myFlatKnots[0]->Array1(), aNewU);
-  aNewU = (aNewU - mySpanStart[0]) * anInvU;
-  if (!myFlatKnots[1].IsNull()) // B-spline is V-periodical
-    PeriodicNormalization(myDegree[1], myFlatKnots[1]->Array1(), aNewV);
-  aNewV = (aNewV - mySpanStart[1]) * anInvV;
+  Standard_Real aNewU = (theU - mySpanStart[0]) * mySpanLengthRec[0];
+  Standard_Real aNewV = (theV - mySpanStart[1]) * mySpanLengthRec[1];
 
   Standard_Real* aPolesArray = ConvertArray(myPolesWeights);
-  Standard_Real aPntDeriv[36]; // result storage (point and derivative coordinates)
-  for (Standard_Integer i = 0; i < 36; i++) aPntDeriv[i] = 0.0;
+  static const Standard_Integer PNT_DERIV_LEN = 36;
+  Standard_Real aPntDeriv[PNT_DERIV_LEN]; // result storage (point and derivative coordinates)
+  memset(aPntDeriv, 0, PNT_DERIV_LEN * sizeof(Standard_Real));
 
   Standard_Integer aDimension = myIsRational ? 4 : 3;
   Standard_Integer aCacheCols = myPolesWeights->RowLength();
@@ -381,7 +285,7 @@ void BSplSLib_Cache::D2(const Standard_Real& theU,
                                    aPntDeriv[6 * aDimension]);
 
   Standard_Real* aResult = aPntDeriv;
-  Standard_Real aTempStorage[36];
+  Standard_Real aTempStorage[PNT_DERIV_LEN];
   if (myIsRational) // calculate derivatives divided by weight's derivatives
   {
     BSplSLib::RationalDerivative(2, 2, 2, 2, aPntDeriv[0], aTempStorage[0]);
@@ -414,10 +318,10 @@ void BSplSLib_Cache::D2(const Standard_Real& theU,
     aShift += (aDimension << 1);
     theCurvatureV.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]);
   }
-  theTangentU.Multiply(anInvU);
-  theTangentV.Multiply(anInvV);
-  theCurvatureU.Multiply(anInvU * anInvU);
-  theCurvatureV.Multiply(anInvV * anInvV);
-  theCurvatureUV.Multiply(anInvU * anInvV);
+  theTangentU.Multiply(mySpanLengthRec[0]);
+  theTangentV.Multiply(mySpanLengthRec[1]);
+  theCurvatureU.Multiply(mySpanLengthRec[0] * mySpanLengthRec[0]);
+  theCurvatureV.Multiply(mySpanLengthRec[1] * mySpanLengthRec[1]);
+  theCurvatureUV.Multiply(mySpanLengthRec[0] * mySpanLengthRec[1]);
 }
 
diff --git a/src/BSplSLib/BSplSLib_Cache.hxx b/src/BSplSLib/BSplSLib_Cache.hxx
index 821cf7e1f4..f81b1407cf 100644
--- a/src/BSplSLib/BSplSLib_Cache.hxx
+++ b/src/BSplSLib/BSplSLib_Cache.hxx
@@ -38,23 +38,6 @@ class BSplSLib_Cache : public Standard_Transient
 public:
   //! Default constructor
   Standard_EXPORT BSplSLib_Cache();
-  //! Constructor for caching of the span for the surface
-  //! \param theDegreeU    degree along the first parameter (U) of the surface
-  //! \param thePeriodicU  identify the surface is periodical along U axis
-  //! \param theFlatKnotsU knots of the surface (with repetition) along U axis
-  //! \param theDegreeV    degree alogn the second parameter (V) of the surface
-  //! \param thePeriodicV  identify the surface is periodical along V axis
-  //! \param theFlatKnotsV knots of the surface (with repetition) along V axis
-  //! \param thePoles      array of poles of the surface
-  //! \param theWeights    array of weights of corresponding poles
-  Standard_EXPORT BSplSLib_Cache(const Standard_Integer&        theDegreeU,
-                                 const Standard_Boolean&        thePeriodicU,
-                                 const TColStd_Array1OfReal&    theFlatKnotsU,
-                                 const Standard_Integer&        theDegreeV,
-                                 const Standard_Boolean&        thePeriodicV,
-                                 const TColStd_Array1OfReal&    theFlatKnotsV,
-                                 const TColgp_Array2OfPnt&      thePoles,
-                                 const TColStd_Array2OfReal*    theWeights = NULL);
 
   //! Verifies validity of the cache using parameters of the point
   //! \param theParameterU  first parameter of the point placed in the span
@@ -63,32 +46,32 @@ public:
                                                 Standard_Real theParameterV) const;
 
   //! Recomputes the cache data. Does not verify validity of the cache
-  //! \param theParameterU  the parametric value on the U axis to identify the span
-  //! \param theParameterV  the parametric value on the V axis to identify the span
   //! \param theDegreeU     degree along U axis
   //! \param thePeriodicU   identify whether the surface is periodic along U axis
   //! \param theFlatKnotsU  flat knots of the surface along U axis
+  //! \param theCachedSpanU index along U axis of span to be cached
   //! \param theDegreeV     degree along V axis
   //! \param thePeriodicV   identify whether the surface is periodic along V axis
   //! \param theFlatKnotsV  flat knots of the surface along V axis
+  //! \param theCachedSpanV index along V axis of span to be cached
   //! \param thePoles       array of poles of the surface
   //! \param theWeights     array of weights of corresponding poles
-  Standard_EXPORT void BuildCache(const Standard_Real&           theParameterU, 
-                                  const Standard_Real&           theParameterV, 
-                                  const Standard_Integer&        theDegreeU, 
-                                  const Standard_Boolean&        thePeriodicU, 
-                                  const TColStd_Array1OfReal&    theFlatKnotsU, 
-                                  const Standard_Integer&        theDegreeV, 
-                                  const Standard_Boolean&        thePeriodicV, 
-                                  const TColStd_Array1OfReal&    theFlatKnotsV, 
-                                  const TColgp_Array2OfPnt&      thePoles, 
-                                  const TColStd_Array2OfReal*    theWeights = NULL);
+  Standard_EXPORT void BuildCache(const Standard_Integer      theDegreeU, 
+                                  const Standard_Boolean      thePeriodicU, 
+                                  const TColStd_Array1OfReal& theFlatKnotsU, 
+                                  const Standard_Integer      theCachedSpanU,
+                                  const Standard_Integer      theDegreeV,
+                                  const Standard_Boolean      thePeriodicV, 
+                                  const TColStd_Array1OfReal& theFlatKnotsV, 
+                                  const Standard_Integer      theCachedSpanV,
+                                  const TColgp_Array2OfPnt&   thePoles,
+                                  const TColStd_Array2OfReal* theWeights = NULL);
 
   //! Calculates the point on the surface for specified parameters
   //! \param[in]  theU      first parameter for calculation of the value
   //! \param[in]  theV      second parameter for calculation of the value
   //! \param[out] thePoint  the result of calculation (the point on the surface)
-  Standard_EXPORT void D0(const Standard_Real& theU, const Standard_Real& theV, gp_Pnt& thePoint) const;
+  Standard_EXPORT void D0(const Standard_Real theU, const Standard_Real theV, gp_Pnt& thePoint) const;
 
   //! Calculates the point on the surface and its first derivative
   //! \param[in]  theU         first parameter of calculation of the value
@@ -96,11 +79,11 @@ public:
   //! \param[out] thePoint     the result of calculation (the point on the surface)
   //! \param[out] theTangentU  tangent vector along U axis in the calculated point
   //! \param[out] theTangentV  tangent vector along V axis in the calculated point
-  Standard_EXPORT void D1(const Standard_Real& theU, 
-                          const Standard_Real& theV, 
-                                gp_Pnt&        thePoint, 
-                                gp_Vec&        theTangentU, 
-                                gp_Vec&        theTangentV) const;
+  Standard_EXPORT void D1(const Standard_Real theU, 
+                          const Standard_Real theV, 
+                                gp_Pnt&       thePoint, 
+                                gp_Vec&       theTangentU, 
+                                gp_Vec&       theTangentV) const;
 
   //! Calculates the point on the surface and derivatives till second order
   //! \param[in]  theU            first parameter of calculation of the value
@@ -111,27 +94,18 @@ public:
   //! \param[out] theCurvatureU   curvature vector (2nd derivative on U) along U axis
   //! \param[out] theCurvatureV   curvature vector (2nd derivative on V) along V axis
   //! \param[out] theCurvatureUV  2nd mixed derivative on U anv V
-  Standard_EXPORT void D2(const Standard_Real& theU, 
-                          const Standard_Real& theV, 
-                                gp_Pnt&        thePoint, 
-                                gp_Vec&        theTangentU, 
-                                gp_Vec&        theTangentV, 
-                                gp_Vec&        theCurvatureU, 
-                                gp_Vec&        theCurvatureV, 
-                                gp_Vec&        theCurvatureUV) const;
+  Standard_EXPORT void D2(const Standard_Real theU, 
+                          const Standard_Real theV, 
+                                gp_Pnt&       thePoint, 
+                                gp_Vec&       theTangentU, 
+                                gp_Vec&       theTangentV, 
+                                gp_Vec&       theCurvatureU, 
+                                gp_Vec&       theCurvatureV, 
+                                gp_Vec&       theCurvatureUV) const;
 
 
   DEFINE_STANDARD_RTTIEXT(BSplSLib_Cache,Standard_Transient)
 
-protected:
-  //! Normalizes the parameter for periodical surfaces
-  //! \param[in]     theDegree     degree along selected direction
-  //! \param[in]     theFlatKnots  knots with repetitions along selected direction
-  //! \param[in,out] theParameter  the value to be normalized into the knots array
-  void PeriodicNormalization(const Standard_Integer& theDegree, 
-                             const TColStd_Array1OfReal& theFlatKnots, 
-                                   Standard_Real& theParameter) const;
-
 private:
   Handle(TColStd_HArray2OfReal) myPolesWeights; ///< array of poles and weights of calculated cache
                                                 // the array has following structure:
@@ -140,15 +114,14 @@ private:
                                                 // for non-rational surfaces there is no weight;
                                                 // size of array: (max(myDegree)+1) * A*(min(myDegree)+1), where A = 4 or 3
 
-  Standard_Boolean              myIsRational;    ///< identifies the rationality of Bezier/B-spline surface
-  Standard_Real                 mySpanStart[2];  ///< parameters (u, v) for the frst point of the span
-  Standard_Real                 mySpanLength[2]; ///< lengths of the span along corresponding parameter
-  Standard_Integer              mySpanIndex[2];  ///< indexes of the span on Bezier/B-spline surface
-  Standard_Integer              mySpanIndexMin[2]; ///< minimal indexes of span
-  Standard_Integer              mySpanIndexMax[2]; ///< maximal indexes of span
-  Standard_Integer              myDegree[2];     ///< degrees of Bezier/B-spline for each parameter
-  Handle(TColStd_HArray1OfReal) myFlatKnots[2];  ///< arrays of knots of Bezier/B-spline 
-                                                 // (used for periodic normalization of parameters, Null for non-periodical splines)
+  Standard_Boolean              myIsRational;       ///< identifies the rationality of Bezier/B-spline surface
+  Standard_Real                 mySpanStart[2];     ///< parameters (u, v) for the frst point of the span
+  Standard_Real                 mySpanLength[2];    ///< lengths of the span along corresponding parameter
+  Standard_Real                 mySpanLengthRec[2]; ///< reciprocal values for the span lengths (mySpanLengthRec[i] = 1/mySpanLength[i])
+  Standard_Integer              mySpanIndex[2];     ///< indexes of the span on Bezier/B-spline surface
+  Standard_Integer              mySpanIndexMin[2];  ///< minimal indexes of span
+  Standard_Integer              mySpanIndexMax[2];  ///< maximal indexes of span
+  Standard_Integer              myDegree[2];        ///< degrees of Bezier/B-spline for each parameter
 };
 
 DEFINE_STANDARD_HANDLE(BSplSLib_Cache, Standard_Transient)
diff --git a/src/BSplSLib/BSplSLib_MultiSpanCache.cxx b/src/BSplSLib/BSplSLib_MultiSpanCache.cxx
new file mode 100644
index 0000000000..4e50ef06a4
--- /dev/null
+++ b/src/BSplSLib/BSplSLib_MultiSpanCache.cxx
@@ -0,0 +1,235 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <BSplSLib_MultiSpanCache.hxx>
+
+#include <BSplCLib.hxx>
+#include <ElCLib.hxx>
+#include <Precision.hxx>
+
+BSplSLib_MultiSpanCache::BSplSLib_MultiSpanCache(const Standard_Integer         theDegreeU,
+                                                 const Standard_Boolean         thePeriodicU,
+                                                 const Standard_Integer         theDegreeV,
+                                                 const Standard_Boolean         thePeriodicV,
+                                                 const TColgp_Array2OfPnt&      thePoles,
+                                                 const TColStd_Array2OfReal*    theWeights)
+  : myDegreeU(theDegreeU),
+    myDegreeV(theDegreeV),
+    myPeriodicU(thePeriodicU),
+    myPeriodicV(thePeriodicV),
+    myKnotsU(NULL),
+    myKnotsV(NULL),
+    myMultsU(NULL),
+    myMultsV(NULL),
+    myFlatKnotsU(NULL),
+    myFlatKnotsV(NULL),
+    myFirstKnotU(0.),
+    myFirstKnotV(0.),
+    myLastKnotU(1.),
+    myLastKnotV(1.),
+    myPoles(thePoles),
+    myWeights(theWeights),
+    mySingleSpanU(1),
+    mySingleSpanV(1),
+    myCaches(1)
+{
+  Clear();
+
+  // Build cache for single span
+  TColStd_Array1OfReal aFlatKnotsU(BSplCLib::FlatBezierKnots(myDegreeU), 1, (myDegreeU + 1) * 2);
+  TColStd_Array1OfReal aFlatKnotsV(BSplCLib::FlatBezierKnots(myDegreeV), 1, (myDegreeV + 1) * 2);
+  myCaches[myLastCacheInd] = new BSplSLib_Cache;
+  myCaches[myLastCacheInd]->BuildCache(myDegreeU, myPeriodicU, aFlatKnotsU, myDegreeU + 1,
+                                       myDegreeV, myPeriodicV, aFlatKnotsV, myDegreeV + 1,
+                                       myPoles, myWeights);
+}
+
+BSplSLib_MultiSpanCache::BSplSLib_MultiSpanCache(const Standard_Integer         theDegreeU,
+                                                 const Standard_Boolean         thePeriodicU,
+                                                 const TColStd_Array1OfReal*    theKnotsU,
+                                                 const TColStd_Array1OfInteger* theMultsU,
+                                                 const TColStd_Array1OfReal*    theFlatKnotsU,
+                                                 const Standard_Integer         theDegreeV,
+                                                 const Standard_Boolean         thePeriodicV,
+                                                 const TColStd_Array1OfReal*    theKnotsV,
+                                                 const TColStd_Array1OfInteger* theMultsV,
+                                                 const TColStd_Array1OfReal*    theFlatKnotsV,
+                                                 const TColgp_Array2OfPnt&      thePoles,
+                                                 const TColStd_Array2OfReal*    theWeights)
+  : myDegreeU(theDegreeU),
+    myDegreeV(theDegreeV),
+    myPeriodicU(thePeriodicU),
+    myPeriodicV(thePeriodicV),
+    myKnotsU(theKnotsU),
+    myKnotsV(theKnotsV),
+    myMultsU(theMultsU),
+    myMultsV(theMultsV),
+    myFlatKnotsU(theFlatKnotsU),
+    myFlatKnotsV(theFlatKnotsV),
+    myFirstKnotU(theKnotsU->First()),
+    myFirstKnotV(theKnotsV->First()),
+    myLastKnotU(theKnotsU->Last()),
+    myLastKnotV(theKnotsV->Last()),
+    myPoles(thePoles),
+    myWeights(theWeights),
+    mySingleSpanU(0),
+    mySingleSpanV(0),
+    myCaches((theKnotsU->Upper() - theKnotsU->Lower()) * (theKnotsV->Upper() - theKnotsV->Lower()))
+{
+  Clear();
+
+  Standard_Boolean isSingleSpanU = myKnotsU->Length() == 2;
+  Standard_Boolean isSingleSpanV = myKnotsV->Length() == 2;
+
+  // Check the surface has one span at all
+  if (isSingleSpanU || isSingleSpanV)
+  {
+    Standard_Integer aSpanIndex; // works as temporary index if surface has more than one span
+    Standard_Real aTempU = myKnotsU->First();
+    Standard_Real aTempV = myKnotsV->First();
+    SpanIndex(aTempU, aTempV,
+              isSingleSpanU ? mySingleSpanU : aSpanIndex,
+              isSingleSpanV ? mySingleSpanV : aSpanIndex);
+  }
+}
+
+BSplSLib_MultiSpanCache::~BSplSLib_MultiSpanCache()
+{
+  Standard_Integer aSize = (Standard_Integer)myCaches.Size();
+  for (Standard_Integer i = 0; i < aSize; ++i)
+    myCaches[i].Nullify();
+}
+
+void BSplSLib_MultiSpanCache::Clear()
+{
+  myLastCacheInd = 0;
+  myLastU.myParameter = Precision::Infinite();
+  myLastU.mySpan = -1;
+  myLastV.myParameter = Precision::Infinite();
+  myLastV.mySpan = -1;
+}
+
+void BSplSLib_MultiSpanCache::D0(const Standard_Real theU,
+                                 const Standard_Real theV,
+                                       gp_Pnt&       thePoint)
+{
+  Standard_Real aU = theU;
+  Standard_Real aV = theV;
+  const Handle(BSplSLib_Cache)& aCache = FindCache(aU, aV);
+  aCache->D0(aU, aV, thePoint);
+}
+
+void BSplSLib_MultiSpanCache::D1(const Standard_Real theU,
+                                 const Standard_Real theV, 
+                                       gp_Pnt&       thePoint, 
+                                       gp_Vec&       theTangentU, 
+                                       gp_Vec&       theTangentV)
+{
+  Standard_Real aU = theU;
+  Standard_Real aV = theV;
+  const Handle(BSplSLib_Cache)& aCache = FindCache(aU, aV);
+  aCache->D1(aU, aV, thePoint, theTangentU, theTangentV);
+}
+
+void BSplSLib_MultiSpanCache::D2(const Standard_Real theU,
+                                 const Standard_Real theV, 
+                                       gp_Pnt&       thePoint, 
+                                       gp_Vec&       theTangentU, 
+                                       gp_Vec&       theTangentV, 
+                                       gp_Vec&       theCurvatureU, 
+                                       gp_Vec&       theCurvatureV, 
+                                       gp_Vec&       theCurvatureUV)
+{
+  Standard_Real aU = theU;
+  Standard_Real aV = theV;
+  const Handle(BSplSLib_Cache)& aCache = FindCache(aU, aV);
+  aCache->D2(aU, aV, thePoint, theTangentU, theTangentV,
+             theCurvatureU, theCurvatureV, theCurvatureUV);
+}
+
+Standard_Integer BSplSLib_MultiSpanCache::SpanIndex(const Standard_Real theU,
+                                                    const Standard_Real theV,
+                                                    Standard_Integer&   theSpanIndexU,
+                                                    Standard_Integer&   theSpanIndexV) const
+{
+  // Get indices along each parameter (U, V)
+  theSpanIndexU = mySingleSpanU;
+  theSpanIndexV = mySingleSpanV;
+  if (IsSingleSpan())
+    return 0;
+
+  if (theSpanIndexU == 0)
+  {
+    if (theU == myLastU.myParameter) // the parameters should be equal to a bit
+      theSpanIndexU = myLastU.mySpan;
+    else
+    {
+      Standard_Real aNewU = theU; // unused parameter, because theU is already in period
+      BSplCLib::LocateParameter(myDegreeU, *myKnotsU, myMultsU, theU, myPeriodicU,
+                                theSpanIndexU, aNewU);
+    }
+  }
+  if (theSpanIndexV == 0)
+  {
+    if (theV == myLastV.myParameter) // the parameters should be equal to a bit
+      theSpanIndexV = myLastV.mySpan;
+    else
+    {
+      Standard_Real aNewV = theV; // unused parameter, because theV is already in period
+      BSplCLib::LocateParameter(myDegreeV, *myKnotsV, myMultsV, theV, myPeriodicV,
+                                theSpanIndexV, aNewV);
+    }
+  }
+  return (theSpanIndexU - myKnotsU->Lower()) * (myKnotsV->Upper() - myKnotsV->Lower()) +
+          theSpanIndexV - myKnotsV->Lower();
+}
+
+const Handle(BSplSLib_Cache)& BSplSLib_MultiSpanCache::FindCache(Standard_Real& theU, Standard_Real& theV)
+{
+  // Normalize the parameters for periodical B-splines
+  if (myPeriodicU && (theU > myLastKnotU || theU < myFirstKnotU))
+    theU = ElCLib::InPeriod(theU, myFirstKnotU, myLastKnotU);
+  if (myPeriodicV && (theV > myLastKnotV || theV < myFirstKnotV))
+    theV = ElCLib::InPeriod(theV, myFirstKnotV, myLastKnotV);
+
+  // Do not want to search in due to lack of performance.
+  // Just check the last used cache is valid for the given parameters.
+  if (!myCaches[myLastCacheInd].IsNull() &&
+      (IsSingleSpan() || myCaches[myLastCacheInd]->IsCacheValid(theU, theV)))
+    return myCaches[myLastCacheInd];
+
+  Standard_Integer aSpanIndexU = 0;
+  Standard_Integer aSpanIndexV = 0;
+  myLastCacheInd = SpanIndex(theU, theV, aSpanIndexU, aSpanIndexV);
+  // store last found indices
+  myLastU.myParameter = theU;
+  myLastU.mySpan = aSpanIndexU;
+  myLastV.myParameter = theV;
+  myLastV.mySpan = aSpanIndexV;
+
+  // check the cache is already built
+  Handle(BSplSLib_Cache)& aCache = myCaches[myLastCacheInd];
+  if (aCache.IsNull())
+  {
+    // calculate index of cache for the flat knots
+    aSpanIndexU = BSplCLib::FlatIndex(myDegreeU, aSpanIndexU, *myMultsU, myPeriodicU);
+    aSpanIndexV = BSplCLib::FlatIndex(myDegreeV, aSpanIndexV, *myMultsV, myPeriodicV);
+    // build new cache
+    aCache = new BSplSLib_Cache;
+    aCache->BuildCache(myDegreeU, myPeriodicU, *myFlatKnotsU, aSpanIndexU,
+                       myDegreeV, myPeriodicV, *myFlatKnotsV, aSpanIndexV,
+                       myPoles, myWeights);
+  }
+
+  return aCache;
+}
diff --git a/src/BSplSLib/BSplSLib_MultiSpanCache.hxx b/src/BSplSLib/BSplSLib_MultiSpanCache.hxx
new file mode 100644
index 0000000000..ab6b759156
--- /dev/null
+++ b/src/BSplSLib/BSplSLib_MultiSpanCache.hxx
@@ -0,0 +1,171 @@
+// Copyright (c) 2016 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _BSplSLib_MultiSpanCache_Headerfile
+#define _BSplSLib_MultiSpanCache_Headerfile
+
+#include <BSplSLib_Cache.hxx>
+#include <NCollection_LocalArray.hxx>
+
+//! \brief A container for list of caches for Bezier and B-spline surfaces.
+//!        Stores a 2D array of caches. Each cache is initialized in first time called.
+class BSplSLib_MultiSpanCache : public Standard_Transient
+{
+public:
+  //! Construct cache for Bezier surface with given parameters
+  //! \param theDegreeU    [in] degree along the first parameter (U) of the surface
+  //! \param thePeriodicU  [in] identify the surface is periodical along U axis
+  //! \param theDegreeV    [in] degree along the second parameter (V) of the surface
+  //! \param thePeriodicV  [in] identify the surface is periodical along V axis
+  //! \param thePoles      [in] array of poles of the surface
+  //! \param theWeights    [in] array of weights of corresponding poles
+  //! \param theMaxSpans   [in] maximal spans to be cached
+  Standard_EXPORT BSplSLib_MultiSpanCache(const Standard_Integer         theDegreeU,
+                                          const Standard_Boolean         thePeriodicU,
+                                          const Standard_Integer         theDegreeV,
+                                          const Standard_Boolean         thePeriodicV,
+                                          const TColgp_Array2OfPnt&      thePoles,
+                                          const TColStd_Array2OfReal*    theWeights  = NULL);
+
+  //! Construct multi-span cache for B-spline surface with given parameters
+  //! \param theDegreeU    [in] degree along the first parameter (U) of the surface
+  //! \param thePeriodicU  [in] identify the surface is periodical along U axis
+  //! \param theKnotsU     [in] knots of the surface along U axis
+  //! \param theMultsU     [in] multiplicities of knots along U axis
+  //! \param theFlatKnotsU [in] knots of the surface (with repetition) along U axis
+  //! \param theDegreeV    [in] degree alogn the second parameter (V) of the surface
+  //! \param thePeriodicV  [in] identify the surface is periodical along V axis
+  //! \param theKnotsV     [in] knots of the surface along V axis
+  //! \param theMultsV     [in] multiplicities of knots along V axis
+  //! \param theFlatKnotsV [in] knots of the surface (with repetition) along V axis
+  //! \param thePoles      [in] array of poles of the surface
+  //! \param theWeights    [in] array of weights of corresponding poles
+  //! \param theMaxSpans   [in] maximal spans to be cached
+  Standard_EXPORT BSplSLib_MultiSpanCache(const Standard_Integer         theDegreeU,
+                                          const Standard_Boolean         thePeriodicU,
+                                          const TColStd_Array1OfReal*    theKnotsU,
+                                          const TColStd_Array1OfInteger* theMultsU,
+                                          const TColStd_Array1OfReal*    theFlatKnotsU,
+                                          const Standard_Integer         theDegreeV,
+                                          const Standard_Boolean         thePeriodicV,
+                                          const TColStd_Array1OfReal*    theKnotsV,
+                                          const TColStd_Array1OfInteger* theMultsV,
+                                          const TColStd_Array1OfReal*    theFlatKnotsV,
+                                          const TColgp_Array2OfPnt&      thePoles,
+                                          const TColStd_Array2OfReal*    theWeights  = NULL);
+
+  virtual ~BSplSLib_MultiSpanCache();
+
+  //! Clean all cached data
+  Standard_EXPORT void Clear();
+
+  //! Calculates the point on the surface for specified parameters
+  //! \param theU     [in]  first parameter for calculation of the value
+  //! \param theV     [in]  second parameter for calculation of the value
+  //! \param thePoint [out] the result of calculation (the point on the surface)
+  Standard_EXPORT void D0(const Standard_Real theU, const Standard_Real theV, gp_Pnt& thePoint);
+
+  //! Calculates the point on the surface and its first derivative
+  //! \param theU        [in]  first parameter of calculation of the value
+  //! \param theV        [in]  second parameter of calculation of the value
+  //! \param thePoint    [out] the result of calculation (the point on the surface)
+  //! \param theTangentU [out] tangent vector along U axis in the calculated point
+  //! \param theTangentV [out] tangent vector along V axis in the calculated point
+  Standard_EXPORT void D1(const Standard_Real theU, 
+                          const Standard_Real theV, 
+                                gp_Pnt&       thePoint, 
+                                gp_Vec&       theTangentU, 
+                                gp_Vec&       theTangentV);
+
+  //! Calculates the point on the surface and derivatives till second order
+  //! \param theU           [in]  first parameter of calculation of the value
+  //! \param theV           [in]  second parameter of calculation of the value
+  //! \param thePoint       [out] the result of calculation (the point on the surface)
+  //! \param theTangentU    [out] tangent vector along U axis in the calculated point
+  //! \param theTangentV    [out] tangent vector along V axis in the calculated point
+  //! \param theCurvatureU  [out] curvature vector (2nd derivative on U) along U axis
+  //! \param theCurvatureV  [out] curvature vector (2nd derivative on V) along V axis
+  //! \param theCurvatureUV [out] 2nd mixed derivative on U anv V
+  Standard_EXPORT void D2(const Standard_Real theU, 
+                          const Standard_Real theV, 
+                                gp_Pnt&       thePoint, 
+                                gp_Vec&       theTangentU, 
+                                gp_Vec&       theTangentV, 
+                                gp_Vec&       theCurvatureU, 
+                                gp_Vec&       theCurvatureV, 
+                                gp_Vec&       theCurvatureUV);
+
+
+  DEFINE_STANDARD_RTTI_INLINE(BSplSLib_MultiSpanCache, Standard_Transient)
+
+private:
+  //! Calculate an index of a span by the parameters on surface
+  //! and corresponding span indices along each parameter.
+  //! \param theU          [in]  parameter U
+  //! \param theV          [in]  parameter V
+  //! \param theSpanIndexU [out] index of span containing theU parameter
+  //! \param theSpanIndexV [out] index of span containing theV parameter
+  //! \return Index of the cache in the array
+  Standard_Integer SpanIndex(const Standard_Real theU,
+                             const Standard_Real theV,
+                             Standard_Integer&   theSpanIndexU,
+                             Standard_Integer&   theSpanIndexV) const;
+
+  //! Find cached span containing parametric point (theU, theV).
+  //! Parameters are adjusted to period for periodic surface.
+  //! If such span is not cached yet, prepares new cache object.
+  const Handle(BSplSLib_Cache)& FindCache(Standard_Real& theU, Standard_Real& theV);
+
+  //! Return Standard_True if the surface has just one span
+  Standard_Boolean IsSingleSpan() const
+  { return mySingleSpanU != 0 && mySingleSpanV != 0; }
+
+  BSplSLib_MultiSpanCache(const BSplSLib_MultiSpanCache&);
+  const BSplSLib_MultiSpanCache& operator=(const BSplSLib_MultiSpanCache&);
+
+private:
+  typedef NCollection_LocalArray<Handle(BSplSLib_Cache), 1> CacheArray;
+
+  Standard_Integer               myDegreeU;       ///< degree along U
+  Standard_Integer               myDegreeV;       ///< degree along V
+  Standard_Boolean               myPeriodicU;     ///< periodicity along U
+  Standard_Boolean               myPeriodicV;     ///< periodicity along V
+  const TColStd_Array1OfReal*    myKnotsU;        ///< knots along U
+  const TColStd_Array1OfReal*    myKnotsV;        ///< knots along V
+  const TColStd_Array1OfInteger* myMultsU;        ///< multiplicities along U
+  const TColStd_Array1OfInteger* myMultsV;        ///< multiplicities along V
+  const TColStd_Array1OfReal*    myFlatKnotsU;    ///< knots along U (duplicated according to multiplicity)
+  const TColStd_Array1OfReal*    myFlatKnotsV;    ///< knots along V (duplicated according to multiplicity)
+  Standard_Real                  myFirstKnotU;    ///< first knot along U (used for periodic normalization)
+  Standard_Real                  myLastKnotU;     ///< last knot along U (used for periodic normalization)
+  Standard_Real                  myFirstKnotV;    ///< first knot along V (used for periodic normalization)
+  Standard_Real                  myLastKnotV;     ///< last knot along V (used for periodic normalization)
+  const TColgp_Array2OfPnt&      myPoles;         ///< array of poles
+  const TColStd_Array2OfReal*    myWeights;       ///< array of weights
+
+  CacheArray                     myCaches;        ///< array of caches
+  Standard_Integer               myLastCacheInd;  ///< index of last used cache
+
+  struct {
+    Standard_Real    myParameter;
+    Standard_Integer mySpan;
+  }                              myLastU,         ///< last U and index of the span containing it
+                                 myLastV;         ///< last V and index of the span containing it
+
+  Standard_Integer               mySingleSpanU;   ///< index of the span along U, if the surface has just one span on this parameter, otherwise is 0
+  Standard_Integer               mySingleSpanV;   ///< index of the span along V, if the surface has just one span on this parameter, otherwise is 0
+};
+
+DEFINE_STANDARD_HANDLE(BSplSLib_MultiSpanCache, Standard_Transient)
+
+#endif
diff --git a/src/BSplSLib/FILES b/src/BSplSLib/FILES
index a8a96a2cb7..b7bc4db07c 100755
--- a/src/BSplSLib/FILES
+++ b/src/BSplSLib/FILES
@@ -5,3 +5,5 @@ BSplSLib_BzSyntaxes.cxx
 BSplSLib_Cache.cxx
 BSplSLib_Cache.hxx
 BSplSLib_EvaluatorFunction.hxx
+BSplSLib_MultiSpanCache.cxx
+BSplSLib_MultiSpanCache.hxx
diff --git a/src/Geom2dAdaptor/Geom2dAdaptor_Curve.cxx b/src/Geom2dAdaptor/Geom2dAdaptor_Curve.cxx
index 82cb392a03..fa7cf50f43 100644
--- a/src/Geom2dAdaptor/Geom2dAdaptor_Curve.cxx
+++ b/src/Geom2dAdaptor/Geom2dAdaptor_Curve.cxx
@@ -24,7 +24,6 @@
 
 #include <Adaptor2d_HCurve2d.hxx>
 #include <BSplCLib.hxx>
-#include <BSplCLib_Cache.hxx>
 #include <Geom2d_BezierCurve.hxx>
 #include <Geom2d_BSplineCurve.hxx>
 #include <Geom2d_Circle.hxx>
@@ -182,13 +181,14 @@ void Geom2dAdaptor_Curve::load(const Handle(Geom2d_Curve)& C,
   myLast  = ULast;
   myCurveCache.Nullify();
 
-  if ( myCurve != C) {
+  if (myCurve != C)
+  {
     myCurve = C;
     myNestedEvaluator.Nullify();
     myBSplineCurve.Nullify();
 
-    Handle(Standard_Type) TheType = C->DynamicType();
-    if ( TheType == STANDARD_TYPE(Geom2d_TrimmedCurve)) {
+    const Handle(Standard_Type)& TheType = C->DynamicType();
+    if (TheType == STANDARD_TYPE(Geom2d_TrimmedCurve)) {
       Load(Handle(Geom2d_TrimmedCurve)::DownCast (C)->BasisCurve(),
 	   UFirst,ULast);
     }
@@ -546,36 +546,6 @@ Standard_Real Geom2dAdaptor_Curve::Period() const
   return myCurve->LastParameter() - myCurve->FirstParameter();
 }
 
-//=======================================================================
-//function : RebuildCache
-//purpose  : 
-//=======================================================================
-void Geom2dAdaptor_Curve::RebuildCache(const Standard_Real theParameter) const
-{
-  if (myTypeCurve == GeomAbs_BezierCurve)
-  {
-    // Create cache for Bezier
-    Handle(Geom2d_BezierCurve) aBezier = Handle(Geom2d_BezierCurve)::DownCast(myCurve);
-    Standard_Integer aDeg = aBezier->Degree();
-    TColStd_Array1OfReal aFlatKnots(BSplCLib::FlatBezierKnots(aDeg), 1, 2 * (aDeg + 1));
-    if (myCurveCache.IsNull())
-      myCurveCache = new BSplCLib_Cache(aDeg, aBezier->IsPeriodic(), aFlatKnots,
-        aBezier->Poles(), aBezier->Weights());
-    myCurveCache->BuildCache(theParameter, aDeg, aBezier->IsPeriodic(), aFlatKnots,
-      aBezier->Poles(), aBezier->Weights());
-  }
-  else if (myTypeCurve == GeomAbs_BSplineCurve)
-  {
-    // Create cache for B-spline
-    if (myCurveCache.IsNull())
-      myCurveCache = new BSplCLib_Cache(myBSplineCurve->Degree(), myBSplineCurve->IsPeriodic(),
-        myBSplineCurve->KnotSequence(), myBSplineCurve->Poles(), myBSplineCurve->Weights());
-    myCurveCache->BuildCache(theParameter, myBSplineCurve->Degree(),
-      myBSplineCurve->IsPeriodic(), myBSplineCurve->KnotSequence(),
-      myBSplineCurve->Poles(), myBSplineCurve->Weights());
-  }
-}
-
 //=======================================================================
 //function : IsBoundary
 //purpose  : 
@@ -607,6 +577,34 @@ Standard_Boolean Geom2dAdaptor_Curve::IsBoundary(const Standard_Real theU,
   return Standard_False;
 }
 
+//=======================================================================
+//function : CreateCache
+//purpose  : 
+//=======================================================================
+
+void Geom2dAdaptor_Curve::CreateCache() const
+{
+  switch (myTypeCurve)
+  {
+  case GeomAbs_BezierCurve: {
+    // Create cache for Bezier
+    Handle(Geom2d_BezierCurve) aBezier = Handle(Geom2d_BezierCurve)::DownCast(myCurve);
+    myCurveCache = new BSplCLib_MultiSpanCache2D(aBezier->Degree(), aBezier->IsPeriodic(),
+        aBezier->Poles(), aBezier->Weights());
+    break;
+  }
+  case GeomAbs_BSplineCurve:
+    // Create cache for B-spline
+    myCurveCache = new BSplCLib_MultiSpanCache2D(
+        myBSplineCurve->Degree(), myBSplineCurve->IsPeriodic(),
+        &myBSplineCurve->Knots(), &myBSplineCurve->Multiplicities(),
+        &myBSplineCurve->KnotSequence(), myBSplineCurve->Poles(), myBSplineCurve->Weights());
+    break;
+  default: // avoid gcc compilation warnings
+    break; 
+  }
+}
+
 //=======================================================================
 //function : Value
 //purpose  : 
@@ -633,14 +631,11 @@ void Geom2dAdaptor_Curve::D0(const Standard_Real U, gp_Pnt2d& P) const
   {
     Standard_Integer aStart = 0, aFinish = 0;
     if (IsBoundary(U, aStart, aFinish))
-    {
       myBSplineCurve->LocalD0(U, aStart, aFinish, P);
-    }
     else
     {
-      // use cached data
-      if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U))
-        RebuildCache(U);
+      if (myCurveCache.IsNull())
+        CreateCache();
       myCurveCache->D0(U, P);
     }
     break;
@@ -670,14 +665,11 @@ void Geom2dAdaptor_Curve::D1(const Standard_Real U,
   {
     Standard_Integer aStart = 0, aFinish = 0;
     if (IsBoundary(U, aStart, aFinish))
-    {
       myBSplineCurve->LocalD1(U, aStart, aFinish, P, V);
-    }
     else
     {
-      // use cached data
-      if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U))
-        RebuildCache(U);
+      if (myCurveCache.IsNull())
+        CreateCache();
       myCurveCache->D1(U, P, V);
     }
     break;
@@ -707,14 +699,11 @@ void Geom2dAdaptor_Curve::D2(const Standard_Real U,
   {
     Standard_Integer aStart = 0, aFinish = 0;
     if (IsBoundary(U, aStart, aFinish))
-    {
       myBSplineCurve->LocalD2(U, aStart, aFinish, P, V1, V2);
-    }
     else
     {
-      // use cached data
-      if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U))
-        RebuildCache(U);
+      if (myCurveCache.IsNull())
+        CreateCache();
       myCurveCache->D2(U, P, V1, V2);
     }
     break;
@@ -745,14 +734,11 @@ void Geom2dAdaptor_Curve::D3(const Standard_Real U,
   {
     Standard_Integer aStart = 0, aFinish = 0;
     if (IsBoundary(U, aStart, aFinish))
-    {
       myBSplineCurve->LocalD3(U, aStart, aFinish, P, V1, V2, V3);
-    }
     else
     {
-      // use cached data
-      if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U))
-        RebuildCache(U);
+      if (myCurveCache.IsNull())
+        CreateCache();
       myCurveCache->D3(U, P, V1, V2, V3);
     }
     break;
diff --git a/src/Geom2dAdaptor/Geom2dAdaptor_Curve.hxx b/src/Geom2dAdaptor/Geom2dAdaptor_Curve.hxx
index 6ecbf6a5c4..0dfd760abf 100644
--- a/src/Geom2dAdaptor/Geom2dAdaptor_Curve.hxx
+++ b/src/Geom2dAdaptor/Geom2dAdaptor_Curve.hxx
@@ -23,7 +23,7 @@
 
 #include <GeomAbs_CurveType.hxx>
 #include <Standard_Real.hxx>
-#include <BSplCLib_Cache.hxx>
+#include <BSplCLib_MultiSpanCache.hxx>
 #include <Adaptor2d_Curve2d.hxx>
 #include <GeomAbs_Shape.hxx>
 #include <Standard_Integer.hxx>
@@ -187,9 +187,8 @@ private:
   //! Check theU relates to start or finish point of B-spline curve and return indices of span the point is located
   Standard_Boolean IsBoundary(const Standard_Real theU, Standard_Integer& theSpanStart, Standard_Integer& theSpanFinish) const;
 
-  //! Rebuilds B-spline cache
-  //! \param theParameter the value on the knot axis which identifies the caching span
-  void RebuildCache (const Standard_Real theParameter) const;
+  //! Allocates memory for a cache object
+  void CreateCache() const;
 
 
   Handle(Geom2d_Curve) myCurve;
@@ -198,7 +197,7 @@ private:
   Standard_Real myLast;
 
   Handle(Geom2d_BSplineCurve) myBSplineCurve; ///< B-spline representation to prevent castings
-  mutable Handle(BSplCLib_Cache) myCurveCache; ///< Cached data for B-spline or Bezier curve
+  mutable Handle(BSplCLib_MultiSpanCache2D) myCurveCache; ///< Cached data for B-spline or Bezier curve
   Handle(Geom2dEvaluator_Curve) myNestedEvaluator; ///< Calculates value of offset curve
 
 
diff --git a/src/GeomAdaptor/GeomAdaptor_Curve.cxx b/src/GeomAdaptor/GeomAdaptor_Curve.cxx
index 2cb8276460..56c568cff3 100644
--- a/src/GeomAdaptor/GeomAdaptor_Curve.cxx
+++ b/src/GeomAdaptor/GeomAdaptor_Curve.cxx
@@ -24,7 +24,6 @@
 
 #include <Adaptor3d_HCurve.hxx>
 #include <BSplCLib.hxx>
-#include <BSplCLib_Cache.hxx>
 #include <Geom_BezierCurve.hxx>
 #include <Geom_BSplineCurve.hxx>
 #include <Geom_Circle.hxx>
@@ -138,7 +137,8 @@ void GeomAdaptor_Curve::load(const Handle(Geom_Curve)& C,
   myLast  = ULast;
   myCurveCache.Nullify();
 
-  if ( myCurve != C) {
+  if (myCurve != C)
+  {
     myCurve = C;
     myNestedEvaluator.Nullify();
     myBSplineCurve.Nullify();
@@ -524,36 +524,6 @@ Standard_Real GeomAdaptor_Curve::Period() const
   return myCurve->LastParameter() - myCurve->FirstParameter();
 }
 
-//=======================================================================
-//function : RebuildCache
-//purpose  : 
-//=======================================================================
-void GeomAdaptor_Curve::RebuildCache(const Standard_Real theParameter) const
-{
-  if (myTypeCurve == GeomAbs_BezierCurve)
-  {
-    // Create cache for Bezier
-    Handle(Geom_BezierCurve) aBezier = Handle(Geom_BezierCurve)::DownCast(myCurve);
-    Standard_Integer aDeg = aBezier->Degree();
-    TColStd_Array1OfReal aFlatKnots(BSplCLib::FlatBezierKnots(aDeg), 1, 2 * (aDeg + 1));
-    if (myCurveCache.IsNull())
-      myCurveCache = new BSplCLib_Cache(aDeg, aBezier->IsPeriodic(), aFlatKnots,
-        aBezier->Poles(), aBezier->Weights());
-    myCurveCache->BuildCache(theParameter, aDeg, aBezier->IsPeriodic(), aFlatKnots,
-        aBezier->Poles(), aBezier->Weights());
-}
-  else if (myTypeCurve == GeomAbs_BSplineCurve)
-{
-    // Create cache for B-spline
-    if (myCurveCache.IsNull())
-      myCurveCache = new BSplCLib_Cache(myBSplineCurve->Degree(), myBSplineCurve->IsPeriodic(),
-        myBSplineCurve->KnotSequence(), myBSplineCurve->Poles(), myBSplineCurve->Weights());
-    myCurveCache->BuildCache(theParameter, myBSplineCurve->Degree(),
-        myBSplineCurve->IsPeriodic(), myBSplineCurve->KnotSequence(),
-        myBSplineCurve->Poles(), myBSplineCurve->Weights());
-}
-}
-
 //=======================================================================
 //function : IsBoundary
 //purpose  : 
@@ -585,6 +555,34 @@ Standard_Boolean GeomAdaptor_Curve::IsBoundary(const Standard_Real theU,
   return Standard_False;
   }
 
+//=======================================================================
+//function : CreateCache
+//purpose  : 
+//=======================================================================
+
+void GeomAdaptor_Curve::CreateCache() const
+{
+  switch (myTypeCurve)
+  {
+  case GeomAbs_BezierCurve: {
+    // Create cache for Bezier
+    Handle(Geom_BezierCurve) aBezier = Handle(Geom_BezierCurve)::DownCast(myCurve);
+    myCurveCache = new BSplCLib_MultiSpanCache3D(aBezier->Degree(), aBezier->IsPeriodic(),
+        aBezier->Poles(), aBezier->Weights());
+    break;
+  }
+  case GeomAbs_BSplineCurve:
+    // Create cache for B-spline
+    myCurveCache = new BSplCLib_MultiSpanCache3D(
+        myBSplineCurve->Degree(), myBSplineCurve->IsPeriodic(),
+        &myBSplineCurve->Knots(), &myBSplineCurve->Multiplicities(),
+        &myBSplineCurve->KnotSequence(), myBSplineCurve->Poles(), myBSplineCurve->Weights());
+    break;
+  default: // avoid gcc compilation warnings
+    break;
+  }
+}
+
 //=======================================================================
 //function : Value
 //purpose  : 
@@ -611,14 +609,11 @@ void GeomAdaptor_Curve::D0(const Standard_Real U, gp_Pnt& P) const
   {
     Standard_Integer aStart = 0, aFinish = 0;
     if (IsBoundary(U, aStart, aFinish))
-    {
       myBSplineCurve->LocalD0(U, aStart, aFinish, P);
-    }
     else
   {
-      // use cached data
-      if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U))
-        RebuildCache(U);
+      if (myCurveCache.IsNull())
+        CreateCache();
       myCurveCache->D0(U, P);
   }
     break;
@@ -647,14 +642,11 @@ void GeomAdaptor_Curve::D1(const Standard_Real U, gp_Pnt& P, gp_Vec& V) const
   {
     Standard_Integer aStart = 0, aFinish = 0;
     if (IsBoundary(U, aStart, aFinish))
-    {
       myBSplineCurve->LocalD1(U, aStart, aFinish, P, V);
-    }
     else
   {
-      // use cached data
-      if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U))
-        RebuildCache(U);
+      if (myCurveCache.IsNull())
+        CreateCache();
       myCurveCache->D1(U, P, V);
   }
     break;
@@ -684,14 +676,11 @@ void GeomAdaptor_Curve::D2(const Standard_Real U,
   {
     Standard_Integer aStart = 0, aFinish = 0;
     if (IsBoundary(U, aStart, aFinish))
-    {
       myBSplineCurve->LocalD2(U, aStart, aFinish, P, V1, V2);
-    }
     else
   {
-      // use cached data
-      if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U))
-        RebuildCache(U);
+      if (myCurveCache.IsNull())
+        CreateCache();
       myCurveCache->D2(U, P, V1, V2);
   }
     break;
@@ -722,14 +711,11 @@ void GeomAdaptor_Curve::D3(const Standard_Real U,
   {
     Standard_Integer aStart = 0, aFinish = 0;
     if (IsBoundary(U, aStart, aFinish))
-    {
       myBSplineCurve->LocalD3(U, aStart, aFinish, P, V1, V2, V3);
-    }
     else
   {
-      // use cached data
-      if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U))
-        RebuildCache(U);
+      if (myCurveCache.IsNull())
+        CreateCache();
       myCurveCache->D3(U, P, V1, V2, V3);
   }
     break;
diff --git a/src/GeomAdaptor/GeomAdaptor_Curve.hxx b/src/GeomAdaptor/GeomAdaptor_Curve.hxx
index 690480906c..c736720376 100644
--- a/src/GeomAdaptor/GeomAdaptor_Curve.hxx
+++ b/src/GeomAdaptor/GeomAdaptor_Curve.hxx
@@ -23,7 +23,7 @@
 
 #include <GeomAbs_CurveType.hxx>
 #include <Standard_Real.hxx>
-#include <BSplCLib_Cache.hxx>
+#include <BSplCLib_MultiSpanCache.hxx>
 #include <Adaptor3d_Curve.hxx>
 #include <GeomAbs_Shape.hxx>
 #include <Standard_Integer.hxx>
@@ -228,9 +228,8 @@ private:
   //! Check theU relates to start or finish point of B-spline curve and return indices of span the point is located
   Standard_Boolean IsBoundary(const Standard_Real theU, Standard_Integer& theSpanStart, Standard_Integer& theSpanFinish) const;
 
-  //! Rebuilds B-spline cache
-  //! \param theParameter the value on the knot axis which identifies the caching span
-  void RebuildCache (const Standard_Real theParameter) const;
+  //! Allocates memory for a cache object
+  void CreateCache() const;
 
 
   Handle(Geom_Curve) myCurve;
@@ -239,7 +238,7 @@ private:
   Standard_Real myLast;
   
   Handle(Geom_BSplineCurve) myBSplineCurve; ///< B-spline representation to prevent castings
-  mutable Handle(BSplCLib_Cache) myCurveCache; ///< Cached data for B-spline or Bezier curve
+  mutable Handle(BSplCLib_MultiSpanCache3D) myCurveCache; ///< Cached data for B-spline or Bezier curve
   Handle(GeomEvaluator_Curve) myNestedEvaluator; ///< Calculates value of offset curve
 
 
diff --git a/src/GeomAdaptor/GeomAdaptor_Surface.cxx b/src/GeomAdaptor/GeomAdaptor_Surface.cxx
index 24e054270b..ebb65b5312 100644
--- a/src/GeomAdaptor/GeomAdaptor_Surface.cxx
+++ b/src/GeomAdaptor/GeomAdaptor_Surface.cxx
@@ -134,7 +134,8 @@ void GeomAdaptor_Surface::load(const Handle(Geom_Surface)& S,
   myVLast  = VLast;
   mySurfaceCache.Nullify();
 
-  if ( mySurface != S) {
+  if (mySurface != S)
+  {
     mySurface = S;
     myNestedEvaluator.Nullify();
     myBSplineSurface.Nullify();
@@ -179,10 +180,9 @@ void GeomAdaptor_Surface::load(const Handle(Geom_Surface)& S,
         new GeomEvaluator_SurfaceOfExtrusion (aBaseAdaptor, myExtSurf->Direction());
     }
     else if (TheType == STANDARD_TYPE(Geom_BezierSurface))
-    {
       mySurfaceType = GeomAbs_BezierSurface;
-    }
-    else if (TheType == STANDARD_TYPE(Geom_BSplineSurface)) {
+    else if (TheType == STANDARD_TYPE(Geom_BSplineSurface))
+    {
       mySurfaceType = GeomAbs_BSplineSurface;
       myBSplineSurface = Handle(Geom_BSplineSurface)::DownCast(mySurface);
     }
@@ -653,42 +653,34 @@ Standard_Real GeomAdaptor_Surface::VPeriod() const
 }
 
 //=======================================================================
-//function : RebuildCache
+//function : CreateCache
 //purpose  : 
 //=======================================================================
-void GeomAdaptor_Surface::RebuildCache(const Standard_Real theU,
-                                       const Standard_Real theV) const
+
+void GeomAdaptor_Surface::CreateCache() const
 {
-  if (mySurfaceType == GeomAbs_BezierSurface)
+  switch (mySurfaceType)
   {
+  case GeomAbs_BezierSurface: {
     // Create cache for Bezier
     Handle(Geom_BezierSurface) aBezier = Handle(Geom_BezierSurface)::DownCast(mySurface);
-    Standard_Integer aDegU = aBezier->UDegree();
-    Standard_Integer aDegV = aBezier->VDegree();
-    TColStd_Array1OfReal aFlatKnotsU(BSplCLib::FlatBezierKnots(aDegU), 1, 2 * (aDegU + 1));
-    TColStd_Array1OfReal aFlatKnotsV(BSplCLib::FlatBezierKnots(aDegV), 1, 2 * (aDegV + 1));
-    if (mySurfaceCache.IsNull())
-      mySurfaceCache = new BSplSLib_Cache(
-        aDegU, aBezier->IsUPeriodic(), aFlatKnotsU,
-        aDegV, aBezier->IsVPeriodic(), aFlatKnotsV,
+    mySurfaceCache = new BSplSLib_MultiSpanCache(
+        aBezier->UDegree(), aBezier->IsUPeriodic(),
+        aBezier->VDegree(), aBezier->IsVPeriodic(),
         aBezier->Poles(), aBezier->Weights());
-    mySurfaceCache->BuildCache(theU, theV,
-      aDegU, aBezier->IsUPeriodic(), aFlatKnotsU,
-      aDegV, aBezier->IsVPeriodic(), aFlatKnotsV,
-      aBezier->Poles(), aBezier->Weights());
+    break;
   }
-  else if (mySurfaceType == GeomAbs_BSplineSurface)
-  {
+  case GeomAbs_BSplineSurface:
     // Create cache for B-spline
-    if (mySurfaceCache.IsNull())
-      mySurfaceCache = new BSplSLib_Cache(
-        myBSplineSurface->UDegree(), myBSplineSurface->IsUPeriodic(), myBSplineSurface->UKnotSequence(),
-        myBSplineSurface->VDegree(), myBSplineSurface->IsVPeriodic(), myBSplineSurface->VKnotSequence(),
+    mySurfaceCache = new BSplSLib_MultiSpanCache(
+        myBSplineSurface->UDegree(), myBSplineSurface->IsUPeriodic(), &myBSplineSurface->UKnots(),
+        &myBSplineSurface->UMultiplicities(), &myBSplineSurface->UKnotSequence(),
+        myBSplineSurface->VDegree(), myBSplineSurface->IsVPeriodic(), &myBSplineSurface->VKnots(),
+        &myBSplineSurface->VMultiplicities(), &myBSplineSurface->VKnotSequence(),
         myBSplineSurface->Poles(), myBSplineSurface->Weights());
-    mySurfaceCache->BuildCache(theU, theV,
-      myBSplineSurface->UDegree(), myBSplineSurface->IsUPeriodic(), myBSplineSurface->UKnotSequence(),
-      myBSplineSurface->VDegree(), myBSplineSurface->IsVPeriodic(), myBSplineSurface->VKnotSequence(),
-      myBSplineSurface->Poles(), myBSplineSurface->Weights());
+    break;
+  default: // avoid gcc compilation warnings
+    break;
   }
 }
 
@@ -717,8 +709,8 @@ void GeomAdaptor_Surface::D0(const Standard_Real U,
   {
   case GeomAbs_BezierSurface:
   case GeomAbs_BSplineSurface:
-    if (mySurfaceCache.IsNull() || !mySurfaceCache->IsCacheValid(U, V))
-      RebuildCache(U, V);
+    if (mySurfaceCache.IsNull())
+      CreateCache();
     mySurfaceCache->D0(U, V, P);
     break;
 
@@ -747,25 +739,28 @@ void GeomAdaptor_Surface::D1(const Standard_Real U,
                                    gp_Vec&       D1U, 
                                    gp_Vec&       D1V ) const 
 {
-  Standard_Integer Ideb, Ifin, IVdeb, IVfin, USide=0, VSide=0;
   Standard_Real u = U, v = V;
-  if (Abs(U-myUFirst) <= myTolU) {USide= 1; u = myUFirst;}
-  else if (Abs(U-myULast) <= myTolU) {USide= -1; u = myULast;}
-  if (Abs(V-myVFirst) <= myTolV) {VSide= 1; v = myVFirst;}
-  else if (Abs(V-myVLast) <= myTolV) {VSide= -1; v = myVLast;}
 
   switch(mySurfaceType) {
   case GeomAbs_BezierSurface:
   case GeomAbs_BSplineSurface: {
-    if (!myBSplineSurface.IsNull() &&
-        (USide != 0 || VSide != 0) && 
+    Standard_Integer Ideb, Ifin, IVdeb, IVfin, USide = 0, VSide = 0;
+    if (!myBSplineSurface.IsNull())
+    {
+      if (Abs(U - myUFirst) <= myTolU) { USide = 1; u = myUFirst; }
+      else if (Abs(U - myULast) <= myTolU) { USide = -1; u = myULast; }
+      if (Abs(V - myVFirst) <= myTolV) { VSide = 1; v = myVFirst; }
+      else if (Abs(V - myVLast) <= myTolV) { VSide = -1; v = myVLast; }
+    }
+
+    if ((USide != 0 || VSide != 0) && 
         IfUVBound(u, v, Ideb, Ifin, IVdeb, IVfin, USide, VSide))
       myBSplineSurface->LocalD1(u, v, Ideb, Ifin, IVdeb, IVfin, P, D1U, D1V);
     else
     {
-      if (mySurfaceCache.IsNull() || !mySurfaceCache->IsCacheValid(U, V))
-        RebuildCache(U, V);
-      mySurfaceCache->D1(U, V, P, D1U, D1V);
+      if (mySurfaceCache.IsNull())
+        CreateCache();
+      mySurfaceCache->D1(u, v, P, D1U, D1V);
     }
     break;
     }
@@ -797,25 +792,28 @@ void GeomAdaptor_Surface::D2(const Standard_Real U,
                                    gp_Vec&       D2V, 
                                    gp_Vec&       D2UV) const 
 { 
-  Standard_Integer Ideb, Ifin, IVdeb, IVfin, USide=0, VSide=0;
   Standard_Real u = U, v = V;
-  if (Abs(U-myUFirst) <= myTolU) {USide= 1; u = myUFirst;}
-  else if (Abs(U-myULast) <= myTolU) {USide= -1; u = myULast;}
-  if (Abs(V-myVFirst) <= myTolV) {VSide= 1; v = myVFirst;}
-  else if (Abs(V-myVLast) <= myTolV) {VSide= -1; v = myVLast;}
 
   switch(mySurfaceType) {
   case GeomAbs_BezierSurface:
   case  GeomAbs_BSplineSurface: {
-    if (!myBSplineSurface.IsNull() &&
-        (USide != 0 || VSide != 0) && 
+    Standard_Integer Ideb, Ifin, IVdeb, IVfin, USide = 0, VSide = 0;
+    if (!myBSplineSurface.IsNull())
+    {
+      if (Abs(U - myUFirst) <= myTolU) { USide = 1; u = myUFirst; }
+      else if (Abs(U - myULast) <= myTolU) { USide = -1; u = myULast; }
+      if (Abs(V - myVFirst) <= myTolV) { VSide = 1; v = myVFirst; }
+      else if (Abs(V - myVLast) <= myTolV) { VSide = -1; v = myVLast; }
+    }
+
+    if ((USide != 0 || VSide != 0) && 
         IfUVBound(u, v, Ideb, Ifin, IVdeb, IVfin, USide, VSide))
       myBSplineSurface->LocalD2(u, v, Ideb, Ifin, IVdeb, IVfin, P, D1U, D1V, D2U, D2V, D2UV);
     else
     {
-      if (mySurfaceCache.IsNull() || !mySurfaceCache->IsCacheValid(U, V))
-        RebuildCache(U, V);
-      mySurfaceCache->D2(U, V, P, D1U, D1V, D2U, D2V, D2UV);
+      if (mySurfaceCache.IsNull())
+        CreateCache();
+      mySurfaceCache->D2(u, v, P, D1U, D1V, D2U, D2V, D2UV);
     }
     break;
   }
diff --git a/src/GeomAdaptor/GeomAdaptor_Surface.hxx b/src/GeomAdaptor/GeomAdaptor_Surface.hxx
index 2f0d4838c3..303581e213 100644
--- a/src/GeomAdaptor/GeomAdaptor_Surface.hxx
+++ b/src/GeomAdaptor/GeomAdaptor_Surface.hxx
@@ -23,7 +23,7 @@
 
 #include <GeomAbs_SurfaceType.hxx>
 #include <Standard_Real.hxx>
-#include <BSplSLib_Cache.hxx>
+#include <BSplSLib_MultiSpanCache.hxx>
 #include <Adaptor3d_Surface.hxx>
 #include <GeomAbs_Shape.hxx>
 #include <Standard_Integer.hxx>
@@ -256,11 +256,9 @@ private:
   Standard_EXPORT Standard_Boolean IfUVBound (const Standard_Real U, const Standard_Real V, Standard_Integer& Ideb, Standard_Integer& Ifin, Standard_Integer& IVdeb, Standard_Integer& IVfin, const Standard_Integer USide, const Standard_Integer VSide) const;
   
   Standard_EXPORT void load (const Handle(Geom_Surface)& S, const Standard_Real UFirst, const Standard_Real ULast, const Standard_Real VFirst, const Standard_Real VLast, const Standard_Real TolU = 0.0, const Standard_Real TolV = 0.0);
-  
-  //! Rebuilds B-spline cache
-  //! \param theU first parameter to identify the span for caching
-  //! \param theV second parameter to identify the span for caching
-  Standard_EXPORT void RebuildCache (const Standard_Real theU, const Standard_Real theV) const;
+
+  //! Allocates memory for a cache object
+  void CreateCache() const;
 
 
   Handle(Geom_Surface) mySurface;
@@ -272,7 +270,7 @@ private:
   Standard_Real myTolV;
   
   Handle(Geom_BSplineSurface) myBSplineSurface; ///< B-spline representation to prevent downcasts
-  mutable Handle(BSplSLib_Cache) mySurfaceCache; ///< Cached data for B-spline or Bezier surface
+  mutable Handle(BSplSLib_MultiSpanCache) mySurfaceCache; ///< Cached data for B-spline or Bezier surface
 
 protected:
   GeomAbs_SurfaceType mySurfaceType;
diff --git a/src/QABugs/QABugs_19.cxx b/src/QABugs/QABugs_19.cxx
index 36fff1fd04..95b8706a55 100644
--- a/src/QABugs/QABugs_19.cxx
+++ b/src/QABugs/QABugs_19.cxx
@@ -5106,6 +5106,66 @@ static Standard_Integer OCC27065(Draw_Interpretor& di,
   return 0;
 }
 
+//=======================================================================
+//function : OCC27074
+//purpose  : Calculate value of B-spline surface using adaptor with multi-span cache
+//=======================================================================
+static Standard_Integer OCC27074(Draw_Interpretor& theDI, Standard_Integer theArgc, const char** theArgv)
+{
+  if (theArgc < 3 && theArgc > 4)
+  {
+    std::cout << "Incorrect number of arguments. See usage:" << std::endl;
+    theDI.PrintHelp(theArgv[0]);
+    return 1;
+  }
+
+  Handle(Geom_Surface) aSurf = DrawTrSurf::GetSurface(theArgv[1]);
+  GeomAdaptor_Surface anAdaptor(aSurf);
+
+  Standard_Integer aNbValues = Draw::Atoi(theArgv[2]);
+  Standard_Boolean isUseAdaptor = theArgc > 3 && theArgv[3][0] == '-' && theArgv[3][1] == 'a';
+
+  // Number of values to be calculated along each coordinate
+  Standard_Integer aNbU = (Standard_Integer)Sqrt(aNbValues);
+  Standard_Integer aNbV = (Standard_Integer)Ceiling((Standard_Real)aNbValues / aNbU);
+
+  Standard_Real aU1, aU2, aV1, aV2;
+  aSurf->Bounds(aU1, aU2, aV1, aV2);
+
+  Standard_Real aStepU = (aU2 - aU1) / (aNbU - 1);
+  Standard_Real aStepV = (aV2 - aV1) / (aNbV - 1);
+
+  // Calculate values of surface to fill multi-span cache as much as possible.
+  // The parameters are divided to clusters by NxN points. Each time it is calculated
+  // one point from cluster and then the point from next cluster is calculated.
+  Standard_Integer aClusterSize = 100;
+  if (aSurf->DynamicType() == STANDARD_TYPE(Geom_BSplineSurface))
+  {
+    Handle(Geom_BSplineSurface) aBSpl = Handle(Geom_BSplineSurface)::DownCast(aSurf);
+    Standard_Integer aScaleU = aNbU / (aBSpl->NbUKnots() - 1);
+    Standard_Integer aScaleV = aNbV / (aBSpl->NbVKnots() - 1);
+    aClusterSize = Min(aScaleU, aScaleV);
+  }
+
+  Standard_Real aCurU, aCurV;
+  for (Standard_Integer aClusterU = 0; aClusterU < aClusterSize; ++aClusterU)
+    for (Standard_Integer anIndU = aClusterU; anIndU < aNbU; anIndU += aClusterSize)
+    {
+      aCurU = aU1 + aStepU * anIndU;
+      for (Standard_Integer aClusterV = 0; aClusterV < aClusterSize; ++aClusterV)
+        for (Standard_Integer anIndV = aClusterV; anIndV < aNbV; anIndV += aClusterSize)
+        {
+          aCurV = aV1 + aStepV * anIndV;
+          if (isUseAdaptor)
+            anAdaptor.Value(aCurU, aCurV);
+          else
+            aSurf->Value(aCurU, aCurV);
+        }
+    }
+
+  return 0;
+}
+
 //========================================================================
 //function : OCC27318
 //purpose  : Creates a box that is not listed in map of AIS objects of ViewerTest
@@ -5511,6 +5571,10 @@ void QABugs::Commands_19(Draw_Interpretor& theCommands) {
                    "OCC27065 spine profile",
                    __FILE__, OCC27065, group);
 
+  theCommands.Add ("OCC27074",
+                   "OCC27074 surf N [-a]\nCalculate value of surface N times\n(-a means calculation using adaptor)",
+                   __FILE__, OCC27074, group);
+
   theCommands.Add ("OCC27318",
                    "OCC27318: Creates a box that is not listed in map of AIS objects of ViewerTest",
                    __FILE__, OCC27318, group);
diff --git a/tests/blend/simple/Q6 b/tests/blend/simple/Q6
index 1c2a537fa4..01a4f96dbc 100644
--- a/tests/blend/simple/Q6
+++ b/tests/blend/simple/Q6
@@ -3,4 +3,4 @@ tscale s 0 0 0 SCALE
 explode s E
 blend result s SCALE*2 s_5
  
-checkprops result -s 1.65391e+008
+checkprops result -s 1.65391e+08 -eps 1.e-4
diff --git a/tests/bugs/modalg_6/bug25613_1 b/tests/bugs/modalg_6/bug25613_1
index 1f9b9cd322..3f307fe795 100644
--- a/tests/bugs/modalg_6/bug25613_1
+++ b/tests/bugs/modalg_6/bug25613_1
@@ -22,7 +22,7 @@ if { [ expr ($dist - $dist_good) ] < -$Tol } {
   puts "Error in xdistef command (cannot find maximal distance)"
 }
 
-if { $dist > $dist_good } {
+if { $dist > $dist_good + $Tol } {
 #Check if distance found is correct
 
   mkcurve c3d e
diff --git a/tests/bugs/modalg_6/bug25613_2 b/tests/bugs/modalg_6/bug25613_2
index 10dcdf3e47..f0b3ee0d47 100644
--- a/tests/bugs/modalg_6/bug25613_2
+++ b/tests/bugs/modalg_6/bug25613_2
@@ -22,7 +22,7 @@ if { [ expr ($dist - $dist_good) ] < -$Tol } {
   puts "Error in xdistef command (cannot find maximal distance)"
 }
 
-if { $dist > $dist_good } {
+if { $dist > $dist_good + $Tol } {
 #Check if distance found is correct
 
   mkcurve c3d e
diff --git a/tests/de/step_2/R2 b/tests/de/step_2/R2
index cc18f1564c..dbf4b23312 100644
--- a/tests/de/step_2/R2
+++ b/tests/de/step_2/R2
@@ -7,8 +7,8 @@ set ref_data {
 DATA        : Faulties = 0  ( 0 )  Warnings = 0  ( 0 )  Summary  = 0  ( 0 )
 TPSTAT      : Faulties = 0  ( 0 )  Warnings = 31  ( 28 )  Summary  = 31  ( 28 )
 CHECKSHAPE  : Wires    = 0  ( 0 )  Faces    = 0  ( 0 )  Shells   = 0  ( 0 )   Solids   = 0 ( 0 )
-NBSHAPES    : Solid    = 0  ( 0 )  Shell    = 1  ( 1 )  Face     = 357  ( 357 )   Summary  = 2004  ( 2003 )
-STATSHAPE   : Solid    = 0  ( 0 )  Shell    = 1  ( 1 )  Face     = 357  ( 357 )   FreeWire = 0  ( 0 )   FreeEdge  = 0 ( 0 )   SharedEdge = 840  ( 839 )
+NBSHAPES    : Solid    = 0  ( 0 )  Shell    = 1  ( 1 )  Face     = 357  ( 357 )   Summary  = 2005  ( 2003 )
+STATSHAPE   : Solid    = 0  ( 0 )  Shell    = 1  ( 1 )  Face     = 357  ( 357 )   FreeWire = 0  ( 0 )   FreeEdge  = 0 ( 0 )   SharedEdge = 841  ( 839 )
 TOLERANCE   : MaxTol   = 0.007356791914  ( 0.009485808595 )  AvgTol   =  0.0003528568313  (  0.001207996284 )
 LABELS      : N0Labels = 1  ( 1 )  N1Labels = 0  ( 0 )  N2Labels = 0  ( 0 )   TotalLabels = 1  ( 1 )   NameLabels = 1  ( 1 )   ColorLabels = 1  ( 1 )   LayerLabels = 0  ( 0 )
 PROPS       : Centroid = 0  ( 0 )  Volume   = 0  ( 0 )  Area     = 0  ( 0 )
diff --git a/tests/de/step_2/T1 b/tests/de/step_2/T1
index 7cab7c8f0e..3b32cdc71c 100644
--- a/tests/de/step_2/T1
+++ b/tests/de/step_2/T1
@@ -7,8 +7,8 @@ set ref_data {
 DATA        : Faulties = 0  ( 0 )  Warnings = 0  ( 0 )  Summary  = 0  ( 0 )
 TPSTAT      : Faulties = 0  ( 0 )  Warnings = 31  ( 28 )  Summary  = 31  ( 28 )
 CHECKSHAPE  : Wires    = 0  ( 0 )  Faces    = 0  ( 0 )  Shells   = 0  ( 0 )   Solids   = 0 ( 0 )
-NBSHAPES    : Solid    = 1  ( 1 )  Shell    = 1  ( 1 )  Face     = 357  ( 357 )   Summary  = 2005  ( 2004 )
-STATSHAPE   : Solid    = 1  ( 1 )  Shell    = 1  ( 1 )  Face     = 357  ( 357 )   FreeWire = 0  ( 0 )   FreeEdge  = 0 ( 0 )   SharedEdge = 840  ( 839 )
+NBSHAPES    : Solid    = 1  ( 1 )  Shell    = 1  ( 1 )  Face     = 357  ( 357 )   Summary  = 2006  ( 2004 )
+STATSHAPE   : Solid    = 1  ( 1 )  Shell    = 1  ( 1 )  Face     = 357  ( 357 )   FreeWire = 0  ( 0 )   FreeEdge  = 0 ( 0 )   SharedEdge = 841  ( 839 )
 TOLERANCE   : MaxTol   = 0.007356791914  ( 0.009485808595 )  AvgTol   =  0.0003528126958  (  0.001207999522 )
 LABELS      : N0Labels = 1  ( 1 )  N1Labels = 0  ( 0 )  N2Labels = 0  ( 0 )   TotalLabels = 1  ( 1 )   NameLabels = 1  ( 1 )   ColorLabels = 0  ( 0 )   LayerLabels = 0  ( 0 )
 PROPS       : Centroid = 0  ( 0 )  Volume   = 0  ( 0 )  Area     = 0  ( 0 )
diff --git a/tests/perf/bspline/hugenurbs b/tests/perf/bspline/hugenurbs
new file mode 100644
index 0000000000..9d76b4f339
--- /dev/null
+++ b/tests/perf/bspline/hugenurbs
@@ -0,0 +1,39 @@
+puts "========"
+puts "CR27074"
+puts "========"
+puts ""
+#######################################################################
+# Compare allocated memory for evaluation of huge NURBS surface
+#######################################################################
+
+pload QAcommands
+
+cpulimit 1000
+
+# create simple B-spline
+bsplinesurf s \
+1 2  0 2  1 2 \
+1 2  0 2  1 2 \
+0 0 0 1  1 0 1 1 \
+0 1 0 1  1 1 1 1
+
+# increase number of spans
+set nbspans 1000
+for {set i 1} {$i < $nbspans} {incr i} {
+  set knot [expr double($i) / $nbspans]
+  insertuknot s $knot 1
+  insertvknot s $knot 1
+}
+
+# increase degree
+set deg 5
+incudeg s $deg
+incvdeg s $deg
+
+# calculate values of B-spline
+set nbiter 1000000
+dchrono t reset
+dchrono t start
+OCC27074 s $nbiter -a
+dchrono t stop
+dchrono t show
diff --git a/tests/perf/bspline/multispan b/tests/perf/bspline/multispan
new file mode 100644
index 0000000000..f5f7ca1ad0
--- /dev/null
+++ b/tests/perf/bspline/multispan
@@ -0,0 +1,64 @@
+puts "========"
+puts "CR27074"
+puts "========"
+puts ""
+#######################################################################
+# Compare performance of calculation value of NURBS surface with multi-span cache
+#######################################################################
+
+pload QAcommands
+
+cpulimit 1000
+
+bsplinesurf nsurf \
+5 5 0 6 1 1 4 1 5 1 8 6 \
+5 5 0 6 2 1 3 1 6 1 7 6 \
+0  0  0  1   2  0  0  1   5  0 -1  1   10  0  5  1   12  0  1  1   15  0 -3  1   16  0 -3  1   19  0 -4 1   24  0  0  1 \
+0 10  2  1   3 10  0  1   8 10  5  1   10 10  3  1   12 10  2  1   15 10  0  1   20 10  5  1   21 10  3 1   24 10  0  1 \
+0 20 10  1   4 20  4  1   7 20  4  1   10 20 20  1   12 20 10  1   16 20  4  1   19 20  4  1   20 20 10 1   24 20  0  1 \
+0 30  0  1   2 30  0  1   8 30  0  1   10 30  0  1   12 30  0  1   14 30  0  1   20 30  0  1   22 30  0 1   24 30  0  1 \
+0 40 -1  1   4 40  5  1   9 40  1  1   10 40  5  1   12 40 -1  1   16 40  5  1   21 40  1  1   22 40  5 1   24 40  0  1 \
+0 50  5  1   4 50 10  1   6 50 10  1   10 50  5  1   12 50  5  1   16 50 10  1   18 50 10  1   20 50  5 1   24 50  0  1 \
+0 60  4  1   3 60 -3  1   7 60 -4  1   10 60  4  1   12 60  4  1   15 60 -3  1   19 60 -4  1   20 60  4 1   24 60  0  1 \
+0 70 -5  1   3 70  0  1   5 70  0  1   10 70 -3  1   12 70 -5  1   15 70  0  1   17 70  0  1   20 70 -3 1   24 70  0  1 \
+0 80  7  1   3 80  1  1   7 80  3  1   10 80  0  1   12 80  7  1   15 80  1  1   19 80  3  1   21 80  0 1   24 80  0  1
+
+bsplinesurf rsurf \
+5 5 0 6 1 1 4 1 5 1 8 6 \
+5 5 0 6 2 1 3 1 6 1 7 6 \
+0  0  0  1   2  0  0  1   5  0 -1  1   10  0  5  2   12  0  1  1   15  0 -3  2   16  0 -3  1   19  0 -4 1   24  0  0  1 \
+0 10  2  3   3 10  0  1   8 10  5  3   10 10  3  1   12 10  2  3   15 10  0  1   20 10  5  3   21 10  3 1   24 10  0  2 \
+0 20 10  1   4 20  4  3   7 20  4  2   10 20 20  4   12 20 10  1   16 20  4  3   19 20  4  2   20 20 10 4   24 20  0  1 \
+0 30  0  2   2 30  0  1   8 30  0  1   10 30  0  2   12 30  0  2   14 30  0  1   20 30  0  1   22 30  0 2   24 30  0  3 \
+0 40 -1  1   4 40  5  2   9 40  1  3   10 40  5  2   12 40 -1  1   16 40  5  2   21 40  1  3   22 40  5 2   24 40  0  1 \
+0 50  5  1   4 50 10  5   6 50 10  5   10 50  5  1   12 50  5  1   16 50 10  5   18 50 10  5   20 50  5 1   24 50  0  2 \
+0 60  4  3   3 60 -3  1   7 60 -4  1   10 60  4  1   12 60  4  3   15 60 -3  1   19 60 -4  1   20 60  4 1   24 60  0  1 \
+0 70 -5  5   3 70  0  1   5 70  0  1   10 70 -3  2   12 70 -5  5   15 70  0  1   17 70  0  1   20 70 -3 2   24 70  0  3 \
+0 80  7  1   3 80  1  2   7 80  3  1   10 80  0  1   12 80  7  1   15 80  1  2   19 80  3  1   21 80  0 1   24 80  0  1
+
+
+set surfaces [list nsurf rsurf]
+# number of values to be calculated
+set nbiter 4000000
+
+foreach s $surfaces {
+  # calculate values without adaptor
+  dchrono t reset
+  dchrono t start
+  OCC27074 $s $nbiter
+  dchrono t stop
+  set elapsed [dchrono t show]
+  regexp {CPU user time: ([-0-9.+eE]+) seconds} ${elapsed} full cpu_noadaptor
+
+  # calculate values using adaptor
+  dchrono t reset
+  dchrono t start
+  OCC27074 $s $nbiter -a
+  dchrono t stop
+  set elapsed [dchrono t show]
+  regexp {CPU user time: ([-0-9.+eE]+) seconds} ${elapsed} full cpu_adaptor
+
+  if {$cpu_adaptor >= $cpu_noadaptor} {
+    puts "ERROR: Multi-span cached adaptor is slower than surface"
+  }
+}
-- 
2.22.0.windows.1

