CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
827 lines
31 KiB
C++
827 lines
31 KiB
C++
// Copyright (c) 2018 - 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 "hipfft/hipfft.h"
|
|
#include <fftw3.h>
|
|
#include <gtest/gtest.h>
|
|
#include <hip/hip_vector_types.h>
|
|
#include <random>
|
|
#include <vector>
|
|
|
|
#include "../../shared/fftw_transform.h"
|
|
#include "../../shared/params_gen.h"
|
|
#include "../hipfft_params.h"
|
|
|
|
DISABLE_WARNING_PUSH
|
|
DISABLE_WARNING_DEPRECATED_DECLARATIONS
|
|
DISABLE_WARNING_RETURN_TYPE
|
|
#include <hip/hip_runtime_api.h>
|
|
DISABLE_WARNING_POP
|
|
|
|
// Function to return maximum error for float and double types for
|
|
// the simple tests below
|
|
template <typename Tfloat>
|
|
inline double type_epsilon_simple();
|
|
template <>
|
|
inline double type_epsilon_simple<float>()
|
|
{
|
|
return 1e-6;
|
|
}
|
|
template <>
|
|
inline double type_epsilon_simple<double>()
|
|
{
|
|
return 1e-7;
|
|
}
|
|
|
|
/* Static utility class template helping with the definition of valid/invalid
|
|
values for arguments of (un)scoped enumeration types in API testing.
|
|
Usage: for an enumeration type of interest, say "enum_of_interest", define
|
|
template <>
|
|
const std::vector<enum_of_interest> enum_helper<enum_of_interest>::valid_values =
|
|
{all, the, known, valid, values, of, type, enum_of_interest};
|
|
before using any of this class' self-explanatory public member functions.
|
|
*/
|
|
template <typename T, std::enable_if_t<std::is_enum_v<T>, bool> = true>
|
|
class enum_helper
|
|
{
|
|
using base_t = typename std::underlying_type<T>::type;
|
|
// static class cannot be instantiated, copied, or moved
|
|
enum_helper() = delete;
|
|
~enum_helper() = delete;
|
|
enum_helper(const enum_helper&) = delete;
|
|
enum_helper(enum_helper&& other) = delete;
|
|
enum_helper& operator=(const enum_helper&) = delete;
|
|
enum_helper& operator=(enum_helper&& other) = delete;
|
|
|
|
public:
|
|
static const std::vector<T> valid_values;
|
|
|
|
static bool has_value(const T& val)
|
|
{
|
|
return std::any_of(
|
|
valid_values.begin(), valid_values.end(), [&](const T& v) { return v == val; });
|
|
}
|
|
|
|
static size_t num_valid_values()
|
|
{
|
|
return valid_values.size();
|
|
}
|
|
static T get_any_valid_value(size_t prng_seed = 0)
|
|
{
|
|
const size_t nvals = num_valid_values();
|
|
if(nvals == 0)
|
|
throw std::runtime_error(
|
|
"enum_helper::get_any_valid_value: no valid value is defined.");
|
|
std::ranlux24_base gen(prng_seed);
|
|
return valid_values[static_cast<size_t>(gen()) % nvals];
|
|
}
|
|
|
|
static T get_invalid_value(size_t prng_seed = 0)
|
|
{
|
|
constexpr base_t max_base_val = std::numeric_limits<base_t>::max();
|
|
constexpr base_t min_base_val = std::numeric_limits<base_t>::min();
|
|
|
|
std::ranlux24_base gen(prng_seed);
|
|
std::uniform_int_distribution<base_t> dis(min_base_val, max_base_val);
|
|
// limit number of attempts to 10x the number of possible value
|
|
size_t num_attempts = 0;
|
|
T made_up_value;
|
|
auto generate_candidate = [&]() {
|
|
num_attempts++;
|
|
made_up_value = static_cast<T>(dis(gen));
|
|
return;
|
|
};
|
|
generate_candidate();
|
|
while(has_value(made_up_value) && num_attempts < 10 * num_valid_values())
|
|
{
|
|
generate_candidate();
|
|
}
|
|
if(has_value(made_up_value))
|
|
throw std::runtime_error(
|
|
"enum_helper::get_invalid_value failed to generate an invalid valid");
|
|
return made_up_value;
|
|
}
|
|
};
|
|
|
|
// definition of valid values for various enum types
|
|
template <>
|
|
const std::vector<hipfftLibraryPropertyType> enum_helper<hipfftLibraryPropertyType>::valid_values
|
|
= {hipfftLibraryPropertyType::HIPFFT_MAJOR_VERSION,
|
|
hipfftLibraryPropertyType::HIPFFT_MINOR_VERSION,
|
|
hipfftLibraryPropertyType::HIPFFT_PATCH_LEVEL};
|
|
|
|
TEST(hipfftTest, Create1dPlan)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
size_t length = 1024;
|
|
ASSERT_EQ(hipfftPlan1d(&plan, length, HIPFFT_C2C, 1), HIPFFT_SUCCESS);
|
|
|
|
ASSERT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
TEST(hipfftTest, CreatePlanMany)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
int const rank = 3;
|
|
int const nX = 64;
|
|
int const nY = 128;
|
|
int const nZ = 23;
|
|
int n[3] = {nX, nY, nZ};
|
|
int inembed[3] = {nX, nY, nZ};
|
|
int* inembed_null = nullptr;
|
|
int const istride = 1;
|
|
int const idist = nX * nY * nZ;
|
|
int onembed[3] = {nX, nY, nZ};
|
|
int* onembed_null = nullptr;
|
|
int const ostride = 1;
|
|
int const odist = nX * nY * nZ;
|
|
hipfftType type = HIPFFT_C2C;
|
|
int const batch = 1000;
|
|
size_t workSize;
|
|
|
|
// Tests plan creation with null and not null
|
|
// combinations of inembed and onembed.
|
|
//
|
|
// Valid combinations:
|
|
// inembed == null && onembed == null
|
|
// or
|
|
// inembed != null && onembed != null
|
|
//
|
|
// otherwise HIPFFT_INVALID_VALUE should be
|
|
// returned to maintain compatibility with cuFFT
|
|
|
|
// inembed == null && onembed == null
|
|
{
|
|
hipfftHandle plan_valid_1 = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan_valid_1), HIPFFT_SUCCESS);
|
|
auto ret_hipfft = hipfftMakePlanMany(plan_valid_1,
|
|
rank,
|
|
(int*)n,
|
|
inembed_null,
|
|
istride,
|
|
idist,
|
|
onembed_null,
|
|
ostride,
|
|
odist,
|
|
type,
|
|
batch,
|
|
&workSize);
|
|
ASSERT_EQ(ret_hipfft, HIPFFT_SUCCESS)
|
|
<< "inembed == null && onembed == null failed: " << hipfftResult_string(ret_hipfft);
|
|
ASSERT_EQ(hipfftSetAutoAllocation(plan_valid_1, 0), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftDestroy(plan_valid_1), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
// inembed != null && onembed != null
|
|
{
|
|
hipfftHandle plan_valid_2 = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan_valid_2), HIPFFT_SUCCESS);
|
|
auto ret_hipfft = hipfftMakePlanMany(plan_valid_2,
|
|
rank,
|
|
(int*)n,
|
|
inembed,
|
|
istride,
|
|
idist,
|
|
onembed,
|
|
ostride,
|
|
odist,
|
|
type,
|
|
batch,
|
|
&workSize);
|
|
ASSERT_EQ(ret_hipfft, HIPFFT_SUCCESS)
|
|
<< "inembed != null && onembed != null failed: " << hipfftResult_string(ret_hipfft);
|
|
ASSERT_EQ(hipfftSetAutoAllocation(plan_valid_2, 0), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftDestroy(plan_valid_2), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
// inembed != null && onembed == null
|
|
{
|
|
hipfftHandle plan_invalid_1 = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan_invalid_1), HIPFFT_SUCCESS);
|
|
auto ret_hipfft = hipfftMakePlanMany(plan_invalid_1,
|
|
rank,
|
|
(int*)n,
|
|
inembed,
|
|
istride,
|
|
idist,
|
|
onembed_null,
|
|
ostride,
|
|
odist,
|
|
type,
|
|
batch,
|
|
&workSize);
|
|
ASSERT_EQ(ret_hipfft, HIPFFT_INVALID_VALUE)
|
|
<< "inembed != null && onembed == null failed: " << hipfftResult_string(ret_hipfft);
|
|
ASSERT_EQ(hipfftDestroy(plan_invalid_1), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
// inembed == null && onembed != null
|
|
{
|
|
hipfftHandle plan_invalid_2 = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan_invalid_2), HIPFFT_SUCCESS);
|
|
auto ret_hipfft = hipfftMakePlanMany(plan_invalid_2,
|
|
rank,
|
|
(int*)n,
|
|
inembed_null,
|
|
istride,
|
|
idist,
|
|
onembed,
|
|
ostride,
|
|
odist,
|
|
type,
|
|
batch,
|
|
&workSize);
|
|
ASSERT_EQ(ret_hipfft, HIPFFT_INVALID_VALUE)
|
|
<< "inembed == null && onembed != null failed: " << hipfftResult_string(ret_hipfft);
|
|
ASSERT_EQ(hipfftDestroy(plan_invalid_2), HIPFFT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
TEST(hipfftTest, CreatePlanMany64)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
int const rank = 3;
|
|
long long int const nX = 64;
|
|
long long int const nY = 128;
|
|
long long int const nZ = 23;
|
|
long long int n[3] = {nX, nY, nZ};
|
|
long long int inembed[3] = {nX, nY, nZ};
|
|
long long int const istride = 1;
|
|
long long int const idist = nX * nY * nZ;
|
|
long long int onembed[3] = {nX, nY, nZ};
|
|
long long int onembed_invalid[3] = {nX, nY, -nZ};
|
|
long long int const ostride = 1;
|
|
long long int const odist = nX * nY * nZ;
|
|
hipfftType type = HIPFFT_C2C;
|
|
long long int const batch = 1000;
|
|
long long int const batch_invalid = -2;
|
|
size_t workSize;
|
|
|
|
// Tests the 64-bit version of plan creation
|
|
// with valid/invalid data layouts.
|
|
|
|
// First test with a valid data layout
|
|
{
|
|
hipfftHandle plan_valid = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan_valid), HIPFFT_SUCCESS);
|
|
auto ret_hipfft = hipfftMakePlanMany64(plan_valid,
|
|
rank,
|
|
(long long int*)n,
|
|
inembed,
|
|
istride,
|
|
idist,
|
|
onembed,
|
|
ostride,
|
|
odist,
|
|
type,
|
|
batch,
|
|
&workSize);
|
|
ASSERT_EQ(ret_hipfft, HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftSetAutoAllocation(plan_valid, 0), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftDestroy(plan_valid), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
// invalid data layout (n array has a negative entry). only test rocFFT
|
|
// backend, since it's more strict
|
|
#ifdef __HIP_PLATFORM_AMD__
|
|
long long int n_invalid[3] = {nX, -nY, nZ};
|
|
{
|
|
hipfftHandle plan_invalid_1 = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan_invalid_1), HIPFFT_SUCCESS);
|
|
auto ret_hipfft = hipfftMakePlanMany64(plan_invalid_1,
|
|
rank,
|
|
(long long int*)n_invalid,
|
|
inembed,
|
|
istride,
|
|
idist,
|
|
onembed,
|
|
ostride,
|
|
odist,
|
|
type,
|
|
batch,
|
|
&workSize);
|
|
ASSERT_EQ(ret_hipfft, HIPFFT_INVALID_VALUE);
|
|
ASSERT_EQ(hipfftSetAutoAllocation(plan_invalid_1, 0), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftDestroy(plan_invalid_1), HIPFFT_SUCCESS);
|
|
}
|
|
#endif
|
|
|
|
// invalid data layout (onembed array has a negative entry)
|
|
{
|
|
hipfftHandle plan_invalid_2 = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan_invalid_2), HIPFFT_SUCCESS);
|
|
auto ret_hipfft = hipfftMakePlanMany64(plan_invalid_2,
|
|
rank,
|
|
(long long int*)n,
|
|
inembed,
|
|
istride,
|
|
idist,
|
|
onembed_invalid,
|
|
ostride,
|
|
odist,
|
|
type,
|
|
batch,
|
|
&workSize);
|
|
ASSERT_EQ(ret_hipfft, HIPFFT_INVALID_SIZE);
|
|
ASSERT_EQ(hipfftSetAutoAllocation(plan_invalid_2, 0), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftDestroy(plan_invalid_2), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
// invalid data layout (batch is negative)
|
|
{
|
|
hipfftHandle plan_invalid_3 = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan_invalid_3), HIPFFT_SUCCESS);
|
|
auto ret_hipfft = hipfftMakePlanMany64(plan_invalid_3,
|
|
rank,
|
|
(long long int*)n,
|
|
inembed,
|
|
istride,
|
|
idist,
|
|
onembed,
|
|
ostride,
|
|
odist,
|
|
type,
|
|
batch_invalid,
|
|
&workSize);
|
|
ASSERT_EQ(ret_hipfft, HIPFFT_INVALID_SIZE);
|
|
ASSERT_EQ(hipfftSetAutoAllocation(plan_invalid_3, 0), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftDestroy(plan_invalid_3), HIPFFT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
TEST(hipfftTest, hipfftGetSizeMany)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
int const rank = 3;
|
|
int const nX = 33;
|
|
int const nY = 128;
|
|
int const nZ = 100;
|
|
int n[3] = {nX, nY, nZ};
|
|
int inembed[3] = {nX, nY, nZ};
|
|
int const istride = 1;
|
|
int const idist = nX * nY * nZ;
|
|
int onembed[3] = {nX, nY, nZ};
|
|
int const ostride = 1;
|
|
int const odist = nX * nY * nZ;
|
|
hipfftType type = HIPFFT_C2C;
|
|
int const batch = 1;
|
|
size_t workSize;
|
|
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan), HIPFFT_SUCCESS);
|
|
auto ret_hipfft = hipfftGetSizeMany(plan,
|
|
rank,
|
|
(int*)n,
|
|
inembed,
|
|
istride,
|
|
idist,
|
|
onembed,
|
|
ostride,
|
|
odist,
|
|
type,
|
|
batch,
|
|
&workSize);
|
|
ASSERT_EQ(ret_hipfft, HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftSetAutoAllocation(plan, 0), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
TEST(hipfftTest, hipfftGetSizeMany64)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
int const rank = 3;
|
|
long long int const nX = 133;
|
|
long long int const nY = 354;
|
|
long long int const nZ = 256;
|
|
long long int n[3] = {nX, nY, nZ};
|
|
long long int inembed[3] = {nX, nY, nZ};
|
|
long long int const istride = 1;
|
|
long long int const idist = nX * nY * nZ;
|
|
long long int onembed[3] = {nX, nY, nZ};
|
|
long long int const ostride = 1;
|
|
long long int const odist = nX * nY * nZ;
|
|
hipfftType type = HIPFFT_C2C;
|
|
long long int const batch = 2;
|
|
size_t workSize;
|
|
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan), HIPFFT_SUCCESS);
|
|
auto ret_hipfft = hipfftGetSizeMany64(plan,
|
|
rank,
|
|
(long long int*)n,
|
|
inembed,
|
|
istride,
|
|
idist,
|
|
onembed,
|
|
ostride,
|
|
odist,
|
|
type,
|
|
batch,
|
|
&workSize);
|
|
ASSERT_EQ(ret_hipfft, HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftSetAutoAllocation(plan, 0), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
TEST(hipfftTest, CheckBufferSizeC2C)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan), HIPFFT_SUCCESS);
|
|
size_t n = 1024;
|
|
size_t workSize = 0;
|
|
|
|
ASSERT_EQ(hipfftMakePlan1d(plan, n, HIPFFT_C2C, 1, &workSize), HIPFFT_SUCCESS);
|
|
|
|
#ifdef __HIP_PLATFORM_AMD__
|
|
// No extra work buffer for C2C
|
|
EXPECT_EQ(workSize, 0);
|
|
#endif
|
|
ASSERT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
TEST(hipfftTest, CheckBufferSizeR2C)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan), HIPFFT_SUCCESS);
|
|
// real forward transform cannot modify input, so we need to pick
|
|
// a sufficiently small N such that rocFFT can fuse
|
|
// post-processing into one kernel and avoid a temp buffer
|
|
size_t n = 256;
|
|
size_t workSize = 0;
|
|
|
|
ASSERT_EQ(hipfftMakePlan1d(plan, n, HIPFFT_R2C, 1, &workSize), HIPFFT_SUCCESS);
|
|
|
|
#ifdef __HIP_PLATFORM_AMD__
|
|
// NOTE: keep this condition for ease of changing n for ad-hoc tests
|
|
//
|
|
// cppcheck-suppress knownConditionTrueFalse
|
|
if(n % 2 == 0)
|
|
{
|
|
EXPECT_EQ(workSize, 0);
|
|
}
|
|
else
|
|
{
|
|
EXPECT_EQ(workSize, 2 * n * sizeof(float));
|
|
}
|
|
#endif
|
|
EXPECT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
TEST(hipfftTest, CheckBufferSizeC2R)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan), HIPFFT_SUCCESS);
|
|
size_t n = 2048;
|
|
size_t workSize = 0;
|
|
|
|
ASSERT_EQ(hipfftMakePlan1d(plan, n, HIPFFT_C2R, 1, &workSize), HIPFFT_SUCCESS);
|
|
|
|
#ifdef __HIP_PLATFORM_AMD__
|
|
// NOTE: keep this condition for ease of changing n for ad-hoc tests
|
|
//
|
|
// cppcheck-suppress knownConditionTrueFalse
|
|
if(n % 2 == 0)
|
|
{
|
|
EXPECT_EQ(workSize, 0);
|
|
}
|
|
else
|
|
{
|
|
EXPECT_EQ(workSize, 2 * n * sizeof(float));
|
|
}
|
|
#endif
|
|
ASSERT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
TEST(hipfftTest, CheckBufferSizeD2Z)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan), HIPFFT_SUCCESS);
|
|
// real forward transform cannot modify input, so we need to pick
|
|
// a sufficiently small N such that rocFFT can fuse
|
|
// post-processing into one kernel and avoid a temp buffer
|
|
size_t n = 256;
|
|
size_t batch = 1000;
|
|
size_t workSize = 0;
|
|
|
|
ASSERT_EQ(hipfftMakePlan1d(plan, n, HIPFFT_D2Z, batch, &workSize), HIPFFT_SUCCESS);
|
|
|
|
#ifdef __HIP_PLATFORM_AMD__
|
|
// NOTE: keep this condition for ease of changing n for ad-hoc tests
|
|
//
|
|
// cppcheck-suppress knownConditionTrueFalse
|
|
if(n % 2 == 0)
|
|
{
|
|
EXPECT_EQ(workSize, 0);
|
|
}
|
|
else
|
|
{
|
|
EXPECT_EQ(workSize, 2 * n * sizeof(double));
|
|
}
|
|
#endif
|
|
|
|
ASSERT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
TEST(hipfftTest, CheckBufferSizeZ2D)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan), HIPFFT_SUCCESS);
|
|
size_t n = 2048;
|
|
size_t batch = 1000;
|
|
size_t workSize = 0;
|
|
|
|
ASSERT_EQ(hipfftMakePlan1d(plan, n, HIPFFT_Z2D, batch, &workSize), HIPFFT_SUCCESS);
|
|
|
|
#ifdef __HIP_PLATFORM_AMD__
|
|
// NOTE: keep this condition for ease of changing n for ad-hoc tests
|
|
//
|
|
// cppcheck-suppress knownConditionTrueFalse
|
|
if(n % 2 == 0)
|
|
{
|
|
EXPECT_EQ(workSize, 0);
|
|
}
|
|
else
|
|
{
|
|
EXPECT_EQ(workSize, 2 * n * sizeof(double));
|
|
}
|
|
#endif
|
|
|
|
ASSERT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
}
|
|
|
|
#ifdef __HIP_PLATFORM_AMD__
|
|
TEST(hipfftTest, CheckNullWorkBuffer)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan), HIPFFT_SUCCESS);
|
|
size_t n = 2048;
|
|
size_t batch = 1000;
|
|
size_t workSize = 0;
|
|
|
|
ASSERT_EQ(hipfftMakePlan1d(plan, n, HIPFFT_Z2D, batch, &workSize), HIPFFT_SUCCESS);
|
|
EXPECT_EQ(hipfftSetWorkArea(plan, nullptr), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
}
|
|
#endif
|
|
|
|
TEST(hipfftTest, RunR2C)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
const size_t N = 4096;
|
|
float in[N];
|
|
for(size_t i = 0; i < N; i++)
|
|
in[i] = i + (i % 3) - (i % 7);
|
|
|
|
hipfftReal* d_in;
|
|
hipfftComplex* d_out;
|
|
ASSERT_EQ(hipMalloc(&d_in, N * sizeof(hipfftReal)), hipSuccess);
|
|
ASSERT_EQ(hipMalloc(&d_out, (N / 2 + 1) * sizeof(hipfftComplex)), hipSuccess);
|
|
|
|
ASSERT_EQ(hipMemcpy(d_in, in, N * sizeof(hipfftReal), hipMemcpyHostToDevice), hipSuccess);
|
|
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan), HIPFFT_SUCCESS);
|
|
size_t workSize;
|
|
ASSERT_EQ(hipfftMakePlan1d(plan, N, HIPFFT_R2C, 1, &workSize), HIPFFT_SUCCESS);
|
|
|
|
EXPECT_EQ(hipfftExecR2C(plan, d_in, d_out), HIPFFT_SUCCESS);
|
|
|
|
std::vector<hipfftComplex> out(N / 2 + 1);
|
|
ASSERT_EQ(hipMemcpy(&out[0], d_out, (N / 2 + 1) * sizeof(hipfftComplex), hipMemcpyDeviceToHost),
|
|
hipSuccess);
|
|
|
|
ASSERT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipFree(d_in), hipSuccess);
|
|
ASSERT_EQ(hipFree(d_out), hipSuccess);
|
|
;
|
|
// NOTE: keep this condition for ease of changing n for ad-hoc tests
|
|
//
|
|
// cppcheck-suppress knownConditionTrueFalse
|
|
if(N % 2 != 0)
|
|
{
|
|
EXPECT_TRUE(workSize != 0);
|
|
}
|
|
|
|
double ref_in[N];
|
|
for(size_t i = 0; i < N; i++)
|
|
ref_in[i] = in[i];
|
|
|
|
fftw_complex* ref_out;
|
|
|
|
ref_out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * (N / 2 + 1));
|
|
fftw_plan_wrapper_t<double> ref_p
|
|
= fftw_trait<double>::make_wrapper(fftw_plan_dft_r2c_1d(N, ref_in, ref_out, FFTW_ESTIMATE));
|
|
fftw_execute_type<double>(ref_p);
|
|
|
|
double maxv = 0;
|
|
double nrmse = 0; // normalized root mean square error
|
|
for(size_t i = 0; i < (N / 2 + 1); i++)
|
|
{
|
|
// printf("element %d: FFTW result %f, %f; hipFFT result %f, %f \n", (int)i, ref_out[i][0], ref_out[i][1], out[i].x, out[i].y);
|
|
double dr = ref_out[i][0] - out[i].x;
|
|
double di = ref_out[i][1] - out[i].y;
|
|
maxv = fabs(ref_out[i][0]) > maxv ? fabs(ref_out[i][0]) : maxv;
|
|
maxv = fabs(ref_out[i][1]) > maxv ? fabs(ref_out[i][1]) : maxv;
|
|
|
|
nrmse += ((dr * dr) + (di * di));
|
|
}
|
|
nrmse /= (double)((N / 2 + 1));
|
|
nrmse = sqrt(nrmse);
|
|
nrmse /= maxv;
|
|
|
|
fftw_free(ref_out);
|
|
EXPECT_LT(nrmse, type_epsilon_simple<double>());
|
|
}
|
|
|
|
// ask for a transform whose parameters are only valid out-of-place.
|
|
// since hipFFT generates both in-place and out-place plans up front
|
|
// (because it's not told about the placement until exec time), this
|
|
// ensures that a failure to create an in-place plan doesn't prevent
|
|
// the out-place plan from working.
|
|
TEST(hipfftTest, OutplaceOnly)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
static const int N_in_const = 4;
|
|
static const int N_out_const = N_in_const / 2 + 1;
|
|
// mutable sizes for passing to hipFFT
|
|
int N_in = N_in_const;
|
|
int N_out = N_out_const;
|
|
float in[N_in_const];
|
|
for(int i = 0; i < N_in; i++)
|
|
in[i] = i + (i % 3) - (i % 7);
|
|
|
|
hipfftReal* d_in;
|
|
hipfftComplex* d_out;
|
|
ASSERT_EQ(hipMalloc(&d_in, N_in * sizeof(hipfftReal)), hipSuccess);
|
|
ASSERT_EQ(hipMalloc(&d_out, N_out * sizeof(hipfftComplex)), hipSuccess);
|
|
|
|
ASSERT_EQ(hipMemcpy(d_in, in, N_in * sizeof(hipfftReal), hipMemcpyHostToDevice), hipSuccess);
|
|
|
|
hipfftHandle plan = hipfft_params::INVALID_PLAN_HANDLE;
|
|
ASSERT_EQ(hipfftCreate(&plan), HIPFFT_SUCCESS);
|
|
|
|
ASSERT_EQ(hipfftPlanMany(&plan, 1, &N_in, &N_in, 1, N_in, &N_out, 1, N_out, HIPFFT_R2C, 1),
|
|
HIPFFT_SUCCESS);
|
|
|
|
ASSERT_EQ(plan == hipfft_params::INVALID_PLAN_HANDLE, false);
|
|
|
|
ASSERT_EQ(hipfftExecR2C(plan, d_in, d_out), HIPFFT_SUCCESS) << "hipfftExecR2C failed";
|
|
|
|
std::vector<hipfftComplex> out(N_out);
|
|
ASSERT_EQ(hipMemcpy(out.data(), d_out, N_out * sizeof(hipfftComplex), hipMemcpyDeviceToHost),
|
|
hipSuccess);
|
|
|
|
// in-place transform isn't really *supposed* to work - this
|
|
// might or might not fail but we can at least check that it
|
|
// doesn't blow up.
|
|
//hipfftExecR2C(plan, reinterpret_cast<hipfftReal*>(d_out), d_out);
|
|
|
|
ASSERT_EQ(hipfftDestroy(plan), HIPFFT_SUCCESS);
|
|
ASSERT_EQ(hipFree(d_in), hipSuccess);
|
|
ASSERT_EQ(hipFree(d_out), hipSuccess);
|
|
|
|
double ref_in[N_in_const];
|
|
for(int i = 0; i < N_in_const; i++)
|
|
ref_in[i] = in[i];
|
|
|
|
fftw_complex* ref_out;
|
|
|
|
ref_out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N_out);
|
|
fftw_plan_wrapper_t<double> ref_p = fftw_trait<double>::make_wrapper(
|
|
fftw_plan_dft_r2c_1d(N_in, ref_in, ref_out, FFTW_ESTIMATE));
|
|
fftw_execute_type<double>(ref_p);
|
|
|
|
double maxv = 0;
|
|
double nrmse = 0; // normalized root mean square error
|
|
for(int i = 0; i < N_out; i++)
|
|
{
|
|
// printf("element %d: FFTW result %f, %f; hipFFT result %f, %f \n", (int)i, ref_out[i][0], ref_out[i][1], out[i].x, out[i].y);
|
|
double dr = ref_out[i][0] - out[i].x;
|
|
double di = ref_out[i][1] - out[i].y;
|
|
maxv = fabs(ref_out[i][0]) > maxv ? fabs(ref_out[i][0]) : maxv;
|
|
maxv = fabs(ref_out[i][1]) > maxv ? fabs(ref_out[i][1]) : maxv;
|
|
|
|
nrmse += ((dr * dr) + (di * di));
|
|
}
|
|
nrmse /= (double)(N_out);
|
|
nrmse = sqrt(nrmse);
|
|
nrmse /= maxv;
|
|
|
|
fftw_free(ref_out);
|
|
ASSERT_LT(nrmse, type_epsilon_simple<double>());
|
|
}
|
|
|
|
static constexpr int absurd_version_or_property = std::numeric_limits<int>::min();
|
|
TEST(hipfftTest, GetVersion)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
// valid use case(s)
|
|
int tmp = absurd_version_or_property;
|
|
EXPECT_EQ(hipfftGetVersion(&tmp), HIPFFT_SUCCESS);
|
|
EXPECT_NE(tmp, absurd_version_or_property);
|
|
EXPECT_EQ(hipfftGetVersion(nullptr), HIPFFT_INVALID_VALUE);
|
|
}
|
|
|
|
TEST(hipfftTest, GetProperty)
|
|
{
|
|
if(hash_prob(random_seed, ::testing::UnitTest::GetInstance()->current_test_info()->name())
|
|
> unittest_prob)
|
|
{
|
|
GTEST_SKIP();
|
|
}
|
|
// valid use case(s)
|
|
int tmp;
|
|
for(auto prop_type : enum_helper<hipfftLibraryPropertyType>::valid_values)
|
|
{
|
|
tmp = absurd_version_or_property;
|
|
EXPECT_EQ(hipfftGetProperty(prop_type, &tmp), HIPFFT_SUCCESS);
|
|
EXPECT_NE(tmp, absurd_version_or_property);
|
|
}
|
|
// invalid use case(s)
|
|
const auto valid_property_type = enum_helper<hipfftLibraryPropertyType>::get_any_valid_value();
|
|
EXPECT_EQ(hipfftGetProperty(valid_property_type, nullptr), HIPFFT_INVALID_VALUE);
|
|
const auto invalid_property_type = enum_helper<hipfftLibraryPropertyType>::get_invalid_value();
|
|
EXPECT_EQ(hipfftGetProperty(invalid_property_type, &tmp), HIPFFT_INVALID_VALUE);
|
|
}
|