Files
TheRock/rocm-libraries/projects/hipfft/shared/reference_fft_data.h
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

668 lines
29 KiB
C++

// Copyright (C) 2026 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.
#ifndef REFERENCE_FFT_DATA
#define REFERENCE_FFT_DATA
#include <algorithm>
#include <future>
#include <string>
#include <variant>
#include <vector>
#include "client_except.h"
#include "enum_to_string.h"
#include "fft_params.h"
#include "fftw_transform.h"
#include "gpubuf.h"
#include "rocfft_against_fftw.h"
#include "sys_mem.h"
#include "test_callbacks.h"
#include "test_params.h"
template <typename Tfloat>
inline void execute_cpu_fft(const fft_params& cpu_fft_params,
fftw_plan_wrapper_t<Tfloat>& cpu_plan,
std::vector<hostbuf>& cpu_input,
std::vector<hostbuf>& cpu_output)
{
// If this is either C2R or callbacks are enabled, the
// input will be modified. So we need to modify the copy instead.
std::vector<hostbuf> cpu_input_copy(cpu_input.size());
std::vector<hostbuf>* input_ptr = &cpu_input;
if(cpu_fft_params.run_callbacks != fft_callback_type_none
|| cpu_fft_params.transform_type == fft_transform_type_real_inverse)
{
for(size_t i = 0; i < cpu_input.size(); ++i)
{
cpu_input_copy[i] = cpu_input[i].copy();
}
input_ptr = &cpu_input_copy;
}
// run FFTW (which may destroy CPU input)
apply_load_callback(cpu_fft_params, *input_ptr);
cpu_fft_params.apply_host_load_ops(*input_ptr);
fftw_run<Tfloat>(cpu_fft_params.transform_type, cpu_plan, *input_ptr, cpu_output);
// clean up
// ask FFTW to fully clean up, since it tries to cache plan details
cpu_plan.reset();
if constexpr(std::is_same<Tfloat, double>::value)
fftw_cleanup();
else
fftwf_cleanup();
cpu_fft_params.apply_host_store_ops(cpu_output);
apply_store_callback(cpu_fft_params, cpu_output);
}
struct reference_fft_data_t
{
reference_fft_data_t(const fft_params& cpu_fft_params, hostbuf* input_data = nullptr)
{
if(cpu_fft_params.placement != fft_placement_notinplace || !cpu_fft_params.valid())
{
throw std::invalid_argument("Reference FFT results require valid parameters, "
"configured for out-of-place calculations");
}
if(input_data
&& (cpu_fft_params.isize.size() != 1
|| input_data->size()
< cpu_fft_params.isize[0]
* var_size<size_t>(cpu_fft_params.precision, cpu_fft_params.itype)))
{
throw std::invalid_argument("Invalid input data given to referent FFT results");
}
// If current cached results are valid reference results for the given
// parameters, just use them.
if(!input_data && cached_data.input_is_set.valid() && cached_data.output_is_set.valid()
&& cached_data.can_be_used_for(cpu_fft_params, false /* = strict_precision_check*/))
{
this->swap(cached_data);
// former cached_data.params are now this->params
if(cpu_fft_params.precision < params.precision)
async_narrow_precision(cpu_fft_params.precision);
if(verbose > 3)
{
std::cout << "CPU params:\n";
std::cout << params.str("\n\t") << std::endl;
}
return;
}
// Clear cache as it is no longer useful: reference data will need to be computed.
if(verbose > 2)
std::cout << "Clearing cached reference FFT results" << std::endl;
cached_data.clear();
params = cpu_fft_params;
// input buffer can have minimal size but output must be large enough for
// precision used at compute time (requirement from fftw_run)
if(input_data)
{
cpu_input.resize(1);
std::swap(cpu_input[0], *input_data);
// input is ready
input_is_set = std::async(std::launch::async, []() {});
}
else
cpu_input = allocate_host_buffer(params.precision, params.itype, params.isize);
// Output buffer and fftw plan are needed iff `fftw_compare` is `true`.
if(fftw_compare)
{
const auto compute_prec
= params.precision == fft_precision_half ? fft_precision_single : params.precision;
// Reserve some amount of system memory for the FFTW plan's possible workspace: Use a
// conservative estimate for the size of the workspace that the fftw plan may need to
// guard against OOM kills. The FFTW workspace is estimated as double (resp. five times)
// the maximum size of I/O if all (resp. any) length prime factors do not exceed (resp.
// does exceed) 13.
const auto estimated_bytes_for_fftw_workspace
= std::max(params.isize[0] * var_size<size_t>(compute_prec, params.itype),
params.osize[0] * var_size<size_t>(compute_prec, params.otype))
* (std::any_of(params.length.begin(),
params.length.end(),
[](size_t ell) { return ell > 2 && max_prime_factor(ell) > 13; })
? 5
: 2);
try
{
reservation_for_fftw_internal_alloc.set_desired_size(
estimated_bytes_for_fftw_workspace);
}
catch(const std::invalid_argument& e)
{
// Requested size is too large: reservation cannot be guaranteed
std::stringstream info;
info
<< "Reservation of system memory for FFTW plan's possible workspace (estimated "
"size) cannot be guaranteed. Details:\n"
<< e.what();
throw ROCFFT_SKIP{info.str()};
}
if(verbose > 3)
{
std::cout << "Reserved " << byte_size_to_str(estimated_bytes_for_fftw_workspace)
<< " of system memory temporarily for the fftw plan's possible internal "
"workspace (temporary reservation)."
<< std::endl;
}
cpu_output = allocate_host_buffer(compute_prec, params.otype, params.osize);
switch(compute_prec)
{
case fft_precision_double:
cpu_plan = fftw_plan_via_rocfft<double>(params.length,
params.istride,
params.ostride,
params.nbatch,
params.idist,
params.odist,
params.transform_type,
cpu_input,
cpu_output);
break;
case fft_precision_single:
cpu_plan = fftw_plan_via_rocfft<float>(params.length,
params.istride,
params.ostride,
params.nbatch,
params.idist,
params.odist,
params.transform_type,
cpu_input,
cpu_output);
break;
default:
throw std::invalid_argument("Unexpected compute precision encountered when "
"constructing reference FFT data");
break;
}
}
if(verbose > 3)
{
std::cout << "CPU params:\n";
std::cout << params.str("\n\t") << std::endl;
}
}
bool needs_input_initialization() const
{
return !input_is_set.valid();
}
void initialize_input(const fft_input_generator& gen)
{
if(!is_host_generator(gen))
throw std::invalid_argument("Independent initialization of input data for reference "
"FFT data requires a host-side input generator");
// Avoid corruption of input data by concurrently-executing threads
wait_if_needed_for<fft_io::fft_io_in>();
params.igen = gen; // <-- conditions the behavior of params.compute_input below
input_is_set = std::async(std::launch::async, [&]() { params.compute_input(cpu_input); });
// output data must be invalidated as it needs re-computing:
wait_and_invalidate<fft_io::fft_io_out>();
}
void initialize_input_using(const std::vector<gpubuf>& src_buffers,
const fft_params& test_params)
{
if(!can_be_used_for(test_params))
throw std::invalid_argument(
"Reference results cannot be used with the given test parameters");
// Avoid corruption of input data by concurrently-executing threads
wait_if_needed_for<fft_io::fft_io_in>();
const auto ibuffer_sizes = test_params.ibuffer_sizes();
if(params.itype == test_params.itype && params.istride == test_params.istride
&& params.idist == test_params.idist && params.isize == test_params.isize
&& params.ioffset == test_params.ioffset && src_buffers.size() == cpu_input.size())
{
// Direct copy of device input data into CPU input data
for(unsigned int idx = 0; idx < src_buffers.size(); ++idx)
{
if(cpu_input[idx].size() < ibuffer_sizes[idx])
throw std::logic_error("cpu_input buffer " + std::to_string(idx)
+ " is too small ("
+ byte_size_to_str(cpu_input[idx].size())
+ ") for direct copy of device input data ("
+ byte_size_to_str(ibuffer_sizes[idx]) + ")");
const auto hip_status = hipMemcpy(cpu_input.at(idx).data(),
src_buffers[idx].data(),
ibuffer_sizes[idx],
hipMemcpyDeviceToHost);
if(hip_status != hipSuccess)
{
throw hip_runtime_error("hipMemcpy failure", hip_status);
}
}
}
else
{
auto tmp_host_buffers = allocate_host_buffer(ibuffer_sizes);
// Copy input to CPU
for(unsigned int idx = 0; idx < src_buffers.size(); ++idx)
{
const auto hip_status = hipMemcpy(tmp_host_buffers.at(idx).data(),
src_buffers[idx].data(),
ibuffer_sizes[idx],
hipMemcpyDeviceToHost);
if(hip_status != hipSuccess)
{
throw hip_runtime_error("hipMemcpy failure", hip_status);
}
}
copy_buffers(tmp_host_buffers,
cpu_input,
test_params.ilength(),
test_params.nbatch,
test_params.precision,
test_params.itype,
test_params.istride,
test_params.idist,
params.itype,
params.istride,
params.idist,
test_params.ioffset,
params.ioffset);
}
// input is readily available:
std::promise<void> tmp_promise;
tmp_promise.set_value();
input_is_set = tmp_promise.get_future();
// output data must be invalidated as it needs re-computing:
wait_and_invalidate<fft_io::fft_io_out>();
}
void copy_input_data_in_device_buffers(const std::vector<gpubuf>& device_input_buffers,
const fft_params& test_params) const
{
if(!can_be_used_for(test_params))
throw std::invalid_argument(
"Reference results cannot be used with the given test parameters");
if(!input_is_set.valid())
throw std::logic_error("The input data of reference FFT results needs to be "
"initialized before it may be copied");
input_is_set.get();
const std::vector<hostbuf>* input_to_copy = &cpu_input;
std::vector<hostbuf> temp_host_buffers;
const auto ibuffer_sizes = test_params.ibuffer_sizes();
if(!(params.itype == test_params.itype && params.istride == test_params.istride
&& params.idist == test_params.idist && params.isize == test_params.isize
&& params.ioffset == test_params.ioffset && params.precision == test_params.precision))
{
temp_host_buffers = allocate_host_buffer(ibuffer_sizes);
copy_buffers(cpu_input,
temp_host_buffers,
params.ilength(),
test_params.nbatch, // note: may be smaller than params.nbatch
params.precision,
params.itype,
params.istride,
params.idist,
test_params.itype,
test_params.istride,
test_params.idist,
params.ioffset,
test_params.ioffset);
input_to_copy = &temp_host_buffers;
}
if(device_input_buffers.size() < input_to_copy->size())
throw std::invalid_argument("Fewer device input buffers than expected ("
+ std::to_string(device_input_buffers.size()) + " < "
+ std::to_string(input_to_copy->size())
+ ") for copying reference input data");
// Copy input data to GPU
for(unsigned int idx = 0; idx < input_to_copy->size(); ++idx)
{
if(device_input_buffers[idx].size() < ibuffer_sizes[idx])
throw std::invalid_argument("Device input buffer " + std::to_string(idx)
+ " is too small ("
+ byte_size_to_str(device_input_buffers[idx].size())
+ ") for copying reference input data("
+ byte_size_to_str(ibuffer_sizes[idx]) + " needed)");
const auto hip_status = hipMemcpy(device_input_buffers[idx].data(),
input_to_copy->at(idx).data(),
ibuffer_sizes[idx],
hipMemcpyHostToDevice);
if(hip_status != hipSuccess)
{
throw hip_runtime_error("hipMemcpy failure", hip_status);
}
}
}
bool needs_computing() const
{
return fftw_compare && !output_is_set.valid();
}
~reference_fft_data_t()
{
// Remember the results of the last FFT we computed with FFTW. Tests
// are ordered so that later cases can often reuse this result.
if(input_is_set.valid() && (!fftw_compare || output_is_set.valid()))
{
cached_data.swap(*this);
}
if(verbose > 3 && reservation_for_fftw_internal_alloc.size() > 0)
{
std::cout << "Releasing temporary reservation of "
<< byte_size_to_str(reservation_for_fftw_internal_alloc.size())
<< " of system memory (estimated FFTW plan's possible internal workspace)."
<< std::endl;
}
}
static reference_fft_data_t make_default()
{
return reference_fft_data_t();
}
// Note: the FFTW cpu plan is destroyed when the thread completes
void launch_async_compute()
{
if(!fftw_compare)
throw std::logic_error(
"Reference results should not be computed if `fftw_compare` is disabled");
if(!input_is_set.valid())
throw std::logic_error("Asynchronous computation of reference FFT results mustn't be "
"launched before having set the reference input data");
// Avoid corruption of output data by concurrently-executing threads
wait_if_needed_for<fft_io::fft_io_out>();
output_is_set = std::async(std::launch::async, [&]() {
input_is_set.get();
switch(params.precision)
{
case(fft_precision_double):
execute_cpu_fft<double>(
params, std::get<fftw_plan_wrapper_t<double>>(cpu_plan), cpu_input, cpu_output);
break;
case(fft_precision_single):
execute_cpu_fft<float>(
params, std::get<fftw_plan_wrapper_t<float>>(cpu_plan), cpu_input, cpu_output);
break;
case(fft_precision_half):
// cpu_input and cpu_output must be large enough for computing reference
// results in single-precision floating-point arithmetics but the output's
// precision is downgraded upon completion of the computation.
execute_cpu_fft<rocfft_fp16>(
params, std::get<fftw_plan_wrapper_t<float>>(cpu_plan), cpu_input, cpu_output);
break;
default:
throw std::logic_error("Unexpected precision for the CPU plan");
}
if(verbose > 3 && reservation_for_fftw_internal_alloc.size() > 0)
{
std::cout
<< "Releasing temporary reservation of "
<< byte_size_to_str(reservation_for_fftw_internal_alloc.size())
<< " of system memory (estimated FFTW plan's possible internal workspace)."
<< std::endl;
}
reservation_for_fftw_internal_alloc.release();
});
}
template <fft_io io>
void print_data() const
{
static_assert(io == fft_io::fft_io_in || io == fft_io::fft_io_out);
if constexpr(io == fft_io::fft_io_out)
{
if(!fftw_compare)
throw std::runtime_error(
"Reference output data cannot be printed if `fftw_compare` is disabled");
}
auto& flag = io == fft_io::fft_io_in ? input_is_set : output_is_set;
if(!flag.valid())
throw std::logic_error(
"The desired reference data cannot be printed since it was not set");
flag.get();
if constexpr(io == fft_io::fft_io_in)
{
std::cout << "CPU input:\n";
params.print_ibuffer(cpu_input);
}
else
{
std::cout << "CPU output:\n";
params.print_obuffer(cpu_output);
}
}
template <fft_io io>
std::shared_future<VectorNorms> get_norm(size_t relevant_batch_size)
{
static_assert(io == fft_io::fft_io_in || io == fft_io::fft_io_out);
if constexpr(io == fft_io::fft_io_out)
{
if(!fftw_compare)
throw std::runtime_error("Norms of reference output data cannot be computed if "
"`fftw_compare` is disabled.");
}
if(relevant_batch_size > params.nbatch)
throw std::invalid_argument("Invalid batch size in calculation of I/O norms.");
auto& io_is_set = io == fft_io::fft_io_in ? input_is_set : output_is_set;
if(!io_is_set.valid())
{
throw std::logic_error(
"Desired norm cannot be computed since corresponding data was not set");
}
return std::async(std::launch::async, [&, relevant_batch_size]() {
io_is_set.get();
return norm(io == fft_io::fft_io_in ? cpu_input : cpu_output,
io == fft_io::fft_io_in ? params.ilength() : params.olength(),
std::min(params.nbatch, relevant_batch_size),
params.precision,
io == fft_io::fft_io_in ? params.itype : params.otype,
io == fft_io::fft_io_in ? params.istride : params.ostride,
io == fft_io::fft_io_in ? params.idist : params.odist,
io == fft_io::fft_io_in ? params.ioffset : params.ooffset);
});
}
template <fft_io io>
const std::vector<hostbuf>& get_buffers()
{
static_assert(io == fft_io::fft_io_in || io == fft_io::fft_io_out);
if constexpr(io == fft_io::fft_io_out)
{
if(!fftw_compare)
throw std::runtime_error(
"Reference output data is not available if `fftw_compare` is disabled.");
}
auto& io_is_set = io == fft_io::fft_io_in ? input_is_set : output_is_set;
if(!io_is_set.valid())
{
throw std::logic_error("Desired reference FFT results' input/output buffers cannot be "
"queried since corresponding data was not set");
}
io_is_set.get();
return io == fft_io::fft_io_in ? cpu_input : cpu_output;
}
const fft_params& get_params() const
{
return params;
}
static void clear_cache()
{
cached_data.clear();
}
private:
// to validate test parameters as argument of public member functions
bool can_use_direct_input_copy_with(const fft_params& other) const
{
return params.itype == other.itype && params.istride == other.istride
&& params.idist == other.idist && params.isize == other.isize
&& params.precision <= other.precision;
}
template <fft_io io>
void wait_if_needed_for() const
{
static_assert(io == fft_io::fft_io_in || io == fft_io::fft_io_out);
auto& io_is_set = io == fft_io::fft_io_in ? input_is_set : output_is_set;
if(io_is_set.valid())
io_is_set.wait();
}
template <fft_io io>
void wait_and_invalidate()
{
wait_if_needed_for<io>();
if constexpr(io == fft_io::fft_io_in)
input_is_set = decltype(input_is_set){};
else
output_is_set = decltype(output_is_set){};
}
void clear()
{
params = fft_params{};
wait_and_invalidate<fft_io::fft_io_in>();
wait_and_invalidate<fft_io::fft_io_out>();
cpu_input.clear();
cpu_output.clear();
reservation_for_fftw_internal_alloc.release();
cpu_plan = fftw_trait<float>::make_wrapper(nullptr);
}
void swap(reference_fft_data_t& other)
{
if(this == &other)
return;
if(other.input_is_set.valid())
other.input_is_set.wait();
if(other.output_is_set.valid())
other.output_is_set.wait();
std::swap(input_is_set, other.input_is_set);
std::swap(output_is_set, other.output_is_set);
cpu_input.swap(other.cpu_input);
cpu_output.swap(other.cpu_output);
std::swap(params, other.params);
std::swap(cpu_plan, other.cpu_plan);
reservation_for_fftw_internal_alloc.swap(other.reservation_for_fftw_internal_alloc);
}
void async_narrow_precision(fft_precision narrower_prec)
{
if(!input_is_set.valid() || (fftw_compare && !output_is_set.valid()))
throw std::logic_error("Precision of reference results cannot be narrowed if input or "
"output data were not set prior.");
// Avoid data corruption by concurrent threads
input_is_set.get();
if(fftw_compare)
output_is_set.get();
const auto invalid_ref_prec_excpt = std::logic_error(
"Invalid precision encountered for reference results to be narrowed");
switch(narrower_prec)
{
case fft_precision_single:
{
if(params.precision != fft_precision_double)
throw invalid_ref_prec_excpt;
input_is_set = std::async(std::launch::async, [&]() {
narrow_precision_inplace<double, float>(cpu_input.front());
});
if(fftw_compare)
output_is_set = std::async(std::launch::async, [&]() {
narrow_precision_inplace<double, float>(cpu_output.front());
});
}
break;
case fft_precision_half:
{
if(params.precision == fft_precision_double)
{
input_is_set = std::async(std::launch::async, [&]() {
narrow_precision_inplace<double, rocfft_fp16>(cpu_input.front());
});
if(fftw_compare)
output_is_set = std::async(std::launch::async, [&]() {
narrow_precision_inplace<double, rocfft_fp16>(cpu_output.front());
});
}
else if(params.precision == fft_precision_single)
{
input_is_set = std::async(std::launch::async, [&]() {
narrow_precision_inplace<float, rocfft_fp16>(cpu_input.front());
});
if(fftw_compare)
output_is_set = std::async(std::launch::async, [&]() {
narrow_precision_inplace<float, rocfft_fp16>(cpu_output.front());
});
}
else
throw invalid_ref_prec_excpt;
}
break;
default:
throw std::invalid_argument(
"Unexpected precision given to narrow reference FFT results");
}
params.precision = narrower_prec;
}
// Note: batch size uses >= since existing reference results can just be
// cropped for smaller batch sizes, if needed. The check on precision can be
// strict or loose: Floating-point precision of reference results can never be
// narrower than what is being tested, but some public member functions do
// require *identical* precisions.
bool can_be_used_for(const fft_params& test_params, bool strict_precision_check = true) const
{
auto ret = params.length == test_params.length
&& params.transform_type == test_params.transform_type
&& params.nbatch >= test_params.nbatch
&& params.run_callbacks == test_params.run_callbacks
&& params.scale_factor == test_params.scale_factor;
if(strict_precision_check)
ret &= params.precision == test_params.precision;
else
ret &= params.precision >= test_params.precision;
return ret;
}
fft_params params;
std::shared_future<void> input_is_set;
std::shared_future<void> output_is_set;
// FFTW input/output
std::vector<hostbuf> cpu_input, cpu_output;
// CPU (FFTW) plans are either single or double precision
// (no actual half precision plan on CPU)
std::variant<fftw_plan_wrapper_t<float>, fftw_plan_wrapper_t<double>> cpu_plan
= fftw_trait<float>::make_wrapper(nullptr);
system_memory::nonowned_reservation_t reservation_for_fftw_internal_alloc;
// Private default constructor. (only needed for definition of static cache)
reference_fft_data_t() = default;
static reference_fft_data_t cached_data;
};
#endif