diff --git a/Reconstruction/Jet/JetCalibTools/JetCalibTools/CalibrationMethods/JetSmearingCorrection.h b/Reconstruction/Jet/JetCalibTools/JetCalibTools/CalibrationMethods/JetSmearingCorrection.h
index 1c21a577f738ec7691661508e1edd04486a0d9f9..4e256fd01be1aea8ceceb786a75c0786940127c9 100644
--- a/Reconstruction/Jet/JetCalibTools/JetCalibTools/CalibrationMethods/JetSmearingCorrection.h
+++ b/Reconstruction/Jet/JetCalibTools/JetCalibTools/CalibrationMethods/JetSmearingCorrection.h
@@ -10,6 +10,7 @@
 #include "JetCalibTools/JetCalibrationToolBase.h"
 
 #include <memory>
+#include <vector>
 
 #include "TRandom3.h"
 
@@ -35,10 +36,10 @@ class JetSmearingCorrection
     private:
         // Helper methods
         StatusCode getSigmaSmear(xAOD::Jet& jet, double& sigmaSmear) const;
-        StatusCode getNominalResolution(const xAOD::Jet& jet, TH1* histo, double& resolution) const;
+        StatusCode getNominalResolution(const xAOD::Jet& jet, TH1* histo, const std::vector< std::unique_ptr<TH1> >& projections, double& resolution) const;
         StatusCode readHisto(double& returnValue, TH1* histo, double x) const;
-        StatusCode readHisto(double& returnValue, TH1* histo, double x, double y) const;
-        StatusCode readHisto(double& returnValue, TH1* histo, double x, double y, double z) const;
+        StatusCode readHisto(double& returnValue, TH1* histo, const std::vector< std::unique_ptr<TH1> >& projections, double x, double y) const;
+        StatusCode cacheProjections(TH1* fullHistogram, std::vector< std::unique_ptr<TH1> >& cacheLocation, const std::string& type);
 
         // Private enums
         enum class SmearType
@@ -79,6 +80,10 @@ class JetSmearingCorrection
         InterpType m_interpType;
         std::unique_ptr<TH1> m_smearResolutionMC;
         std::unique_ptr<TH1> m_smearResolutionData;
+
+        // Variables to cache projections in case of 1-D interpolaton in 2-D or 3-D histograms
+        std::vector< std::unique_ptr<TH1> > m_cachedProjResMC;
+        std::vector< std::unique_ptr<TH1> > m_cachedProjResData;
 };
 
 
diff --git a/Reconstruction/Jet/JetCalibTools/Root/JetSmearingCorrection.cxx b/Reconstruction/Jet/JetCalibTools/Root/JetSmearingCorrection.cxx
index 36bf9af353e804f3e8a2e3c75bec93cdf4c6bcd1..5e43783136a08e6c4292234d145d49c8d8e30343 100644
--- a/Reconstruction/Jet/JetCalibTools/Root/JetSmearingCorrection.cxx
+++ b/Reconstruction/Jet/JetCalibTools/Root/JetSmearingCorrection.cxx
@@ -20,6 +20,8 @@ JetSmearingCorrection::JetSmearingCorrection(const std::string name)
     , m_interpType(InterpType::UNKNOWN)
     , m_smearResolutionMC()
     , m_smearResolutionData()
+    , m_cachedProjResMC()
+    , m_cachedProjResData()
 { }
 
 JetSmearingCorrection::JetSmearingCorrection(const std::string& name, TEnv* config, TString jetAlgo, TString calibAreaTag, bool dev)
@@ -36,6 +38,8 @@ JetSmearingCorrection::JetSmearingCorrection(const std::string& name, TEnv* conf
     , m_interpType(InterpType::UNKNOWN)
     , m_smearResolutionMC()
     , m_smearResolutionData()
+    , m_cachedProjResMC()
+    , m_cachedProjResData()
 { }
  
 JetSmearingCorrection::~JetSmearingCorrection()
@@ -215,6 +219,15 @@ StatusCode JetSmearingCorrection::initializeTool(const std::string&)
             return StatusCode::FAILURE;
     }
 
