#include "oneapi/dnnl/dnnl_graph.h"
#include "graph/interface/backend.hpp"
#include "graph/interface/logical_tensor.hpp"
#include "graph/interface/partition_hashing.hpp"
#include "common/verbose.hpp"
using namespace dnnl::impl::graph;
#define VCHECK_LOGICAL_TENSOR(cond, stat, msg, ...) \
VCONDCHECK(graph, create, check, logical_tensor, (cond), stat, msg, \
##__VA_ARGS__)
size_t logical_tensor_wrapper_t::size() const {
if (is_strided()) {
if (ndims() == 0) { return data_type_size(); }
if (has_zero_dim()) { return 0U; }
size_t max_size = 0;
for (int d = 0; d < ndims(); ++d) {
dim_t strided_pdim = dims()[d];
dim_t effective_stride = strided_pdim == 1 ? 1 : strides()[d];
max_size = std::max<size_t>(max_size,
static_cast<size_t>(strided_pdim * effective_stride));
}
size_t data_size = utils::div_up(
max_size * data_type_size(), sub_byte_data_type_multiplier());
return data_size;
} else if (is_opaque()) {
size_t layout_id = lt->layout.layout_id;
auto backend
= backend_registry_t::get_singleton().get_registered_backend(
layout_id);
logical_tensor_t new_lt = *lt;
new_lt.layout.layout_id
= backend_registry_t::extract_layout_id(layout_id);
return backend->get_mem_size(new_lt);
} else {
return (size_t)-1;
}
}
bool logical_tensor_wrapper_t::is_identical(
const logical_tensor_t &lhs, const logical_tensor_t &rhs) const {
bool equal = (lhs.id == rhs.id) && (lhs.ndims == rhs.ndims)
&& (lhs.data_type == rhs.data_type)
&& (lhs.layout_type == rhs.layout_type)
&& (lhs.property == rhs.property);
if (!equal) return false;
if (lhs.ndims == 0 || lhs.ndims == -1) return true;
equal = std::equal(std::begin(lhs.dims), std::begin(lhs.dims) + lhs.ndims,
std::begin(rhs.dims));
if (!equal) return false;
if (lhs.layout_type == layout_type::strided) {
return std::equal(std::begin(lhs.layout.strides),
std::begin(lhs.layout.strides) + lhs.ndims,
std::begin(rhs.layout.strides));
} else if (lhs.layout_type == layout_type::opaque) {
return lhs.layout.layout_id == rhs.layout.layout_id;
} else {
return true;
}
}
bool logical_tensor_wrapper_t::is_similar(const logical_tensor_t &lhs,
const logical_tensor_t &rhs, bool check_id, bool check_dtype) const {
bool equal = check_id ? (lhs.id == rhs.id) : true;
equal = equal && (check_dtype ? lhs.data_type == rhs.data_type : true);
equal = equal && (lhs.ndims == rhs.ndims);
equal = equal && (lhs.property == rhs.property);
if (!equal) return equal;
if (lhs.ndims > 0) {
if (!std::equal(std::begin(lhs.dims), std::begin(lhs.dims) + lhs.ndims,
std::begin(rhs.dims)))
return false;
}
if (lhs.layout_type == rhs.layout_type) {
if (lhs.ndims <= 0) return true;
if (lhs.layout_type == layout_type::strided) {
return std::equal(std::begin(lhs.layout.strides),
std::begin(lhs.layout.strides) + lhs.ndims,
std::begin(rhs.layout.strides));
} else if (lhs.layout_type == layout_type::opaque) {
return lhs.layout.layout_id == rhs.layout.layout_id;
} else { return true;
}
} else {
const bool layout_may_implicit_equal
= (lhs.layout_type == layout_type::opaque
|| lhs.layout_type == layout_type::strided)
&& (rhs.layout_type == layout_type::opaque
|| rhs.layout_type == layout_type::strided);
if (!layout_may_implicit_equal) return false;
if (lhs.ndims <= 0) return true;
size_t layout_id = lhs.layout_type == layout_type::opaque
? lhs.layout.layout_id
: rhs.layout.layout_id;
auto backend
= backend_registry_t::get_singleton().get_registered_backend(
layout_id);
logical_tensor_t new_lt
= lhs.layout_type == layout_type::opaque ? lhs : rhs;
new_lt.layout.layout_id
= backend_registry_t::extract_layout_id(layout_id);
return lhs.layout_type == layout_type::opaque
? backend->compare_logical_tensor(new_lt, rhs)
: backend->compare_logical_tensor(lhs, new_lt);
}
return false;
}
size_t logical_tensor_wrapper_t::hash() const noexcept {
size_t seed = 0;
seed = hash_combine(seed, this->id());
const int32_t nd = this->ndims();
seed = nd > 0 ? partition_hashing::get_array_hash(seed, this->dims(), nd)
: hash_combine(seed, nd);
seed = hash_combine(seed, static_cast<size_t>(this->data_type()));
seed = hash_combine(seed, static_cast<size_t>(this->layout_type()));
switch (this->layout_type()) {
case layout_type::undef:
case layout_type::any: break;
case layout_type::strided:
if (nd > 0)
seed = partition_hashing::get_array_hash(
seed, this->strides(), nd);
break;
case layout_type::opaque:
seed = hash_combine(seed, this->layout_id());
break;
default: assertm(false, "unknown layout_type");
}
seed = hash_combine(seed, static_cast<size_t>(this->property_type()));
return seed;
}
std::string logical_tensor_wrapper_t::str() const {
dnnl::impl::stringstream_t ss;
ss << utils::data_type2str(data_type()) << ":";
ss << this->id() << ":";
ss << utils::layout_type2str(layout_type()) << ":";
ss << utils::property_type2str(property_type()) << ":";
ss << utils::dims2str(vdims()) << ":";
if (layout_type() == layout_type::strided) {
ss << utils::dims2str(vstrides());
} else if (layout_type() == layout_type::opaque) {
ss << layout_id();
} else if (layout_type() == layout_type::any) {
ss << "*";
} else {
assert(!"layout type must be any, strided or opaque.");
ss << "?";
}
return ss.str();
}
status_t DNNL_API dnnl_graph_logical_tensor_init(
logical_tensor_t *logical_tensor, size_t tid, data_type_t dtype,
int32_t ndims, layout_type_t ltype, property_type_t ptype) {
if (logical_tensor == nullptr || ndims > DNNL_MAX_NDIMS) {
return status::invalid_arguments;
}
if (ptype == property_type::host_scalar) {
VCHECK_LOGICAL_TENSOR(ndims == 0, status::invalid_arguments,
"host scalar tensor should have zero ndims");
VCHECK_LOGICAL_TENSOR(utils::one_of(dtype, data_type::s32,
data_type::f32, data_type::s64),
status::invalid_arguments,
"host scalar tensor should have s32, f32 or s64 dtype");
}
auto val = logical_tensor_t();
val.id = tid;
val.ndims = ndims;
val.data_type = dtype;
val.layout_type = ltype;
val.property = ptype;
if (ndims == 0) {
val.dims[0] = 0;
if (ltype == layout_type::strided) { val.layout.strides[0] = 0; }
} else {
std::fill(val.dims, val.dims + DNNL_MAX_NDIMS, DNNL_GRAPH_UNKNOWN_DIM);
std::fill(val.layout.strides, val.layout.strides + DNNL_MAX_NDIMS,
DNNL_GRAPH_UNKNOWN_DIM);
}
*logical_tensor = val;
return status::success;
}
status_t DNNL_API dnnl_graph_logical_tensor_init_with_dims(
logical_tensor_t *logical_tensor, size_t tid, data_type_t dtype,
int32_t ndims, const dims_t dims, layout_type_t ltype,
property_type_t ptype) {
if (!logical_tensor || ndims < 0) return status::invalid_arguments;
if (ptype == property_type::host_scalar) {
VCHECK_LOGICAL_TENSOR(ndims == 0, status::invalid_arguments,
"host scalar tensor should have zero ndims");
VCHECK_LOGICAL_TENSOR(utils::one_of(dtype, data_type::s32,
data_type::f32, data_type::s64),
status::invalid_arguments,
"host scalar tensor should have s32, f32 or s64 dtype");
}
auto val = logical_tensor_t();
val.id = tid;
val.ndims = ndims;
val.data_type = dtype;
val.layout_type = ltype;
val.property = ptype;
if (ndims == 0) {
val.dims[0] = 0;
if (ltype == layout_type::strided) { val.layout.strides[0] = 0; }
} else {
if (!dims) return status::invalid_arguments;
std::copy(dims, dims + ndims, val.dims);
bool sanity = true && std::all_of(dims, dims + ndims, [](int64_t v) {
return v >= 0;
});
if (sanity && ltype == layout_type::strided) {
val.layout.strides[ndims - 1] = 1;
for (int s = ndims - 2; s >= 0; --s) {
val.layout.strides[s] = std::max<dim_t>(dims[s + 1], 1)
* val.layout.strides[s + 1];
}
} else {
std::fill(val.layout.strides, val.layout.strides + DNNL_MAX_NDIMS,
DNNL_GRAPH_UNKNOWN_DIM);
}
}
*logical_tensor = val;
return status::success;
}
status_t DNNL_API dnnl_graph_logical_tensor_init_with_strides(
logical_tensor_t *logical_tensor, size_t tid, data_type_t dtype,
int32_t ndims, const dims_t dims, const dims_t strides,
property_type_t ptype) {
if (!logical_tensor || ndims < 0) return status::invalid_arguments;
if (ptype == property_type::host_scalar) {
VCHECK_LOGICAL_TENSOR(ndims == 0, status::invalid_arguments,
"host scalar tensor should have zero ndims");
VCHECK_LOGICAL_TENSOR(utils::one_of(dtype, data_type::s32,
data_type::f32, data_type::s64),
status::invalid_arguments,
"host scalar tensor should have s32, f32 or s64 dtype");
}
auto val = logical_tensor_t();
val.id = tid;
val.ndims = ndims;
val.data_type = dtype;
val.layout_type = layout_type::strided;
val.property = ptype;
if (ndims == 0) {
val.dims[0] = 0;
val.layout.strides[0] = 0;
} else {
if (utils::any_null(dims, strides)) return status::invalid_arguments;
std::copy(dims, dims + ndims, val.dims);
std::copy(strides, strides + ndims, val.layout.strides);
}
*logical_tensor = val;
return status::success;
}
status_t DNNL_API dnnl_graph_logical_tensor_get_mem_size(
const logical_tensor_t *logical_tensor, size_t *size) {
if (utils::any_null(logical_tensor, size)) return status::invalid_arguments;
*size = logical_tensor_wrapper_t(logical_tensor).size();
return status::success;
}
status_t DNNL_API dnnl_graph_logical_tensor_is_equal(
const logical_tensor_t *lt1, const logical_tensor_t *lt2,
uint8_t *is_equal) {
if (utils::any_null(lt1, lt2, is_equal)) return status::invalid_arguments;
logical_tensor_wrapper_t ltw1 {lt1};
logical_tensor_wrapper_t ltw2 {lt2};
*is_equal = (ltw1 == ltw2) ? 1 : 0;
return status::success;
}