#ifndef GPU_INTEL_KERNEL_CACHE_HPP
#define GPU_INTEL_KERNEL_CACHE_HPP
#include <memory>
#include <type_traits>
#include "common/cache_hit_types.hpp"
#include "common/engine_id.hpp"
#include "common/kernel_cache.hpp"
#include "common/serialization.hpp"
#include "common/utils.hpp"
#include "gpu/intel/engine.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
template <typename T>
struct key_validator_t {
template <typename V>
using is_trivial_t = std::is_base_of<trivially_serializable_base_t, V>;
static_assert(!is_trivial_t<std::vector<int>>::value,
"is_trivial_t has unexpected behavior");
static_assert(is_trivial_t<trivially_serializable_t<int>>::value,
"is_trivial_t has unexpected behavior");
template <typename U,
utils::enable_if_t<is_trivial_t<U>::value, bool> = true>
static bool is_valid(const U &t) {
static_assert(std::is_same<T, U>::value,
"key validation is not intended for comparing different types");
return true;
}
template <typename U,
utils::enable_if_t<!is_trivial_t<U>::value, bool> = true>
static bool is_valid(const U &t) {
static_assert(
std::is_same<T, decltype(T::deserialize(t.serialize()))>::value,
"serialization and deserialization must be supported for "
"validation in C++20 builds");
static_assert(std::is_same<T, U>::value,
"key validation is not intended for comparing different types");
#if __cplusplus >= 202002L
return t == T::deserialize(t.serialize());
#else
return true;
#endif
}
};
template <typename T>
struct trivial_key_t : public T {
template <typename S>
struct create_signature_t {};
template <typename R, typename C, typename A1, typename A2>
struct create_signature_t<R (C::*)(A1, A2) const> {
using result_type = R;
using class_type = C;
using arg1_type = A1;
using arg2_type = A2;
};
using value_type =
typename std::remove_reference<typename create_signature_t<
decltype(&T::create_generator)>::arg2_type>::type;
trivial_key_t() = delete;
trivial_key_t(const T &t, const engine_id_t &id)
: T(t)
, id_(id)
, serialization(t.serialize())
, hash_(hash_combine(serialization.get_hash(), id_.hash())) {}
bool operator==(const trivial_key_t &other) const {
return serialization == other.serialization && id_ == other.id_;
}
size_t hash() const { return hash_; }
bool is_valid() const {
const T *base = this;
return key_validator_t<T>::is_valid(*base);
}
private:
engine_id_t id_;
serialization_stream_t serialization;
size_t hash_;
};
template <typename T>
struct gpu_kernel_value_container_t : public kernel_cache::value_impl_t {
gpu_kernel_value_container_t(T &&t) : value(std::move(t)) {}
T value;
};
struct gpu_kernel_value_t {
gpu_kernel_value_t() = default;
gpu_kernel_value_t(const std::shared_ptr<kernel_cache::value_impl_t> &impl)
: impl_(impl) {}
const kernel_cache::value_impl_t *impl() const { return impl_.get(); }
std::shared_ptr<kernel_cache::value_impl_t> release() {
std::shared_ptr<kernel_cache::value_impl_t> ret = nullptr;
std::swap(ret, impl_);
return ret;
}
private:
std::shared_ptr<kernel_cache::value_impl_t> impl_;
};
struct gpu_kernel_key_impl_t : public kernel_cache::key_impl_t {
virtual status_t create_generator(
impl::engine_t *engine, gpu_kernel_value_t &generator) const
= 0;
};
template <typename K>
struct gpu_kernel_key_container_t final : public gpu_kernel_key_impl_t {
using value_type = typename K::value_type;
~gpu_kernel_key_container_t() final = default;
gpu_kernel_key_container_t(const gpu_kernel_key_container_t &) = default;
gpu_kernel_key_container_t(gpu_kernel_key_container_t &&) = default;
gpu_kernel_key_container_t &operator=(const gpu_kernel_key_container_t &)
= default;
gpu_kernel_key_container_t &operator=(gpu_kernel_key_container_t &&)
= default;
template <typename... Args>
gpu_kernel_key_container_t(Args &&...args)
: key(std::forward<Args>(args)...) {}
status_t create_generator(impl::engine_t *engine,
gpu_kernel_value_t &generator) const override {
auto g = std::make_shared<gpu_kernel_value_container_t<value_type>>(
value_type());
auto *intel_engine = utils::downcast<intel::engine_t *>(engine);
auto status = key.create_generator(*intel_engine, g->value);
generator = std::static_pointer_cast<kernel_cache::value_impl_t>(g);
return status;
}
bool compare(const key_impl_t *key_impl) const override {
auto *o = dynamic_cast<const gpu_kernel_key_container_t<K> *>(key_impl);
if (o == nullptr) return false;
return key == o->key;
}
size_t hash() const override { return key.hash(); }
K key;
};
template <typename K>
using trivial_key_container_t = gpu_kernel_key_container_t<trivial_key_t<K>>;
template <typename value_type>
status_t get_cached_kernels(std::shared_ptr<gpu_kernel_key_impl_t> &&key_impl,
impl::engine_t *engine, std::vector<compute::kernel_t> &kernels,
const std::vector<const char *> &kernel_names,
cache_state_t &kernel_cache_hit);
extern template status_t get_cached_kernels<compute::kernel_t>(
std::shared_ptr<gpu_kernel_key_impl_t> &&key_impl,
impl::engine_t *engine, std::vector<compute::kernel_t> &kernels,
const std::vector<const char *> &kernel_names,
cache_state_t &kernel_cache_hit);
extern template status_t get_cached_kernels<compute::kernel_bundle_t>(
std::shared_ptr<gpu_kernel_key_impl_t> &&key_impl,
impl::engine_t *engine, std::vector<compute::kernel_t> &kernels,
const std::vector<const char *> &kernel_names,
cache_state_t &kernel_cache_hit);
} } } }
#endif