+    // Pre-cache the histogram file in 1D projections if relevant (depends on InterpType)
+    if (m_interpType == InterpType::OnlyX || m_interpType == InterpType::OnlyY)
+    {
+        if (cacheProjections(m_smearResolutionMC.get(),  m_cachedProjResMC,"mc").isFailure())
+            return StatusCode::FAILURE;
+        if (cacheProjections(m_smearResolutionData.get(),m_cachedProjResData,"data").isFailure())
+            return StatusCode::FAILURE;
+    }
+
     return StatusCode::SUCCESS;
 }
 
@@ -262,7 +275,7 @@ StatusCode JetSmearingCorrection::readHisto(double& returnValue, TH1* histo, dou
     return StatusCode::SUCCESS;
 }
 
-StatusCode JetSmearingCorrection::readHisto(double& returnValue, TH1* histo, double x, double y) const
+StatusCode JetSmearingCorrection::readHisto(double& returnValue, TH1* histo, const std::vector< std::unique_ptr<TH1> >& projections, double x, double y) const
 {
     // Ensure that the histogram exists
     if (!histo)
@@ -293,10 +306,6 @@ StatusCode JetSmearingCorrection::readHisto(double& returnValue, TH1* histo, dou
         y = minY + 1.e-6;
 
     // Get the result, interpolating as appropriate
-    // We need to use this as a 2D histogram to have access to the projection methods
-    // We also need a histogram to project into
-    TH2* localHist  = NULL;
-    TH1* projection = NULL;
     switch (m_interpType)
     {
         case InterpType::Full:
@@ -308,27 +317,13 @@ StatusCode JetSmearingCorrection::readHisto(double& returnValue, TH1* histo, dou
             break;
 
         case InterpType::OnlyX:
-            localHist = dynamic_cast<TH2*>(histo);
-            if (!localHist)
-            {
-                ATH_MSG_ERROR("Could not convert the histogram to a TH2, please check inputs");
-                return StatusCode::FAILURE;
-            }
-            projection = localHist->ProjectionX("projx_2D",histo->GetYaxis()->FindBin(y),histo->GetYaxis()->FindBin(y));
-            returnValue = projection->Interpolate(x);
-            delete projection;
+            // Determine the y-bin and use the cached projection to interpolate x
+            returnValue = projections.at(histo->GetYaxis()->FindBin(y))->Interpolate(x);
             break;
 
         case InterpType::OnlyY:
-            localHist = dynamic_cast<TH2*>(histo);
-            if (!localHist)
-            {
-                ATH_MSG_ERROR("Could not convert the histogram to a TH2, please check inputs");
-                return StatusCode::FAILURE;
-            }
-            projection = localHist->ProjectionY("projy_2D",histo->GetXaxis()->FindBin(x),histo->GetXaxis()->FindBin(x));
-            returnValue = projection->Interpolate(y);
-            delete projection;
+            // Determine the x-bin and use the cached projection to interpolate y
+            returnValue = projections.at(histo->GetXaxis()->FindBin(x))->Interpolate(y);
             break;
 
         default:
@@ -339,88 +334,6 @@ StatusCode JetSmearingCorrection::readHisto(double& returnValue, TH1* histo, dou
     return StatusCode::SUCCESS;
 }
 
-StatusCode JetSmearingCorrection::readHisto(double& returnValue, TH1* histo, double x, double y, double z) const
-{
-    // Ensure that the histogram exists
-    if (!histo)
-    {
-        ATH_MSG_ERROR("Unable to read histogram - address is NULL");
-        return StatusCode::FAILURE;
-    }
-
-    // Check dimensionality just to be safe
-    if (histo->GetDimension() != 3)
-    {
-        ATH_MSG_ERROR("Blocking reading of a " << histo->GetDimension() << "D histogram as a 3D histogram");
-        return StatusCode::FAILURE;
-    }
-
-    // Ensure we are within boundaries
-    const double minX = histo->GetXaxis()->GetBinLowEdge(1);
-    const double maxX = histo->GetXaxis()->GetBinLowEdge(histo->GetNbinsX()+1);
-    if ( x >= maxX )
-        x = maxX - 1.e-6;
-    else if ( x <= minX )
-        x = minX + 1.e-6;
-    const double minY = histo->GetYaxis()->GetBinLowEdge(1);
-    const double maxY = histo->GetYaxis()->GetBinLowEdge(histo->GetNbinsY()+1);
-    if ( y >= maxY )
-        y = maxY - 1.e-6;
-    else if ( y <= minY )
-        y = minY + 1.e-6;
-    const double minZ = histo->GetZaxis()->GetBinLowEdge(1);
-    const double maxZ = histo->GetZaxis()->GetBinLowEdge(histo->GetNbinsZ()+1);
-    if ( z >= maxZ )
-        z = maxZ - 1.e-6;
-    else if ( z <= minZ )
-        z = minZ + 1.e-6;
-
-    // Get the result, interpolating as appropriate
-    // We need to use this as a 3D histogram to have access to the projection methods
-    // We also need a histogram to project into
-    TH3* localHist  = NULL;
-    TH1* projection = NULL;
-    switch (m_interpType)
-    {
-        case InterpType::Full:
-            returnValue = histo->Interpolate(x,y,z);
-            break;
-        
-        case InterpType::None:
-            returnValue = histo->GetBinContent(histo->GetXaxis()->FindBin(x),histo->GetYaxis()->FindBin(y),histo->GetZaxis()->FindBin(z));
-            break;
-
-        case InterpType::OnlyX:
-            localHist = dynamic_cast<TH3*>(histo);
-            if (!localHist)
-            {
-                ATH_MSG_ERROR("Could not convert the histogram to a TH3, please check inputs");
-                return StatusCode::FAILURE;
-            }
-            projection = localHist->ProjectionX("projx_3D",histo->GetYaxis()->FindBin(y),histo->GetYaxis()->FindBin(y),histo->GetZaxis()->FindBin(z),histo->GetZaxis()->FindBin(z));
-            returnValue = projection->Interpolate(x);
-            delete projection;
-            break;
-
-        case InterpType::OnlyY:
-            localHist = dynamic_cast<TH3*>(histo);
-            if (!localHist)
-            {
-                ATH_MSG_ERROR("Could not convert the histogram to a TH3, please check inputs");
-                return StatusCode::FAILURE;
-            }
-            projection = localHist->ProjectionY("projy_3D",histo->GetXaxis()->FindBin(x),histo->GetXaxis()->FindBin(x),histo->GetZaxis()->FindBin(z),histo->GetZaxis()->FindBin(z));
-            returnValue = projection->Interpolate(y);
-            delete projection;
-            break;
-
-        default:
-            ATH_MSG_ERROR("Unsupported interpolation type for a 3D histogram");
-            return StatusCode::FAILURE;
-    }
-
-    return StatusCode::SUCCESS;
-}
 
 StatusCode JetSmearingCorrection::getSigmaSmear(xAOD::Jet& jet, double& sigmaSmear) const
 {
@@ -457,23 +370,23 @@ StatusCode JetSmearingCorrection::getSigmaSmear(xAOD::Jet& jet, double& sigmaSme
     return StatusCode::SUCCESS;
 }
 
-StatusCode JetSmearingCorrection::getNominalResolution(const xAOD::Jet& jet, TH1* histo, double& resolution) const
+StatusCode JetSmearingCorrection::getNominalResolution(const xAOD::Jet& jet, TH1* histo, const std::vector< std::unique_ptr<TH1> >& projections, double& resolution) const
 {
     double localRes = 0;
     switch (m_histType)
     {
         case HistType::Pt:
-            if (readHisto(localRes,histo,jet.pt()).isFailure())
+            if (readHisto(localRes,histo,jet.pt()/m_GeV).isFailure())
                 return StatusCode::FAILURE;
             break;
 
         case HistType::PtEta:
-            if (readHisto(localRes,histo,jet.pt(),jet.eta()).isFailure())
+            if (readHisto(localRes,histo,projections,jet.pt()/m_GeV,jet.eta()).isFailure())
                 return StatusCode::FAILURE;
             break;
 
         case HistType::PtAbsEta:
-            if (readHisto(localRes,histo,jet.pt(),fabs(jet.eta())).isFailure())
+            if (readHisto(localRes,histo,projections,jet.pt()/m_GeV,fabs(jet.eta())).isFailure())
                 return StatusCode::FAILURE;
             break;
 
@@ -491,12 +404,12 @@ StatusCode JetSmearingCorrection::getNominalResolution(const xAOD::Jet& jet, TH1
 
 StatusCode JetSmearingCorrection::getNominalResolutionData(const xAOD::Jet& jet, double& resolution) const
 {
-    return getNominalResolution(jet,m_smearResolutionData.get(),resolution);
+    return getNominalResolution(jet,m_smearResolutionData.get(),m_cachedProjResData,resolution);
 }
 
 StatusCode JetSmearingCorrection::getNominalResolutionMC(const xAOD::Jet& jet, double& resolution) const
 {
-    return getNominalResolution(jet,m_smearResolutionMC.get(),resolution);
+    return getNominalResolution(jet,m_smearResolutionMC.get(),m_cachedProjResMC,resolution);
 }
 
 
@@ -546,4 +459,90 @@ StatusCode JetSmearingCorrection::calibrateImpl(xAOD::Jet& jet, JetEventInfo&) c
     return StatusCode::SUCCESS;
 }
 
+StatusCode JetSmearingCorrection::cacheProjections(TH1* fullHistogram, std::vector< std::unique_ptr<TH1> >& cacheLocation, const std::string& type)
+{
+    // Ensure the histogram exists
+    if (!fullHistogram)
+    {
+        ATH_MSG_FATAL("Cannot cache histogram as it doesn't exist: " << type);
+        return StatusCode::FAILURE;
+    }
+
+    // Ensure the number of dimensions is sane
+    if (fullHistogram->GetDimension() < 1 || fullHistogram->GetDimension() > 2)
+    {
+        ATH_MSG_FATAL("Unsupported histogram dimensionality for projection caching: " << fullHistogram->GetDimension());
+        return StatusCode::FAILURE;
+    }
+
+    // Protect vs InterpType
+    switch (m_interpType)
+    {
+        case InterpType::OnlyX:
+            // Simple case of 1D
+            if (fullHistogram->GetDimension() == 1)
+                return StatusCode::SUCCESS;
+            break;
+
+        case InterpType::OnlyY:
+            //  Failure case of 1D
+            if (fullHistogram->GetDimension() == 1)
+            {
+                ATH_MSG_FATAL("Cannot project in Y for a 1D histogram: " << type);
+                return StatusCode::FAILURE;
+            }
+            break;
+
+        default:
+            ATH_MSG_FATAL("The interpolation type is not supported for caching: " << type);
+            return StatusCode::FAILURE;
+    }
+    
+    // If we got here, then the request makes sense
+    // Start the projections
+    // Intentionally include underflow and overflow bins
+    // This keeps the same indexing scheme as root
+    // Avoids confusion and problems later at cost of a small amount of RAM
+    if (fullHistogram->GetDimension() == 2)
+    {
+        TH2* localHist = dynamic_cast<TH2*>(fullHistogram);
+        if (!localHist)
+        {
+            ATH_MSG_FATAL("Failed to convert histogram to a TH2, please check inputs: " << type);
+            return StatusCode::FAILURE;
+        }
+        if (m_interpType == InterpType::OnlyX)
+        {
+            for (Long64_t binY = 0; binY < localHist->GetNbinsY()+1; ++binY)
+            {
+                // Single bin of Y, interpolate across X
+                cacheLocation.push_back(std::unique_ptr<TH1>(localHist->ProjectionX(Form("projx_%s_%lld",type.c_str(),binY),binY,binY)));
+            }
+        }
+        else if (m_interpType == InterpType::OnlyY)
+        {
+            for (Long64_t binX = 0; binX < localHist->GetNbinsX()+1; ++binX)
+            {
+                // Single bin of X, interpolate across Y
+                cacheLocation.push_back(std::unique_ptr<TH1>(localHist->ProjectionY(Form("projy_%s_%lld",type.c_str(),binX),binX,binX)));
+            }
+        }
+        else
+        {
+            // We shouldn't make it here due to earlier checks
+            ATH_MSG_FATAL("Unexpected interpolation type, somehow escaped earlier checks: " << type);
+            return StatusCode::FAILURE;
+        }
+    }
+    else
+    {
+        // We shouldn't make it here due to earlier checks
+        ATH_MSG_FATAL("Unexpected dimensionality: " << fullHistogram->GetDimension());
+        return StatusCode::FAILURE;
+    }
+
+    // All done
+    return StatusCode::SUCCESS;
+}
+
 
diff --git a/Reconstruction/Jet/JetCalibTools/util/JetCalibTools_SmearingPlots.cxx b/Reconstruction/Jet/JetCalibTools/util/JetCalibTools_SmearingPlots.cxx
index 3c7cf7465ef7972d270b5fdac1c1063e1831df72..ace27098b2dfc86876397fa54f382694110faf5f 100644
--- a/Reconstruction/Jet/JetCalibTools/util/JetCalibTools_SmearingPlots.cxx
+++ b/Reconstruction/Jet/JetCalibTools/util/JetCalibTools_SmearingPlots.cxx
@@ -14,11 +14,13 @@
 #include "AsgTools/AnaToolHandle.h"
 
 #include <vector>
+#include <ctime>
 #include "TString.h"
 #include "TH1D.h"
 #include "TF1.h"
 #include "TCanvas.h"
 #include "TLatex.h"
+#include "TRandom3.h"
 
 
 namespace jet
@@ -41,9 +43,9 @@ namespace jet
 int main (int argc, char* argv[])
 {
     // Check argument usage
-    if (argc != 5 && argc != 6)
+    if (argc < 5 || argc > 7)
     {
-        std::cout << "USAGE: " << argv[0] << " <JetCollection> <ConfigFile> <OutputFile> <isData> (dev mode switch)" << std::endl;
+        std::cout << "USAGE: " << argv[0] << " <JetCollection> <ConfigFile> <OutputFile> <isData> (dev mode switch) (timing test switch)" << std::endl;
         return 1;
     }
 
@@ -53,6 +55,7 @@ int main (int argc, char* argv[])
     const TString outFile = argv[3];
     const bool isData     = (TString(argv[4]) == "true");
     const bool isDevMode  = ( argc > 5 && (TString(argv[5]) == "true" || TString(argv[5]) == "dev") ) ? true : false;
+    const bool isTimeTest = ( argc > 6 && TString(argv[6]) == "true" ) ? true : false;
 
     // Derived information
     const bool outFileIsExtensible = outFile.EndsWith(".pdf") || outFile.EndsWith(".ps");
@@ -61,10 +64,11 @@ int main (int argc, char* argv[])
     // Assumed constants
     const TString calibSeq = "Smear"; // only want to apply the smearing correction here
     const std::vector<float> ptVals = {20, 40, 60, 100, 400, 1000};
-    const float eta = 0;
+    const float eta = 0.202;
     const float phi = 0;
-    const float mass = 100;
+    const float mass = 10;
     const int maxNumIter = 1e5;
+    const int numTimeIter = 100;
 
     // Accessor strings
     const TString startingScaleString = "JetGSCScaleMomentum";
@@ -133,7 +137,46 @@ int main (int argc, char* argv[])
     {
         const TString baseName = Form("Smear_%.0f",pt);
         hists_pt.push_back(new TH1D(baseName+"_pT",baseName+"_pT",100,0.25*pt,1.75*pt));
-        hists_m.push_back(new TH1D(baseName+"_mass",baseName+"_pT",100,0.25*mass,1.75*mass));
+        hists_m.push_back(new TH1D(baseName+"_mass",baseName+"_mass",100,0.25*mass,1.75*mass));
+    }
+
+    // Run the timing test if specified
+    if (isTimeTest)
+    {
+        TRandom3 rand;
+        rand.SetSeed(0); // Deterministic random test
+
+        for (int numTest = 0; numTest < numTimeIter; ++numTest)
+        {
+            // Make a new calibration tool each time
+            JetCalibrationTool* jetCalibTool = new JetCalibrationTool(Form("mytool_%d",numTest));
+            if (jetCalibTool->setProperty("JetCollection",jetAlgo.Data()).isFailure())
+                exit(1);
+            if (jetCalibTool->setProperty("ConfigFile",config.Data()).isFailure())
+                exit(1);
+            if (jetCalibTool->setProperty("CalibSequence",calibSeq.Data()).isFailure())
+                exit(1);
+            if (jetCalibTool->setProperty("IsData",isData).isFailure())
+                exit(1);
+            if (isDevMode && jetCalibTool->setProperty("DEVmode",isDevMode).isFailure())
+                exit(1);
+            if (jetCalibTool->initialize().isFailure())
+                exit(1);
+
+            clock_t startTime = clock();
+            for (int numIter = 0; numIter < maxNumIter; ++numIter)
+            {
+                xAOD::JetFourMom_t fourvec(rand.Uniform(20.e3,1000.e3),rand.Uniform(-2,2),rand.Uniform(-3.14,3.14),10.e3);
+                jet->setJetP4(fourvec);
+                startingScale.setAttribute(*jet,fourvec);
+                xAOD::Jet* smearedJet = nullptr;
+                jetCalibTool->calibratedCopy(*jet,smearedJet);
+                //calibTool->calibratedCopy(*jet,smearedJet);
+                delete smearedJet;
+            }
+            delete jetCalibTool;
+            printf("Iteration %d: %f seconds\n",numTest+1,(clock()-startTime)/((double)CLOCKS_PER_SEC));
+        }
     }
 
     // Fill the histograms
@@ -147,12 +190,12 @@ int main (int argc, char* argv[])
         hist_m->Sumw2();
 
 
-        printf("Running for pT of %.0f GeV\n",pt);
+        printf("Running for pT of %.0f\n",pt);
 
         for (int numIter = 0; numIter < maxNumIter; ++numIter)
         {
             // Set the jet four-vector
-            xAOD::JetFourMom_t fourvec(pt,eta,phi,mass);
+            xAOD::JetFourMom_t fourvec(pt*1.e3,eta,phi,mass*1.e3);
             jet->setJetP4(fourvec);
             startingScale.setAttribute(*jet,fourvec);
 
@@ -173,8 +216,8 @@ int main (int argc, char* argv[])
             }
 
             // Fill the histograms
-            hist_pt->Fill(smearedJet->pt());
-            hist_m->Fill(smearedJet->m());
+            hist_pt->Fill(smearedJet->pt()/1.e3);
+            hist_m->Fill(smearedJet->m()/1.e3);
 
             // Clean up
             delete smearedJet;
@@ -190,7 +233,7 @@ int main (int argc, char* argv[])
     {
         //  Set the jet four-vector
         const float pt = nominalResData.GetXaxis()->GetBinCenter(binX);
-        xAOD::JetFourMom_t fourvec(pt,eta,phi,mass);
+        xAOD::JetFourMom_t fourvec(pt*1.e3,eta,phi,mass*1.e3);
         jet->setJetP4(fourvec);
 
         // Jet kinematics set, now get the nominal resolutions