diff --git a/Framework/DataHandling/inc/MantidDataHandling/GroupDetectors2.h b/Framework/DataHandling/inc/MantidDataHandling/GroupDetectors2.h
index 40a3464743137b473018fbe01fda0fbb3d9114ec..9f5dcbb1e915bd6c2daf79434391ebc78f8e5854 100644
--- a/Framework/DataHandling/inc/MantidDataHandling/GroupDetectors2.h
+++ b/Framework/DataHandling/inc/MantidDataHandling/GroupDetectors2.h
@@ -204,10 +204,6 @@ private:
                          DataObjects::EventWorkspace_sptr outputWS,
                          const double prog4Copy);
 
-  /// Returns true if detectors exists and is masked
-  bool isMaskedDetector(const API::SpectrumInfo &detector,
-                        const size_t index) const;
-
   /// Copy the ungrouped spectra from the input workspace to the output
   template <class TIn, class TOut>
   void moveOthers(const std::set<int64_t> &unGroupedSet, const TIn &inputWS,
diff --git a/Framework/DataHandling/src/GroupDetectors2.cpp b/Framework/DataHandling/src/GroupDetectors2.cpp
index d1ad725e416fe4074ce99053b0a5f4a6592d29c6..915af2ebb564e56c9184b87a0e7239cde74c1a2e 100644
--- a/Framework/DataHandling/src/GroupDetectors2.cpp
+++ b/Framework/DataHandling/src/GroupDetectors2.cpp
@@ -40,6 +40,7 @@ using namespace DataObjects;
 using std::size_t;
 
 namespace { // anonymous namespace
+enum class Behaviour { SUM, AVERAGE };
 
 /**
  * Translate the PerformIndexOperations processing instructions from a vector
@@ -958,70 +959,83 @@ size_t GroupDetectors2::formGroups(API::MatrixWorkspace_const_sptr inputWS,
                                    const double prog4Copy, const bool keepAll,
                                    const std::set<int64_t> &unGroupedSet,
                                    Indexing::IndexInfo &indexInfo) {
-  // get "Behaviour" string
-  const std::string behaviour = getProperty("Behaviour");
-  int bhv = 0;
-  if (behaviour == "Average")
-    bhv = 1;
-
-  API::MatrixWorkspace_sptr beh = API::WorkspaceFactory::Instance().create(
-      "Workspace2D", static_cast<int>(m_GroupWsInds.size()), 1, 1);
-
-  g_log.debug() << name() << ": Preparing to group spectra into "
-                << m_GroupWsInds.size() << " groups\n";
-
-  // where we are copying spectra to, we start copying to the start of the
-  // output workspace
+  const std::string behaviourChoice = getProperty("Behaviour");
+  const auto behaviour =
+      behaviourChoice == "Sum" ? Behaviour::SUM : Behaviour::AVERAGE;
   size_t outIndex = 0;
-  // Only used for averaging behaviour. We may have a 1:1 map where a Divide
-  // would be waste as it would be just dividing by 1
-  bool requireDivide(false);
   const auto &spectrumInfo = inputWS->spectrumInfo();
-
+  const auto nFinalHistograms =
+      m_GroupWsInds.size() + (keepAll ? unGroupedSet.size() : 0);
   auto spectrumGroups = std::vector<std::vector<size_t>>();
+  spectrumGroups.reserve(nFinalHistograms);
   auto spectrumNumbers = std::vector<Indexing::SpectrumNumber>();
+  spectrumNumbers.reserve(nFinalHistograms);
 
-  for (storage_map::const_iterator it = m_GroupWsInds.begin();
-       it != m_GroupWsInds.end(); ++it) {
+  for (const auto &group : m_GroupWsInds) {
     // This is the grouped spectrum
     auto &outSpec = outputWS->getSpectrum(outIndex);
 
     // The spectrum number of the group is the key
-    spectrumNumbers.push_back(it->first);
+    spectrumNumbers.emplace_back(group.first);
     // Start fresh with no detector IDs
     outSpec.clearDetectorIDs();
 
     // Copy over X data from first spectrum, the bin boundaries for all spectra
     // are assumed to be the same here
     outSpec.setSharedX(inputWS->sharedX(0));
-    auto outputHistogram = outSpec.histogram();
 
     // Keep track of number of detectors required for masking
-    size_t nonMaskedSpectra(0);
-    auto spectrumGroup = std::vector<size_t>();
-
-    for (auto originalWI : it->second) {
-      // detectors to add to firstSpecNum
-      const auto &inputSpectrum = inputWS->getSpectrum(originalWI);
-
-      outputHistogram += inputSpectrum.histogram();
-      outSpec.addDetectorIDs(inputSpectrum.getDetectorIDs());
-
-      if (!isMaskedDetector(spectrumInfo, originalWI))
-        ++nonMaskedSpectra;
-
-      spectrumGroup.push_back(originalWI);
+    std::vector<size_t> spectrumGroup;
+    spectrumGroup.reserve(group.second.size());
+
+    auto &Ys = outSpec.mutableY();
+    auto &Es = outSpec.mutableE();
+    std::vector<double> sum(Ys.size(), 0.);
+    std::vector<double> errorSum(Ys.size(), 0.);
+    std::vector<int> count(Ys.size(), 0);
+    for (auto originalWI : group.second) {
+      const auto &inSpec = inputWS->getSpectrum(originalWI);
+      outSpec.addDetectorIDs(inSpec.getDetectorIDs());
+      spectrumGroup.emplace_back(originalWI);
+      if (spectrumInfo.hasDetectors(originalWI) &&
+          spectrumInfo.isMasked(originalWI)) {
+        continue;
+      }
+      const auto &Ys = inputWS->y(originalWI);
+      const auto &Es = inputWS->e(originalWI);
+      if (inputWS->hasMaskedBins(originalWI)) {
+        const auto &maskedBins = inputWS->maskedBins(originalWI);
+        for (size_t binIndex = 0; binIndex < Ys.size(); ++binIndex) {
+          if (maskedBins.count(binIndex) == 0) {
+            sum[binIndex] += Ys[binIndex];
+            errorSum[binIndex] += Es[binIndex] * Es[binIndex];
+            count[binIndex] += 1;
+          }
+        }
+      } else {
+        for (size_t binIndex = 0; binIndex < Ys.size(); ++binIndex) {
+          sum[binIndex] += Ys[binIndex];
+          errorSum[binIndex] += Es[binIndex] * Es[binIndex];
+          count[binIndex] += 1;
+        }
+      }
+    }
+    spectrumGroups.emplace_back(std::move(spectrumGroup));
+    for (size_t binIndex = 0; binIndex < sum.size(); ++binIndex) {
+      errorSum[binIndex] = std::sqrt(errorSum[binIndex]);
+      if (behaviour == Behaviour::AVERAGE) {
+        const auto n = static_cast<double>(count[binIndex]);
+        if (n != 0) {
+          sum[binIndex] /= n;
+          errorSum[binIndex] /= n;
+        } else {
+          sum[binIndex] = 0;
+          errorSum[binIndex] = 0;
+        }
+      }
+      Ys[binIndex] = sum[binIndex];
+      Es[binIndex] = errorSum[binIndex];
     }
-
-    spectrumGroups.push_back(spectrumGroup);
-
-    outSpec.setHistogram(outputHistogram);
-
-    if (nonMaskedSpectra == 0)
-      ++nonMaskedSpectra; // Avoid possible divide by zero
-    if (!requireDivide)
-      requireDivide = (nonMaskedSpectra > 1);
-    beh->mutableY(outIndex)[0] = static_cast<double>(nonMaskedSpectra);
 
     // make regular progress reports and check for cancelling the algorithm
     if (outIndex % INTERVAL == 0) {
@@ -1042,28 +1056,15 @@ size_t GroupDetectors2::formGroups(API::MatrixWorkspace_const_sptr inputWS,
       if (originalWI < 0)
         continue;
 
-      spectrumGroups.push_back(std::vector<size_t>(1, originalWI));
+      spectrumGroups.emplace_back(std::vector<size_t>(1, originalWI));
 
       auto spectrumNumber = inputWS->getSpectrum(originalWI).getSpectrumNo();
-      spectrumNumbers.push_back(spectrumNumber);
+      spectrumNumbers.emplace_back(spectrumNumber);
     }
   }
 
   indexInfo = Indexing::group(inputWS->indexInfo(), std::move(spectrumNumbers),
                               spectrumGroups);
-
-  if (bhv == 1 && requireDivide) {
-    g_log.debug() << "Running Divide algorithm to perform averaging.\n";
-    Mantid::API::IAlgorithm_sptr divide = createChildAlgorithm("Divide");
-    divide->initialize();
-    divide->setProperty<API::MatrixWorkspace_sptr>("LHSWorkspace", outputWS);
-    divide->setProperty<API::MatrixWorkspace_sptr>("RHSWorkspace", beh);
-    divide->setProperty<API::MatrixWorkspace_sptr>("OutputWorkspace", outputWS);
-    divide->execute();
-  }
-
-  g_log.debug() << name() << " created " << outIndex
-                << " new grouped spectra\n";
   return outIndex;
 }
 
@@ -1126,7 +1127,8 @@ GroupDetectors2::formGroupsEvent(DataObjects::EventWorkspace_const_sptr inputWS,
 
       // detectors to add to the output spectrum
       outEL.addDetectorIDs(fromEL.getDetectorIDs());
-      if (!isMaskedDetector(spectrumInfo, originalWI)) {
+      if (!spectrumInfo.hasDetectors(originalWI) ||
+          !spectrumInfo.isMasked(originalWI)) {
         ++nonMaskedSpectra;
       }
     }
@@ -1162,16 +1164,6 @@ GroupDetectors2::formGroupsEvent(DataObjects::EventWorkspace_const_sptr inputWS,
   return outIndex;
 }
 
-bool GroupDetectors2::isMaskedDetector(const API::SpectrumInfo &spectrum,
-                                       const size_t index) const {
-  if (spectrum.hasDetectors(index)) {
-    return spectrum.isMasked(index);
-  } else {
-    // Can't be masked if it doesn't exist
-    return false;
-  }
-}
-
 // RangeHelper
 /** Expands any ranges in the input string of non-negative integers, eg. "1 3-5
  * 4" -> "1 3 4 5 4"
diff --git a/Framework/DataHandling/test/GroupDetectors2Test.h b/Framework/DataHandling/test/GroupDetectors2Test.h
index fb8c68270cd44a9d6b1b089772ecb8e8242fa54a..2a12cec0ec85c3f16499b2e756d6db75791ec163 100644
--- a/Framework/DataHandling/test/GroupDetectors2Test.h
+++ b/Framework/DataHandling/test/GroupDetectors2Test.h
@@ -608,6 +608,67 @@ public:
     TS_ASSERT_EQUALS(output->y(0)[1], 1.5);
   }
 
+  void testAverageBehaviourWithMaskedBins() {
+    createTestWorkspace(inputWSName, 0);
+    MatrixWorkspace_sptr input =
+        AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
+            inputWSName);
+    input->flagMasked(0, 0);
+    GroupDetectors2 gd2;
+    gd2.initialize();
+    gd2.setChild(true);
+    gd2.setRethrows(true);
+    gd2.setPropertyValue("InputWorkspace", inputWSName);
+    gd2.setPropertyValue("OutputWorkspace", "_unused_for_child");
+    gd2.setPropertyValue("WorkspaceIndexList", "0,1");
+    gd2.setPropertyValue("Behaviour", "Average");
+    TS_ASSERT_THROWS_NOTHING(gd2.execute());
+    TS_ASSERT(gd2.isExecuted())
+    MatrixWorkspace_sptr output = gd2.getProperty("OutputWorkspace");
+    TS_ASSERT_EQUALS(output->getNumberHistograms(), 1)
+    const auto &spectrum = output->getSpectrum(0);
+    const auto &detIds = spectrum.getDetectorIDs();
+    TS_ASSERT_EQUALS(detIds.size(), 2)
+    TS_ASSERT_DIFFERS(detIds.find(0), detIds.end())
+    TS_ASSERT_DIFFERS(detIds.find(1), detIds.end())
+    const auto &y = output->y(0);
+    const auto &e = output->e(0);
+    for (size_t i = 0; i < y.size(); ++i) {
+      const double expectedSignal = i == 0 ? 2. : (1. + 2.) / 2.;
+      TS_ASSERT_EQUALS(y[i], expectedSignal)
+      const double expectedError = i == 0 ? 1. : std::sqrt(2.) / 2.;
+      TS_ASSERT_EQUALS(e[i], expectedError)
+    }
+  }
+
+  void testSumBehaviourWithMaskedBins() {
+    createTestWorkspace(inputWSName, 0);
+    MatrixWorkspace_sptr input =
+        AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
+            inputWSName);
+    input->flagMasked(0, 0);
+    GroupDetectors2 gd2;
+    gd2.initialize();
+    gd2.setChild(true);
+    gd2.setRethrows(true);
+    gd2.setPropertyValue("InputWorkspace", inputWSName);
+    gd2.setPropertyValue("OutputWorkspace", "_unused_for_child");
+    gd2.setPropertyValue("WorkspaceIndexList", "0,1");
+    gd2.setPropertyValue("Behaviour", "Sum");
+    TS_ASSERT_THROWS_NOTHING(gd2.execute());
+    TS_ASSERT(gd2.isExecuted())
+    MatrixWorkspace_sptr output = gd2.getProperty("OutputWorkspace");
+    TS_ASSERT_EQUALS(output->getNumberHistograms(), 1)
+    const auto &y = output->y(0);
+    const auto &e = output->e(0);
+    for (size_t i = 0; i < y.size(); ++i) {
+      const double expectedSignal = i == 0 ? 2. : 1. + 2.;
+      TS_ASSERT_EQUALS(y[i], expectedSignal)
+      const double expectedError = i == 0 ? 1. : std::sqrt(2.);
+      TS_ASSERT_EQUALS(e[i], expectedError)
+    }
+  }
+
   void testEvents() {
     int numPixels = 5;
     int numBins = 5;
@@ -1018,11 +1079,36 @@ public:
                     output->y(0)[0], 0.00001);
   }
 
+  void test_masked_detids_get_propagated() {
+    createTestWorkspace(inputWSName, 0);
+    MatrixWorkspace_sptr input =
+        AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
+            inputWSName);
+    input->mutableDetectorInfo().setMasked(0, true);
+    GroupDetectors2 gd2;
+    gd2.initialize();
+    gd2.setChild(true);
+    gd2.setRethrows(true);
+    gd2.setPropertyValue("InputWorkspace", inputWSName);
+    gd2.setPropertyValue("OutputWorkspace", "_unused_for_child");
+    gd2.setPropertyValue("WorkspaceIndexList", "0,1");
+    gd2.setPropertyValue("Behaviour", "Sum");
+    TS_ASSERT_THROWS_NOTHING(gd2.execute());
+    TS_ASSERT(gd2.isExecuted())
+    MatrixWorkspace_sptr output = gd2.getProperty("OutputWorkspace");
+    TS_ASSERT_EQUALS(output->getNumberHistograms(), 1)
+    const auto &spectrum = output->getSpectrum(0);
+    const auto &ids = spectrum.getDetectorIDs();
+    TS_ASSERT(ids.size() == 2)
+    TS_ASSERT_DIFFERS(ids.find(0), ids.end())
+    TS_ASSERT_DIFFERS(ids.find(1), ids.end())
+  }
+
 private:
   const std::string inputWSName, offsetWSName, outputWSNameBase, inputFile;
-  enum constants { NHIST = 6, NBINS = 4 };
+  enum { NHIST = 6, NBINS = 4 };
 
-  void createTestWorkspace(const std::string &name, const int offset) {
+  static void createTestWorkspace(const std::string &name, const int offset) {
     // Set up a small workspace for testing
     auto space2D = createWorkspace<Workspace2D>(NHIST, NBINS + 1, NBINS);
     space2D->getAxis(0)->unit() = UnitFactory::Instance().create("TOF");
@@ -1101,48 +1187,63 @@ private:
 
 class GroupDetectors2TestPerformance : public CxxTest::TestSuite {
 public:
-  void setUp() override {
-    constexpr int numGroups = 2;
+  static GroupDetectors2TestPerformance *createSuite() {
+    return new GroupDetectors2TestPerformance();
+  }
+  static void destroySuite(GroupDetectors2TestPerformance *suite) {
+    delete suite;
+  }
+
+  GroupDetectors2TestPerformance()
+      : inputEventWs(nullptr), inputMatrixWs(nullptr), groupWs(nullptr), alg() {
+    constexpr int numGroups = 40;
     // This controls speed of test
     constexpr int bankPixelWidth = 30;
+    constexpr int numBins = 1000;
 
-    inputWs = WorkspaceCreationHelper::createEventWorkspaceWithFullInstrument(
-        numGroups, bankPixelWidth);
-    AnalysisDataService::Instance().addOrReplace(nxsWSname, inputWs);
-
+    inputEventWs =
+        WorkspaceCreationHelper::createEventWorkspaceWithFullInstrument(
+            numGroups, bankPixelWidth);
+    inputMatrixWs =
+        WorkspaceCreationHelper::create2DWorkspaceWithRectangularInstrument(
+            numGroups, bankPixelWidth, numBins);
     // Create an axis for each pixel.
-    for (size_t pix = 0; pix < inputWs->getNumberHistograms(); pix++) {
-      size_t xAxisSize = inputWs->x(pix).size();
+    for (size_t pix = 0; pix < inputEventWs->getNumberHistograms(); pix++) {
+      size_t xAxisSize = inputEventWs->x(pix).size();
       Mantid::HistogramData::HistogramX axisVals(xAxisSize, 1.0);
-      inputWs->mutableX(pix) = axisVals;
-      inputWs->getSpectrum(pix).addEventQuickly(TofEvent(1000.0));
+      inputEventWs->mutableX(pix) = axisVals;
+      inputEventWs->getSpectrum(pix).addEventQuickly(TofEvent(1000.0));
     }
-
     setupGroupWS(numGroups);
 
     alg.initialize();
-    alg.setPropertyValue("InputWorkspace", nxsWSname);
-    alg.setPropertyValue("OutputWorkspace", outputws);
-    alg.setPropertyValue("CopyGroupingFromWorkspace", groupWSName);
-
+    alg.setProperty("OutputWorkspace", "_unused_for_child");
+    alg.setProperty("CopyGroupingFromWorkspace", groupWs);
+    alg.setChild(true);
     alg.setRethrows(true);
   }
 
-  void testGroupDetectors2Performance() {
-    TS_ASSERT_THROWS_NOTHING(alg.execute());
+  void testGroupDetectors2EventPerformance() {
+    alg.setProperty("InputWorkspace", inputEventWs);
+    for (size_t i = 0; i < 100; ++i) {
+      TS_ASSERT_THROWS_NOTHING(alg.execute());
+    }
   }
 
-  void tearDown() override {
-    AnalysisDataService::Instance().remove(groupWSName);
-    AnalysisDataService::Instance().remove(nxsWSname);
-    AnalysisDataService::Instance().remove(outputws);
+  void testGroupDetectors2HistogramPerformance() {
+    alg.setProperty("InputWorkspace", inputMatrixWs);
+    for (size_t i = 0; i < 50; ++i) {
+      TS_ASSERT_THROWS_NOTHING(alg.execute());
+    }
   }
 
+  void tearDown() override {}
+
   void setupGroupWS(const size_t numGroups) {
 
     // ------------ Create a grouping workspace to match -------------
-    groupWs = boost::make_shared<GroupingWorkspace>(inputWs->getInstrument());
-    AnalysisDataService::Instance().addOrReplace(groupWSName, groupWs);
+    groupWs =
+        boost::make_shared<GroupingWorkspace>(inputEventWs->getInstrument());
 
     // fill in some groups
     constexpr size_t startingGroupNo = 1;
@@ -1157,11 +1258,8 @@ public:
   }
 
 private:
-  const std::string nxsWSname = "GroupDetectors2TestTarget_ws";
-  const std::string groupWSName = nxsWSname + "_GROUP";
-  const std::string outputws = nxsWSname + "_grouped";
-
-  EventWorkspace_sptr inputWs;
+  EventWorkspace_sptr inputEventWs;
+  MatrixWorkspace_sptr inputMatrixWs;
   GroupingWorkspace_sptr groupWs;
 
   GroupDetectors2 alg;
diff --git a/docs/source/release/v3.14.0/framework.rst b/docs/source/release/v3.14.0/framework.rst
index b6bd62c5ee12de14ca469a4eef54599585e5cb95..7e8f0656a5ace58a655ecb023d38f7c9e303afb0 100644
--- a/docs/source/release/v3.14.0/framework.rst
+++ b/docs/source/release/v3.14.0/framework.rst
@@ -55,6 +55,7 @@ Improvements
 - :ref:`LoadSampleShape <algm-LoadSampleShape-v1>` now supports loading from binary .stl files.
 - :ref:`MaskDetectorsIf <algm-MaskDetectorsIf>` now supports masking a workspace in addition to writing the masking information to a calfile.
 - :ref:`LoadSampleShape <algm-LoadSampleShape-v1>` now supports loading from binary .stl files.
+- :ref:`GroupDetectors <algm-GroupDetectors>` now takes masked bins correctly into account when processing histogram workspaces.
 
 Bugfixes
 ########