diff --git a/Filters/Extraction/vtkExtractCells.cxx b/Filters/Extraction/vtkExtractCells.cxx
index 71e08a5bf546c69a199559a9482dee81d0492e4e..384ba912d306cb0c86455e85693932a276b37174 100644
--- a/Filters/Extraction/vtkExtractCells.cxx
+++ b/Filters/Extraction/vtkExtractCells.cxx
@@ -34,8 +34,6 @@
 #include "vtkUnsignedCharArray.h"
 #include "vtkUnstructuredGrid.h"
 
-vtkStandardNewMacro(vtkExtractCells);
-
 #include <algorithm>
 #include <numeric>
 #include <vector>
@@ -152,39 +150,36 @@ private:
 
 class vtkExtractCellsSTLCloak
 {
+  vtkTimeStamp SortTime;
+
 public:
   std::vector<vtkIdType> CellIds;
-  vtkTimeStamp ModifiedTime;
-  vtkTimeStamp SortTime;
+  std::pair<typename std::vector<vtkIdType>::const_iterator,
+    typename std::vector<vtkIdType>::const_iterator>
+    CellIdsRange;
   FastPointMap PointMap;
 
-  void Modified() { this->ModifiedTime.Modified(); }
-
-  inline bool IsPrepared() const
+  vtkIdType Prepare(vtkIdType numInputCells, vtkExtractCells* self)
   {
-    return this->ModifiedTime.GetMTime() < this->SortTime.GetMTime();
-  }
+    assert(numInputCells > 0);
 
-  void Prepare(vtkIdType numInputCells)
-  {
-    if (!this->IsPrepared())
+    if (self->GetAssumeSortedAndUniqueIds() == false && (self->GetMTime() > this->SortTime))
     {
       vtkSMPTools::Sort(this->CellIds.begin(), this->CellIds.end());
       auto last = std::unique(this->CellIds.begin(), this->CellIds.end());
-      auto first = this->CellIds.begin();
-
-      // These lines clamp the ids to the number of cells in the
-      // dataset. Otherwise segfaults occur when cellIds are greater than the
-      // number of input cells, in particular when cellId==numInputCells.
-      auto clampLast = std::find(first, last, numInputCells);
-      last = (clampLast != last ? clampLast : last);
-
-      this->CellIds.resize(std::distance(first, last));
+      this->CellIds.erase(last, this->CellIds.end());
       this->SortTime.Modified();
     }
+
+    this->CellIdsRange =
+      std::make_pair(std::lower_bound(this->CellIds.begin(), this->CellIds.end(), 0),
+        std::upper_bound(this->CellIds.begin(), this->CellIds.end(), (numInputCells - 1)));
+    return static_cast<vtkIdType>(
+      std::distance(this->CellIdsRange.first, this->CellIdsRange.second));
   }
 };
 
+vtkStandardNewMacro(vtkExtractCells);
 //----------------------------------------------------------------------------
 vtkExtractCells::vtkExtractCells()
 {
@@ -218,19 +213,10 @@ void vtkExtractCells::AddCellList(vtkIdList* l)
     return;
   }
 
+  auto& cellIds = this->CellList->CellIds;
   const vtkIdType* inputBegin = l->GetPointer(0);
   const vtkIdType* inputEnd = inputBegin + inputSize;
-
-  const std::size_t oldSize = this->CellList->CellIds.size();
-  const std::size_t newSize = oldSize + static_cast<std::size_t>(inputSize);
-  this->CellList->CellIds.resize(newSize);
-
-  auto outputBegin = this->CellList->CellIds.begin();
-  std::advance(outputBegin, oldSize);
-
-  std::copy(inputBegin, inputEnd, outputBegin);
-
-  this->CellList->Modified();
+  std::copy(inputBegin, inputEnd, std::back_inserter(cellIds));
   this->Modified();
 }
 
