mirror of
https://github.com/uxlfoundation/oneDNN.git
synced 2025-10-20 18:43:49 +08:00
409 lines
13 KiB
C++
409 lines
13 KiB
C++
/*******************************************************************************
|
|
* Copyright 2022-2025 Intel Corporation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*******************************************************************************/
|
|
|
|
#include "dnnl_test_common.hpp"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "oneapi/dnnl/dnnl.hpp"
|
|
|
|
namespace dnnl {
|
|
|
|
using dt = memory::data_type;
|
|
|
|
class iface_sparse_test_t : public ::testing::Test {};
|
|
|
|
TEST(iface_sparse_test_t, TestSparseMDCreation) {
|
|
const int nnz = 12;
|
|
memory::desc md;
|
|
// CSR.
|
|
ASSERT_NO_THROW(
|
|
md = memory::desc::csr({64, 128}, dt::f32, nnz, dt::s32, dt::s32));
|
|
// COO.
|
|
ASSERT_NO_THROW(md = memory::desc::coo({64, 128}, dt::f32, nnz, dt::s32));
|
|
// Packed.
|
|
ASSERT_NO_THROW(md = memory::desc::packed({64, 128}, dt::f32, nnz));
|
|
}
|
|
|
|
TEST(iface_sparse_test_t, TestSparseMDComparison) {
|
|
const int nnz = 12;
|
|
memory::desc md1;
|
|
memory::desc md2;
|
|
|
|
// CSR.
|
|
|
|
// Different index data types.
|
|
ASSERT_NO_THROW(
|
|
md1 = memory::desc::csr({64, 128}, dt::f32, nnz, dt::s32, dt::s32));
|
|
ASSERT_NO_THROW(
|
|
md2 = memory::desc::csr({64, 128}, dt::f32, nnz, dt::s8, dt::s32));
|
|
ASSERT_NE(md1, md2);
|
|
|
|
// Different pointer data types.
|
|
ASSERT_NO_THROW(
|
|
md1 = memory::desc::csr({64, 128}, dt::f32, nnz, dt::s32, dt::s32));
|
|
ASSERT_NO_THROW(
|
|
md2 = memory::desc::csr({64, 128}, dt::f32, nnz, dt::s32, dt::s8));
|
|
ASSERT_NE(md1, md2);
|
|
|
|
// Different nnz.
|
|
ASSERT_NO_THROW(
|
|
md1 = memory::desc::csr({64, 128}, dt::f32, nnz, dt::s32, dt::s32));
|
|
ASSERT_NO_THROW(md2
|
|
= memory::desc::csr({64, 128}, dt::f32, nnz + 1, dt::s32, dt::s32));
|
|
ASSERT_NE(md1, md2);
|
|
|
|
// COO.
|
|
|
|
// Different value data types.
|
|
ASSERT_NO_THROW(md1 = memory::desc::coo({64, 128}, dt::f32, nnz, dt::s32));
|
|
ASSERT_NO_THROW(md2 = memory::desc::coo({64, 128}, dt::f16, nnz, dt::s32));
|
|
ASSERT_NE(md1, md2);
|
|
|
|
// Different index data types.
|
|
ASSERT_NO_THROW(md1 = memory::desc::coo({64, 128}, dt::f32, nnz, dt::s32));
|
|
ASSERT_NO_THROW(md2 = memory::desc::coo({64, 128}, dt::f32, nnz, dt::s8));
|
|
ASSERT_NE(md1, md2);
|
|
|
|
// Different nnz.
|
|
ASSERT_NO_THROW(md1 = memory::desc::coo({64, 128}, dt::f32, nnz, dt::s32));
|
|
ASSERT_NO_THROW(
|
|
md2 = memory::desc::coo({64, 128}, dt::f32, nnz + 1, dt::s32));
|
|
ASSERT_NE(md1, md2);
|
|
|
|
// Packed.
|
|
|
|
// Equal memory descriptors.
|
|
ASSERT_NO_THROW(md1 = memory::desc::packed({64, 128}, dt::f32, nnz));
|
|
ASSERT_NO_THROW(md2 = memory::desc::packed({64, 128}, dt::f32, nnz));
|
|
ASSERT_EQ(md1, md2);
|
|
|
|
// Different nnz.
|
|
ASSERT_NO_THROW(md1 = memory::desc::packed({64, 128}, dt::f32, nnz));
|
|
ASSERT_NO_THROW(md2 = memory::desc::packed({64, 128}, dt::f32, nnz + 1));
|
|
ASSERT_NE(md1, md2);
|
|
}
|
|
|
|
TEST(iface_sparse_test_t, TestSparseMDQueries) {
|
|
const int nnz = 12;
|
|
const auto indices_dt = dt::s8;
|
|
const auto pointers_dt = dt::s32;
|
|
const memory::dims dims = {64, 128};
|
|
const memory::data_type data_type = dt::f32;
|
|
|
|
memory::desc md;
|
|
|
|
// CSR.
|
|
ASSERT_NO_THROW(md
|
|
= memory::desc::csr(dims, data_type, nnz, indices_dt, pointers_dt));
|
|
ASSERT_EQ(md.get_dims(), dims);
|
|
ASSERT_EQ(md.get_data_type(), data_type);
|
|
ASSERT_EQ(md.get_data_type(0), data_type);
|
|
ASSERT_EQ(md.get_format_kind(), memory::format_kind::sparse);
|
|
|
|
ASSERT_EQ(md.get_nnz(), nnz);
|
|
ASSERT_EQ(md.get_sparse_encoding(), memory::sparse_encoding::csr);
|
|
ASSERT_EQ(md.get_data_type(1), indices_dt);
|
|
ASSERT_EQ(md.get_data_type(2), pointers_dt);
|
|
|
|
// COO.
|
|
ASSERT_NO_THROW(md = memory::desc::coo(dims, data_type, nnz, indices_dt));
|
|
ASSERT_EQ(md.get_dims(), dims);
|
|
ASSERT_EQ(md.get_data_type(), data_type);
|
|
ASSERT_EQ(md.get_data_type(0), data_type);
|
|
ASSERT_EQ(md.get_format_kind(), memory::format_kind::sparse);
|
|
|
|
ASSERT_EQ(md.get_nnz(), nnz);
|
|
ASSERT_EQ(md.get_sparse_encoding(), memory::sparse_encoding::coo);
|
|
ASSERT_EQ(md.get_data_type(1), indices_dt);
|
|
ASSERT_EQ(md.get_data_type(2), indices_dt);
|
|
|
|
// Packed.
|
|
ASSERT_NO_THROW(md = memory::desc::packed(dims, data_type, nnz));
|
|
ASSERT_EQ(md.get_dims(), dims);
|
|
ASSERT_EQ(md.get_data_type(), data_type);
|
|
ASSERT_EQ(md.get_data_type(0), data_type);
|
|
ASSERT_EQ(md.get_format_kind(), memory::format_kind::sparse);
|
|
|
|
ASSERT_EQ(md.get_nnz(), nnz);
|
|
ASSERT_EQ(md.get_sparse_encoding(), memory::sparse_encoding::packed);
|
|
}
|
|
|
|
TEST(iface_sparse_test_t, TestSparseMDSize) {
|
|
const int nnz = 12;
|
|
memory::desc md;
|
|
|
|
// CSR.
|
|
ASSERT_NO_THROW(
|
|
md = memory::desc::csr({64, 128}, dt::f32, nnz, dt::s32, dt::s32));
|
|
// Size of values.
|
|
size_t exp_values_size = nnz * memory::data_type_size(md.get_data_type());
|
|
// Default.
|
|
ASSERT_EQ(md.get_size(), exp_values_size);
|
|
// Explicit.
|
|
ASSERT_EQ(md.get_size(0), exp_values_size);
|
|
|
|
// Size of indices.
|
|
size_t exp_indices_size = nnz * memory::data_type_size(md.get_data_type(1));
|
|
ASSERT_EQ(md.get_size(1), exp_indices_size);
|
|
|
|
// Size of pointers.
|
|
size_t exp_pointers_size = (md.get_dims()[0] + 1)
|
|
* memory::data_type_size(md.get_data_type(2));
|
|
ASSERT_EQ(md.get_size(2), exp_pointers_size);
|
|
|
|
// COO.
|
|
ASSERT_NO_THROW(md = memory::desc::coo({64, 128}, dt::f32, nnz, dt::s32));
|
|
// Size of values.
|
|
exp_values_size = nnz * memory::data_type_size(md.get_data_type());
|
|
// Default.
|
|
ASSERT_EQ(md.get_size(), exp_values_size);
|
|
// Explicit.
|
|
ASSERT_EQ(md.get_size(0), exp_values_size);
|
|
|
|
// Size of indices.
|
|
exp_indices_size = nnz * memory::data_type_size(md.get_data_type(1));
|
|
ASSERT_EQ(md.get_size(1), exp_indices_size);
|
|
ASSERT_EQ(md.get_size(2), exp_indices_size);
|
|
|
|
// Packed.
|
|
|
|
// The user-created memory descriptor for packed encoding cannot
|
|
// be queried for sizes.
|
|
ASSERT_NO_THROW(md = memory::desc::packed({64, 128}, dt::f32, nnz));
|
|
// Size of values.
|
|
// Default.
|
|
ASSERT_EQ(md.get_size(), 0u);
|
|
// Explicit.
|
|
ASSERT_EQ(md.get_size(0), 0u);
|
|
|
|
// Size of offsets.
|
|
ASSERT_EQ(md.get_size(1), 0u);
|
|
|
|
// Size of bitmask.
|
|
ASSERT_EQ(md.get_size(2), 0u);
|
|
}
|
|
|
|
HANDLE_EXCEPTIONS_FOR_TEST(iface_sparse_test_t, TestSparseMemoryCreation) {
|
|
engine eng = get_test_engine();
|
|
|
|
const bool is_unimplemented = (eng.get_kind() == engine::kind::gpu
|
|
|| DNNL_CPU_RUNTIME == DNNL_RUNTIME_SYCL);
|
|
if (is_unimplemented) return;
|
|
|
|
const int nnz = 12;
|
|
memory::desc md;
|
|
|
|
// CSR.
|
|
ASSERT_NO_THROW(
|
|
md = memory::desc::csr({64, 128}, dt::f32, nnz, dt::s32, dt::s32));
|
|
memory mem;
|
|
|
|
// Default memory constructor.
|
|
mem = memory(md, eng);
|
|
|
|
// User provided buffers.
|
|
{
|
|
std::vector<float> values(1);
|
|
std::vector<int> indices(1);
|
|
std::vector<int> pointers(1);
|
|
EXPECT_NO_THROW(
|
|
mem = memory(md, eng,
|
|
{values.data(), indices.data(), pointers.data()}));
|
|
}
|
|
|
|
// COO.
|
|
ASSERT_NO_THROW(md = memory::desc::coo({64, 128}, dt::f32, nnz, dt::s32));
|
|
|
|
// Default memory constructor.
|
|
mem = memory(md, eng);
|
|
// User provided buffers.
|
|
{
|
|
std::vector<float> values(1);
|
|
std::vector<int> row_indices(1);
|
|
std::vector<int> col_indices(1);
|
|
|
|
EXPECT_NO_THROW(mem = memory(md, eng,
|
|
{values.data(), row_indices.data(),
|
|
col_indices.data()}));
|
|
}
|
|
}
|
|
|
|
HANDLE_EXCEPTIONS_FOR_TEST(
|
|
iface_sparse_test_t, TestSparseMemorySetGetDataHandles) {
|
|
engine eng = get_test_engine();
|
|
|
|
const bool is_unimplemented = (eng.get_kind() == engine::kind::gpu
|
|
|| DNNL_CPU_RUNTIME == DNNL_RUNTIME_SYCL);
|
|
if (is_unimplemented) return;
|
|
|
|
const int nnz = 12;
|
|
|
|
// CSR.
|
|
memory::desc md;
|
|
ASSERT_NO_THROW(
|
|
md = memory::desc::csr({64, 128}, dt::f32, nnz, dt::s32, dt::s32));
|
|
memory mem;
|
|
|
|
int nhandles = 3;
|
|
// Default memory constructor.
|
|
mem = memory(md, eng);
|
|
|
|
{
|
|
for (int i = 0; i < nhandles; i++) {
|
|
void *h = mem.get_data_handle(i);
|
|
ASSERT_NE(h, nullptr);
|
|
}
|
|
|
|
// Creating a memory object without underlying buffers.
|
|
for (int i = 0; i < nhandles; i++) {
|
|
EXPECT_NO_THROW(mem.set_data_handle(DNNL_MEMORY_NONE, i));
|
|
}
|
|
|
|
for (int i = 0; i < nhandles; i++) {
|
|
void *h = mem.get_data_handle(i);
|
|
ASSERT_EQ(h, nullptr);
|
|
}
|
|
}
|
|
|
|
// User provided buffers.
|
|
{
|
|
std::vector<float> values(1);
|
|
std::vector<int> indices(1);
|
|
std::vector<int> pointers(1);
|
|
|
|
ASSERT_NO_THROW(mem.set_data_handle(values.data(), 0));
|
|
ASSERT_NO_THROW(mem.set_data_handle(indices.data(), 1));
|
|
ASSERT_NO_THROW(mem.set_data_handle(pointers.data(), 2));
|
|
|
|
ASSERT_EQ(mem.get_data_handle(0), values.data());
|
|
ASSERT_EQ(mem.get_data_handle(1), indices.data());
|
|
ASSERT_EQ(mem.get_data_handle(2), pointers.data());
|
|
}
|
|
|
|
// COO.
|
|
ASSERT_NO_THROW(md = memory::desc::coo({64, 128}, dt::f32, nnz, dt::s32));
|
|
|
|
// Default memory constructor.
|
|
mem = memory(md, eng);
|
|
|
|
{
|
|
for (int i = 0; i < nhandles; i++) {
|
|
void *h = mem.get_data_handle(i);
|
|
ASSERT_NE(h, nullptr);
|
|
}
|
|
|
|
// Creating a memory object without underlying buffers.
|
|
for (int i = 0; i < nhandles; i++) {
|
|
EXPECT_NO_THROW(mem.set_data_handle(DNNL_MEMORY_NONE, i));
|
|
}
|
|
|
|
for (int i = 0; i < nhandles; i++) {
|
|
void *h = mem.get_data_handle(i);
|
|
ASSERT_EQ(h, nullptr);
|
|
}
|
|
}
|
|
|
|
// User provided buffers.
|
|
{
|
|
std::vector<float> values(1);
|
|
std::vector<int> row_indices(1);
|
|
std::vector<int> col_indices(1);
|
|
|
|
ASSERT_NO_THROW(mem.set_data_handle(values.data(), 0));
|
|
ASSERT_NO_THROW(mem.set_data_handle(row_indices.data(), 1));
|
|
ASSERT_NO_THROW(mem.set_data_handle(col_indices.data(), 2));
|
|
|
|
ASSERT_EQ(mem.get_data_handle(0), values.data());
|
|
ASSERT_EQ(mem.get_data_handle(1), row_indices.data());
|
|
ASSERT_EQ(mem.get_data_handle(2), col_indices.data());
|
|
}
|
|
}
|
|
|
|
TEST(iface_sparse_test_t, TestSparseMemoryMapUnmap) {
|
|
engine eng = get_test_engine();
|
|
|
|
const bool is_unimplemented = (eng.get_kind() == engine::kind::gpu
|
|
|| DNNL_CPU_RUNTIME == DNNL_RUNTIME_SYCL);
|
|
if (is_unimplemented) return;
|
|
|
|
const int nnz = 2;
|
|
|
|
// CSR.
|
|
memory::desc md;
|
|
ASSERT_NO_THROW(
|
|
md = memory::desc::csr({2, 2}, dt::f32, nnz, dt::s32, dt::s32));
|
|
|
|
std::vector<float> values = {1.5, 2.5};
|
|
std::vector<int> indices = {0, 1};
|
|
std::vector<int> pointers = {0, 1, 2};
|
|
|
|
memory mem(md, eng, {values.data(), indices.data(), pointers.data()});
|
|
|
|
float *mapped_values = nullptr;
|
|
int *mapped_indices = nullptr;
|
|
int *mapped_pointers = nullptr;
|
|
|
|
ASSERT_NO_THROW(mapped_values = mem.map_data<float>(0));
|
|
ASSERT_NO_THROW(mapped_indices = mem.map_data<int>(1));
|
|
ASSERT_NO_THROW(mapped_pointers = mem.map_data<int>(2));
|
|
|
|
for (size_t i = 0; i < values.size(); i++)
|
|
ASSERT_EQ(values[i], mapped_values[i]);
|
|
|
|
for (size_t i = 0; i < indices.size(); i++)
|
|
ASSERT_EQ(indices[i], mapped_indices[i]);
|
|
|
|
for (size_t i = 0; i < pointers.size(); i++)
|
|
ASSERT_EQ(pointers[i], mapped_pointers[i]);
|
|
|
|
ASSERT_NO_THROW(mem.unmap_data(mapped_values, 0));
|
|
ASSERT_NO_THROW(mem.unmap_data(mapped_indices, 1));
|
|
ASSERT_NO_THROW(mem.unmap_data(mapped_pointers, 2));
|
|
|
|
// COO.
|
|
ASSERT_NO_THROW(md = memory::desc::coo({2, 2}, dt::f32, nnz, dt::s32));
|
|
|
|
std::vector<float> coo_values = {1.5, 2.5};
|
|
std::vector<int> row_indices = {0, 1};
|
|
std::vector<int> col_indices = {0, 1};
|
|
|
|
memory coo_mem(md, eng,
|
|
{coo_values.data(), row_indices.data(), col_indices.data()});
|
|
|
|
float *mapped_coo_values = nullptr;
|
|
int *mapped_row_indices = nullptr;
|
|
int *mapped_col_indices = nullptr;
|
|
|
|
ASSERT_NO_THROW(mapped_coo_values = coo_mem.map_data<float>(0));
|
|
ASSERT_NO_THROW(mapped_row_indices = coo_mem.map_data<int>(1));
|
|
ASSERT_NO_THROW(mapped_col_indices = coo_mem.map_data<int>(2));
|
|
|
|
for (size_t i = 0; i < coo_values.size(); i++)
|
|
ASSERT_EQ(values[i], mapped_values[i]);
|
|
|
|
for (size_t i = 0; i < row_indices.size(); i++)
|
|
ASSERT_EQ(row_indices[i], mapped_row_indices[i]);
|
|
|
|
for (size_t i = 0; i < col_indices.size(); i++)
|
|
ASSERT_EQ(col_indices[i], mapped_col_indices[i]);
|
|
|
|
ASSERT_NO_THROW(mem.unmap_data(mapped_coo_values, 0));
|
|
ASSERT_NO_THROW(mem.unmap_data(mapped_row_indices, 1));
|
|
ASSERT_NO_THROW(mem.unmap_data(mapped_col_indices, 2));
|
|
}
|
|
|
|
} // namespace dnnl
|