#ifndef GRAPH_INTERFACE_CONSTANT_TENSOR_CACHE_HPP
#define GRAPH_INTERFACE_CONSTANT_TENSOR_CACHE_HPP
#include <atomic>
#include <functional>
#include <future>
#include <limits>
#include <memory>
#include <type_traits>
#include <unordered_map>
#include "common/c_types_map.hpp"
#include "common/engine.hpp"
#include "common/rw_mutex.hpp"
#include "graph/interface/allocator.hpp"
#include "graph/interface/c_types_map.hpp"
namespace dnnl {
namespace impl {
namespace graph {
class constant_buffer_t {
public:
using malloc_func_t = void *(*)(size_t, impl::engine_t *, allocator_t *);
using free_func_t = void (*)(void *, impl::engine_t *, allocator_t *);
constant_buffer_t(size_t size, impl::engine_t *eng, allocator_t *alc,
malloc_func_t malloc_func, free_func_t free_func)
: size_(size)
, eng_(eng)
, alc_(alc)
, malloc_func_(malloc_func)
, free_func_(free_func) {
data_ = malloc_func_(size, eng, alc);
eng_->retain();
}
virtual ~constant_buffer_t() {
free_func_(data_, eng_, alc_);
eng_->release();
}
constant_buffer_t(const constant_buffer_t &) = delete;
constant_buffer_t(constant_buffer_t &&) = delete;
constant_buffer_t &operator=(const constant_buffer_t &) = delete;
constant_buffer_t &operator=(constant_buffer_t &&) = delete;
template <typename T>
T *data() const {
return static_cast<T *>(data_);
}
size_t size() const { return size_; }
virtual void notify_evict() {}
protected:
void *data_;
size_t size_;
impl::engine_t *eng_;
allocator_t *alc_;
private:
malloc_func_t malloc_func_;
free_func_t free_func_;
};
struct constant_tensor_cache_t {
using key_t = size_t;
using cached_t = std::shared_ptr<constant_buffer_t>;
using value_t = std::shared_future<cached_t>;
explicit constant_tensor_cache_t(
size_t capacity_in_bytes, const std::string &name = "");
~constant_tensor_cache_t();
void retain() { counter_.fetch_add(1, std::memory_order_relaxed); }
void release() {
if (counter_.fetch_sub(1, std::memory_order_relaxed) == 1) {
delete this;
}
}
status_t set_capacity(size_t capacity);
size_t get_capacity();
value_t get_or_add(key_t backend_id, key_t backend_specific_key,
size_t size, const value_t &value);
void remove_if_exist(key_t backend_id, key_t backend_specific_key);
size_t get_size() const;
static key_t combine_key(key_t backend_id, key_t backend_specific_key);
private:
void evict(size_t n);
value_t get(const key_t &key);
void add(const key_t &key, size_t size, const value_t &constant);
void lock_read() { rw_mutex_.lock_read(); }
void lock_write() { rw_mutex_.lock_write(); }
void unlock_read() { rw_mutex_.unlock_read(); }
void unlock_write() { rw_mutex_.unlock_write(); }
constant_tensor_cache_t(const constant_tensor_cache_t &) = delete;
constant_tensor_cache_t(constant_tensor_cache_t &&) = delete;
constant_tensor_cache_t &operator=(const constant_tensor_cache_t &)
= delete;
constant_tensor_cache_t &operator=(constant_tensor_cache_t &&) = delete;
struct timed_entry_t {
value_t value_;
std::atomic<size_t> timestamp_;
timed_entry_t(const value_t &value, size_t timestamp)
: value_(value), timestamp_(timestamp) {}
};
std::unordered_map<key_t, timed_entry_t> &constant_map() {
return *constant_map_;
}
const std::unordered_map<key_t, timed_entry_t> &constant_map() const {
return *constant_map_;
}
std::unique_ptr<std::unordered_map<key_t, timed_entry_t>> constant_map_;
impl::utils::rw_mutex_t rw_mutex_;
std::string name_;
std::atomic<size_t> capacity_in_bytes_;
std::atomic<int32_t> counter_;
};
constant_tensor_cache_t *get_constant_tensor_cache(
impl::engine_kind_t eng_kind, size_t index);
} } }
#endif