@@ -250,13 +236,7 @@ void vtkExtractCells::SetCellIds(const vtkIdType* ptr, vtkIdType numValues)
 void vtkExtractCells::AddCellIds(const vtkIdType* ptr, vtkIdType numValues)
 {
   auto& cellIds = this->CellList->CellIds;
-  const auto oldsize = cellIds.size();
-  cellIds.resize(oldsize + numValues);
-
-  auto start = cellIds.begin();
-  std::advance(start, oldsize);
-  std::copy(ptr, ptr + numValues, start);
-  this->CellList->Modified();
+  std::copy(ptr, ptr + numValues, std::back_inserter(cellIds));
   this->Modified();
 }
 
@@ -270,19 +250,11 @@ void vtkExtractCells::AddCellRange(vtkIdType from, vtkIdType to)
   }
 
   // This range specification is inconsistent with C++. Left for backward
-  // compatibility reasons.
-  const vtkIdType inputSize = to - from + 1; // +1 to include 'to'
-  const std::size_t oldSize = this->CellList->CellIds.size();
-  const std::size_t newSize = oldSize + static_cast<std::size_t>(inputSize);
+  // compatibility reasons.  Add 1 to `to` to make it consistent.
+  ++to;
 
-  this->CellList->CellIds.resize(newSize);
-
-  auto outputBegin = this->CellList->CellIds.begin() + oldSize;
-  auto outputEnd = this->CellList->CellIds.begin() + newSize;
-
-  std::iota(outputBegin, outputEnd, from);
-
-  this->CellList->Modified();
+  auto& cellIds = this->CellList->CellIds;
+  std::generate_n(std::back_inserter(cellIds), (to - from), [&from]() { return from++; });
   this->Modified();
 }
 
@@ -308,11 +280,8 @@ int vtkExtractCells::RequestData(vtkInformation* vtkNotUsed(request),
     return 1;
   }
 
