/******************************************************************************* * * MIT License * * Copyright (C) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * *******************************************************************************/ #include #include #include #include #include using namespace Tensile; using namespace Tensile::Client; namespace po = Tensile::Client::po; template class DataInitializationTest : public ::testing::Test { public: using val = po::variable_value; using AType = typename TypedInputs::AType; using BType = typename TypedInputs::BType; using CType = typename TypedInputs::CType; using DType = typename TypedInputs::DType; using AlphaType = typename TypedInputs::AlphaType; using BetaType = typename TypedInputs::BetaType; using TestDataInitialization = TypedDataInitialization; using ManagedInputs = typename TestDataInitialization::ManagedInputs; static_assert(std::is_same::value, "inconsistent types"); static_assert(std::is_same::value, "inconsistent types"); static_assert(std::is_same::value, "inconsistent types"); static_assert(std::is_same::value, "inconsistent types"); static_assert(std::is_same::value, "inconsistent types"); static_assert(std::is_same::value, "inconsistent types"); po::variables_map DataTypeArgs() { po::variables_map rv; rv.insert({"a-type", val(std::any(TypeInfo::Enum))}); rv.insert({"b-type", val(std::any(TypeInfo::Enum))}); rv.insert({"c-type", val(std::any(TypeInfo::Enum))}); rv.insert({"d-type", val(std::any(TypeInfo::Enum))}); rv.insert({"alpha-type", val(std::any(TypeInfo::Enum))}); rv.insert({"beta-type", val(std::any(TypeInfo::Enum))}); return rv; } void RunDataContaminationTest(bool cEqualD, bool pristineGPU, BoundsCheckMode boundsCheck) { using val = po::variable_value; po::variables_map args = this->DataTypeArgs(); args.insert({"init-a", val(std::any(InitMode::Zero))}); args.insert({"init-b", val(std::any(InitMode::Zero))}); args.insert({"init-c", val(std::any(InitMode::Zero))}); args.insert({"init-d", val(std::any(InitMode::Zero))}); args.insert({"init-alpha", val(std::any(InitMode::Zero))}); args.insert({"init-beta", val(std::any(InitMode::Zero))}); args.insert({"c-equal-d", val(std::any(cEqualD))}); args.insert({"pristine-on-gpu", val(std::any(pristineGPU))}); args.insert({"bounds-check", val(std::any(boundsCheck))}); args.insert({"num-elements-to-validate", val(std::any(1))}); args.insert({"offset-a", val(std::any((size_t)0))}); args.insert({"offset-b", val(std::any((size_t)0))}); args.insert({"offset-c", val(std::any((size_t)0))}); args.insert({"offset-d", val(std::any((size_t)0))}); args.insert({"strided-batched", val(std::any(false))}); TensorDescriptor a(TypeInfo::Enum, {10, 10, 1}); TensorDescriptor b(TypeInfo::Enum, {10, 10, 1}); TensorDescriptor c(TypeInfo::Enum, {10, 10, 1}); TensorDescriptor d(TypeInfo::Enum, {10, 10, 1}); TensorOps nop; auto problem = ContractionProblem::GEMM(false, false, a, nop, b, nop, c, nop, d, nop, 1.5); ClientProblemFactory factory(problem); auto init = DataInitialization::Get(args, factory); auto genericInputs = init->prepareCPUInputs(problem); auto cpuInputs = std::dynamic_pointer_cast(genericInputs); if(cpuInputs == nullptr) ASSERT_NE(cpuInputs, nullptr); if(cEqualD) EXPECT_EQ(cpuInputs->c, cpuInputs->d); else EXPECT_NE(cpuInputs->c, cpuInputs->d); auto zero = DataInitialization::getValue(); for(size_t i = 0; i < d.totalAllocatedElements(); i++) EXPECT_EQ(cpuInputs->d[i], zero) << i; for(size_t i = 0; i < d.totalAllocatedElements(); i++) cpuInputs->d[i] = DataInitialization::getValue(); cpuInputs = std::dynamic_pointer_cast(init->prepareCPUInputs(problem)); for(size_t i = 0; i < d.totalAllocatedElements(); i++) EXPECT_EQ(cpuInputs->d[i], zero) << i; auto gpuInputs = std::dynamic_pointer_cast(init->prepareGPUInputs(problem)); std::vector gpuD(d.totalAllocatedElements()); auto memcpyStatus = hipMemcpy(gpuD.data(), gpuInputs->d, d.totalAllocatedBytes(), hipMemcpyDeviceToHost); EXPECT_EQ(memcpyStatus, hipSuccess); for(size_t i = 0; i < d.totalAllocatedElements(); i++) EXPECT_EQ(gpuD[i], zero) << i; } }; using InputTypes = ::testing::Types, TypedContractionInputs, TypedContractionInputs, ContractionInputs_B_B_S, TypedContractionInputs>, TypedContractionInputs>, TypedContractionInputs, TypedContractionInputs>; TYPED_TEST_SUITE(DataInitializationTest, InputTypes); /* * Unfortunately, googletest doesn't support tests that are both templated AND * parameterized, so this is the chosen compromise. */ TYPED_TEST(DataInitializationTest, Contamination_false_false_false) { this->RunDataContaminationTest(false, false, BoundsCheckMode::Disable); } TYPED_TEST(DataInitializationTest, Contamination_false_true_false) { this->RunDataContaminationTest(false, true, BoundsCheckMode::Disable); } TYPED_TEST(DataInitializationTest, Contamination_true_false_false) { this->RunDataContaminationTest(true, false, BoundsCheckMode::Disable); } TYPED_TEST(DataInitializationTest, Contamination_true_true_false) { this->RunDataContaminationTest(true, true, BoundsCheckMode::Disable); } TYPED_TEST(DataInitializationTest, Contamination_false_false_true) { this->RunDataContaminationTest(false, false, BoundsCheckMode::NaN); } TYPED_TEST(DataInitializationTest, Contamination_false_true_true) { this->RunDataContaminationTest(false, true, BoundsCheckMode::NaN); } TYPED_TEST(DataInitializationTest, Contamination_true_false_true) { this->RunDataContaminationTest(true, false, BoundsCheckMode::NaN); } TYPED_TEST(DataInitializationTest, Contamination_true_true_true) { this->RunDataContaminationTest(true, true, BoundsCheckMode::NaN); } template struct DataInitializationTestFloating : public ::testing::Test { using TestType = typename std::tuple_element<0, T>::type; using ComparisonType = TestType; }; template <> struct DataInitializationTestFloating> : public ::testing::Test { using TestType = Half; using ComparisonType = float; }; // Typeinfo is not present for Half so wrap it in a tuple to avoid a missing // symbol. using FloatingPointTypes = ::testing:: Types, std::tuple, std::tuple, std::tuple>; TYPED_TEST_SUITE(DataInitializationTestFloating, FloatingPointTypes); TYPED_TEST(DataInitializationTestFloating, Simple) { using Type = typename TestFixture::TestType; using Comparison = typename TestFixture::ComparisonType; Type value(1.0); EXPECT_EQ(DataInitialization::isBadInput(value), false); EXPECT_EQ(DataInitialization::isBadOutput(value), false); value = DataInitialization::getValue(InitMode::BadInput); EXPECT_EQ(std::isnan(static_cast(value)), true) << value; EXPECT_EQ(DataInitialization::isBadInput(value), true) << value; EXPECT_EQ(DataInitialization::isBadOutput(value), false) << value; value = DataInitialization::getValue(InitMode::BadOutput); EXPECT_EQ(std::isinf(static_cast(value)), true) << value; EXPECT_EQ(DataInitialization::isBadInput(value), false) << value; EXPECT_EQ(DataInitialization::isBadOutput(value), true) << value; } template struct DataInitializationTestComplex : public ::testing::Test { }; using ComplexTypes = ::testing::Types, std::complex>; TYPED_TEST_SUITE(DataInitializationTestComplex, ComplexTypes); TYPED_TEST(DataInitializationTestComplex, Simple) { TypeParam value(1, 1); EXPECT_EQ(DataInitialization::isBadInput(value), false); EXPECT_EQ(DataInitialization::isBadOutput(value), false); value = DataInitialization::getValue(InitMode::BadInput); EXPECT_EQ(std::isnan(value.real()), true); EXPECT_EQ(std::isnan(value.imag()), true); EXPECT_EQ(DataInitialization::isBadInput(value), true); EXPECT_EQ(DataInitialization::isBadOutput(value), false); value = DataInitialization::getValue(InitMode::BadOutput); EXPECT_EQ(std::isinf(value.real()), true); EXPECT_EQ(std::isinf(value.imag()), true); EXPECT_EQ(DataInitialization::isBadInput(value), false); EXPECT_EQ(DataInitialization::isBadOutput(value), true); } TEST(DataInitializationTest, BadValues_int32) { int32_t value = 1; EXPECT_EQ(DataInitialization::isBadInput(value), false); EXPECT_EQ(DataInitialization::isBadOutput(value), false); value = DataInitialization::getValue(InitMode::BadInput); EXPECT_EQ(std::numeric_limits::max(), value); EXPECT_EQ(DataInitialization::isBadInput(value), true); EXPECT_EQ(DataInitialization::isBadOutput(value), false); value = DataInitialization::getValue(InitMode::BadOutput); EXPECT_EQ(std::numeric_limits::min(), value); EXPECT_EQ(DataInitialization::isBadInput(value), false); EXPECT_EQ(DataInitialization::isBadOutput(value), true); } TEST(DataInitializationTest, BadValues_Int8x4) { auto maxval = std::numeric_limits::max(); auto minval = std::numeric_limits::min(); EXPECT_EQ(Int8x4(maxval, maxval, maxval, maxval), DataInitialization::getValue(InitMode::BadInput)); EXPECT_EQ(Int8x4(minval, minval, minval, minval), DataInitialization::getValue(InitMode::BadOutput)); }