Program Listing for File cache_manager.hpp

Return to documentation for file (SeQuant/domain/eval/cache_manager.hpp)

#ifndef SEQUANT_EVAL_CACHE_MANAGER_HPP
#define SEQUANT_EVAL_CACHE_MANAGER_HPP

#include <SeQuant/core/asy_cost.hpp>
#include <SeQuant/core/container.hpp>
#include <SeQuant/core/eval_node.hpp>
#include <SeQuant/core/expr.hpp>
#include <SeQuant/domain/eval/eval_result.hpp>

#include <memory>
#include <range/v3/view.hpp>

namespace sequant {

class CacheManager {
 public:
  using key_type = size_t;

 private:
  class entry {
   private:
    size_t max_life;

    size_t life_c;

    ERPtr data_p;

   public:
    explicit entry(size_t count) noexcept;

    [[nodiscard]] ERPtr access() noexcept;

    void store(ERPtr data) noexcept;

    void reset() noexcept;

    [[nodiscard]] size_t life_count() const noexcept;

    [[nodiscard]] size_t max_life_count() const noexcept;

   private:
    [[nodiscard]] int decay() noexcept;

  };  // entry

  static ERPtr store(entry& entry, ERPtr data) noexcept;

  container::map<key_type, entry> cache_map_;

 public:
  template <typename Iterable1 = container::map<size_t, size_t>>
  explicit CacheManager(Iterable1&& decaying) noexcept {
    for (auto&& [k, c] : decaying) cache_map_.try_emplace(k, entry{c});
  }

  void reset() noexcept;

  ERPtr access(key_type key) noexcept;

  [[nodiscard]] ERPtr store(key_type key, ERPtr data) noexcept;

  [[nodiscard]] bool exists(key_type key) const noexcept;

  [[nodiscard]] int life(key_type key) const noexcept;

  [[nodiscard]] int max_life(key_type key) const noexcept;

  [[nodiscard]] container::set<size_t> keys() const noexcept;

  static CacheManager empty() noexcept;

  // for unit testing
  template <typename T>
  struct access_by;
  template <typename T>
  friend struct access_by;

};  // CacheManager

template <typename NodesI,
          typename = std::enable_if_t<IsIterableOfEvalNodes<NodesI>>>
CacheManager cache_manager(NodesI const& nodes,
                           size_t min_repeats = 2) noexcept {
  auto imed_counts = container::map<size_t, size_t>{};

  // visits a node and check if its hash value exists in imed_counts map
  // if it does increase the count and return false (to signal stop visiting
  // children nodes) otherwise returns true.
  auto imed_visitor = [&imed_counts](auto&& n) -> bool {
    auto&& end = imed_counts.end();
    auto&& h = hash::value(*n);
    if (auto&& found = imed_counts.find(h); found != end) {
      ++found->second;
      return false;
    } else
      imed_counts.emplace(h, 1);
    return true;
  };  // imed_visitor

  // visit imeds
  ranges::for_each(nodes, [&imed_visitor](auto&& tree) {
    tree.visit_internal(imed_visitor);
  });

  // remove less repeating imeds
  auto less_repeating = [min_repeats](auto&& pair) {
    return pair.second < min_repeats;
  };
  ranges::actions::remove_if(imed_counts, less_repeating);

  return CacheManager{imed_counts};
}

AsyCost peak_cache(Sum const& expr);

}  // namespace sequant

#endif  // SEQUANT_EVAL_CACHE_MANAGER_HPP