-  // Sort/uniquify the cell ids if needed.
-  vtkIdType numCellsInput = input->GetNumberOfCells();
-  this->CellList->Prepare(numCellsInput);
-
-  vtkIdType numCells = static_cast<vtkIdType>(this->CellList->CellIds.size());
+  const vtkIdType numCellsInput = input->GetNumberOfCells();
+  const vtkIdType numCells = this->CellList->Prepare(numCellsInput, this);
   if (numCells == numCellsInput)
   {
     this->Copy(input, output);
@@ -435,9 +404,10 @@ vtkIdType vtkExtractCells::ReMapPointIds(vtkDataSet* grid)
   if (!this->InputIsUgrid)
   {
     vtkNew<vtkIdList> ptIds;
-
-    for (vtkIdType cellId : this->CellList->CellIds)
+    const auto& range = this->CellList->CellIdsRange;
+    for (auto iter = range.first; iter != range.second; ++iter)
     {
+      const auto cellId = (*iter);
       grid->GetCellPoints(cellId, ptIds);
 
       vtkIdType npts = ptIds->GetNumberOfIds();
@@ -462,8 +432,10 @@ vtkIdType vtkExtractCells::ReMapPointIds(vtkDataSet* grid)
     this->SubSetUGridCellArraySize = 0;
     this->SubSetUGridFacesArraySize = 0;
 
-    for (vtkIdType cellId : this->CellList->CellIds)
+    const auto& range = this->CellList->CellIdsRange;
+    for (auto iter = range.first; iter != range.second; ++iter)
     {
+      const auto cellId = (*iter);
       if (cellId > maxid)
       {
         continue;
@@ -516,7 +488,8 @@ vtkIdType vtkExtractCells::ReMapPointIds(vtkDataSet* grid)
 //----------------------------------------------------------------------------
 void vtkExtractCells::CopyCellsDataSet(vtkDataSet* input, vtkUnstructuredGrid* output)
 {
-  output->Allocate(static_cast<vtkIdType>(this->CellList->CellIds.size()));
+  const auto& range = this->CellList->CellIdsRange;
+  output->Allocate(static_cast<vtkIdType>(std::distance(range.first, range.second)));
 
   vtkCellData* oldCD = input->GetCellData();
   vtkCellData* newCD = output->GetCellData();
@@ -536,8 +509,9 @@ void vtkExtractCells::CopyCellsDataSet(vtkDataSet* input, vtkUnstructuredGrid* o
 
   vtkNew<vtkIdList> cellPoints;
 
-  for (vtkIdType cellId : this->CellList->CellIds)
+  for (auto iter = range.first; iter != range.second; ++iter)
   {
+    const auto cellId = (*iter);
     input->GetCellPoints(cellId, cellPoints);
 
     for (vtkIdType i = 0; i < cellPoints->GetNumberOfIds(); i++)
@@ -585,7 +559,8 @@ void vtkExtractCells::CopyCellsUnstructuredGrid(vtkDataSet* input, vtkUnstructur
     origMap->Delete();
   }
 
-  vtkIdType numCells = static_cast<vtkIdType>(this->CellList->CellIds.size());
+  const auto& range = this->CellList->CellIdsRange;
+  const auto numCells = static_cast<vtkIdType>(std::distance(range.first, range.second));
 
   vtkNew<vtkCellArray> cellArray; // output
   vtkNew<vtkIdTypeArray> newcells;
@@ -605,8 +580,9 @@ void vtkExtractCells::CopyCellsUnstructuredGrid(vtkDataSet* input, vtkUnstructur
   vtkIdType maxid = ugrid->GetNumberOfCells();
   bool havePolyhedron = false;
 
-  for (vtkIdType oldCellId : this->CellList->CellIds)
+  for (auto iter = range.first; iter != range.second; ++iter)
   {
+    const auto oldCellId = (*iter);
     if (oldCellId >= maxid)
     {
       continue;
@@ -690,4 +666,5 @@ void vtkExtractCells::PrintSelf(ostream& os, vtkIndent indent)
 {
   this->Superclass::PrintSelf(os, indent);
   os << indent << "ExtractAllCells: " << this->ExtractAllCells << endl;
+  os << indent << "AssumeSortedAndUniqueIds: " << this->AssumeSortedAndUniqueIds << endl;
 }
diff --git a/Filters/Extraction/vtkExtractCells.h b/Filters/Extraction/vtkExtractCells.h
index c03871c8dd7d3f1bd8be6de3c208430e708a8618..f10d4dfdbb3c591ee57c572a97dfaefeb131102f 100644
--- a/Filters/Extraction/vtkExtractCells.h
+++ b/Filters/Extraction/vtkExtractCells.h
@@ -81,11 +81,23 @@ public:
    * If all cells are being extracted, this filter can use fast path to speed up
    * the extraction. In that case, one can set this flag to true. When set to
    * true, cell ids added via the various methods are simply ignored.
+   * Defaults to false.
    */
   vtkSetMacro(ExtractAllCells, bool);
   vtkGetMacro(ExtractAllCells, bool);
   vtkBooleanMacro(ExtractAllCells, bool);
   //@}
+
+  //@{
+  /**
+   * If the cell ids specified are already sorted and unique, then set this to
+   * true to avoid the filter from doing time-consuming sorts and uniquification
+   * operations. Defaults to false.
+   */
+  vtkSetMacro(AssumeSortedAndUniqueIds, bool);
+  vtkGetMacro(AssumeSortedAndUniqueIds, bool);
+  vtkBooleanMacro(AssumeSortedAndUniqueIds, bool);
+  //@}
 protected:
   int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;
   int FillInputPortInformation(int port, vtkInformation* info) override;
@@ -104,6 +116,7 @@ protected:
   vtkIdType SubSetUGridFacesArraySize = 0;
   bool InputIsUgrid = false;
   bool ExtractAllCells = false;
+  bool AssumeSortedAndUniqueIds = false;
 
 private:
   vtkExtractCells(const vtkExtractCells&) = delete;
diff --git a/Filters/Extraction/vtkExtractSelection.cxx b/Filters/Extraction/vtkExtractSelection.cxx
index 5469f94b13b80b2e6e87213bfb654fa1f89e0c0d..a0ef0dd1b10c7dbf4cca676ed681a09b3f675a35 100644
--- a/Filters/Extraction/vtkExtractSelection.cxx
+++ b/Filters/Extraction/vtkExtractSelection.cxx
@@ -510,6 +510,7 @@ void vtkExtractSelection::ExtractSelectedCells(
         ids.push_back(cc);
       }
     }
+    extractor->SetAssumeSortedAndUniqueIds(true);
     extractor->SetCellIds(&ids.front(), static_cast<vtkIdType>(ids.size()));
   }