From a5bf6744190cdb38621699e5e34c7e615dfd1e2d Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Wed, 9 Jan 2019 00:23:23 -0500 Subject: [PATCH] [Input] Cache parsed YAML files to speed up repeated reads --- include/cantera/base/AnyMap.h | 6 ++++++ src/base/AnyMap.cpp | 32 ++++++++++++++++++++++++++++---- src/base/application.cpp | 2 +- src/base/application.h | 2 ++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/include/cantera/base/AnyMap.h b/include/cantera/base/AnyMap.h index 594caf8bc..0aa0b44e8 100644 --- a/include/cantera/base/AnyMap.h +++ b/include/cantera/base/AnyMap.h @@ -340,6 +340,12 @@ private: std::unordered_map m_data; UnitSystem m_units; + + //! Cache for previously-parsed input (YAML) files. The key is the full path + //! to the file, and the second element of the value is the last-modified + //! time for the file, which is used to enable change detection. + static std::unordered_map> s_cache; + friend class AnyValue; }; diff --git a/src/base/AnyMap.cpp b/src/base/AnyMap.cpp index 4871053cf..ee4052790 100644 --- a/src/base/AnyMap.cpp +++ b/src/base/AnyMap.cpp @@ -4,6 +4,7 @@ // at http://www.cantera.org/license.txt for license and copyright information. #include "cantera/base/AnyMap.h" +#include "application.h" #include "cantera/base/yaml.h" #include "cantera/base/stringUtils.h" #ifdef CT_USE_DEMANGLE @@ -12,11 +13,14 @@ #include #include +#include namespace ba = boost::algorithm; namespace { // helper functions +std::mutex yaml_cache_mutex; + bool isFloat(const std::string& val) { // This function duplicates the logic of fpValueCheck, but doesn't throw @@ -238,6 +242,8 @@ std::map AnyValue::s_typenames = { {typeid(AnyMap).name(), "AnyMap"}, }; +std::unordered_map> AnyMap::s_cache; + // Methods of class AnyValue AnyValue::AnyValue() @@ -745,17 +751,35 @@ AnyMap AnyMap::fromYamlString(const std::string& yaml) { AnyMap AnyMap::fromYamlFile(const std::string& name) { std::string fullName = findInputFile(name); - AnyMap amap; + int mtime = get_modified_time(fullName); + + // Check for an already-parsed YAML file with the same last-modified time, + // and return that if possible + std::unique_lock lock(yaml_cache_mutex); + auto iter = s_cache.find(fullName); + if (iter != s_cache.end() && iter->second.second == mtime) { + return iter->second.first; + } + + // Generate an AnyMap from the YAML file and store it in the cache + auto& cache_item = s_cache[fullName]; + cache_item.second = mtime; try { YAML::Node node = YAML::LoadFile(fullName); - amap = node.as(); + cache_item.first = node.as(); + cache_item.first.applyUnits(UnitSystem()); } catch (YAML::Exception& err) { std::ifstream infile(fullName); + s_cache.erase(fullName); throw CanteraError("AnyMap::fromYamlFile", formatYamlError(err, infile, name)); + } catch (CanteraError& err) { + s_cache.erase(fullName); + throw; } - amap.applyUnits(UnitSystem()); - return amap; + + // Return a copy of the AnyMap + return cache_item.first; } AnyMap::const_iterator begin(const AnyValue& v) { diff --git a/src/base/application.cpp b/src/base/application.cpp index 146efaa56..a6daf1f78 100644 --- a/src/base/application.cpp +++ b/src/base/application.cpp @@ -38,7 +38,7 @@ static std::mutex app_mutex; //! Mutex for controlling access to XML file storage static std::mutex xml_mutex; -static int get_modified_time(const std::string& path) { +int get_modified_time(const std::string& path) { #ifdef _WIN32 HANDLE hFile = CreateFile(path.c_str(), NULL, NULL, NULL, OPEN_EXISTING, 0, NULL); diff --git a/src/base/application.h b/src/base/application.h index 9c5df3014..e268da1e2 100644 --- a/src/base/application.h +++ b/src/base/application.h @@ -19,6 +19,8 @@ namespace Cantera class XML_Node; +int get_modified_time(const std::string& path); + /*! * @defgroup globalData Global Data *