rocFFT: ROCm Fast Fourier Transforms
U nastavku koristimo kod iz repozitorija rocFFT (službena dokumentacija).
rocFFT je softverska biblioteka za računanje brzihi Fourierovih transformacija, te:
- pruža brzu i preciznu platformu za računanje diskretnih FFT-a
- podržava single i double precision floating point formate
- podržava 1D, 2D i 3D transformacije
- podržava računanje transformacija u hrpama
- podržava realne i kompleksne FFT
- podržava dužine koje sadrže bilo koju kombinaciju umnožaka 2, 3 i 5
Tipovi podataka
Postoji par struktura podataka koje su implementirane u ovu biblioteku. Dvije od najvažnijih su:
Plan
U plan računanja ulaze svi parametri potrebni za izvedbu transformacije. Parametri su:
- [out]
plan
-- plan handle - [in]
placement
-- pohrana rezultata - [in]
transform_type
-- tip transformacije - [in]
precision
-- koji tip (single ili double) precision-a će koristiti - [in]
dimensions
-- dimenzije - [in]
lengths
-- dimenzije veličine polja duljina transformacije - [in]
number_of_transforms
-- broj transformacija - [in]
description
-- opis plana
Izvođenje
Nakon što je plan napravljen, izvođenje može biti korišteno da pokrene plan da računa transformaciju na zadanim podatcima. Njegovi parametri su:
- [in]
plan
-- plan handle - [in]
in_buffer
-- polje (veličine 1 kod isprepletenih podataka, veličine 2 kod planarnih podataka) input buffer-a - [inout]
out_buffer
-- polje (veličine 1 kod isprepletenih podataka, veličine 2 kod planarnih podataka) output buffer-a, može biti inullptr
kodinplace
postavljanja rezultata - [in]
info
-- pokretanje info handle-a stvorenogrocfft_execution_info_create(...)
funkcijom
Primjer
Službeni primjer clients/samples/rocfft/rocfft_example_set_stream.cpp
(poveznica na kod) pokazuje primjer kako provesti dvije inplace
transformacije sa dva toka.
Program izgleda ovako:
#define CHECK_HIP_ERR(err) \
if(err != hipSuccess) \
{ \
std::cerr << "hip error code : " << err << std::endl; \
exit(-1); \
}
#define CHECK_ROCFFT_ERR(err) \
if(err != rocfft_status_success) \
{ \
std::cerr << "rocFFT error code : " << err << std::endl; \
exit(-1); \
}
struct fft_fixture_t
{
double2* cpu_buf;
double2* gpu_buf;
hipStream_t stream;
rocfft_execution_info info;
rocfft_plan plan;
};
int main(int argc, char* argv[])
{
std::cout << "rocfft example of 2 inplace transforms with 2 streams.\n" << std::endl;
rocfft_status rc = rocfft_status_success;
size_t length = 8;
size_t total_bytes = length * sizeof(double2);
fft_fixture_t ffts[2];
/// preparation
for(auto& it : ffts)
{
// create cpu buffer
it.cpu_buf = new double2[length];
// init cpu buffer...
// create gpu buffer
CHECK_HIP_ERR(hipMalloc(&(it.gpu_buf), total_bytes));
// copy host to device
CHECK_HIP_ERR(hipMemcpy(it.gpu_buf, it.cpu_buf, total_bytes, hipMemcpyHostToDevice));
// create stream
CHECK_HIP_ERR(hipStreamCreate(&(it.stream)));
// create execution info
CHECK_ROCFFT_ERR(rocfft_execution_info_create(&(it.info)));
// set stream
// NOTE: The stream must be of type hipStream_t.
// It is an error to pass the address of a hipStream_t object.
CHECK_ROCFFT_ERR(rocfft_execution_info_set_stream(it.info, it.stream));
// create plan
CHECK_ROCFFT_ERR(rocfft_plan_create(&it.plan,
rocfft_placement_inplace,
rocfft_transform_type_complex_forward,
rocfft_precision_double,
1,
&length,
1,
nullptr));
size_t work_buf_size = 0;
CHECK_ROCFFT_ERR(rocfft_plan_get_work_buffer_size(it.plan, &work_buf_size));
assert(work_buf_size == 0); // simple 1D inplace fft doesn't need extra working buffer
}
/// execution
for(auto& it : ffts)
{
CHECK_ROCFFT_ERR(
rocfft_execute(it.plan, (void**)&(it.gpu_buf), (void**)&(it.gpu_buf), nullptr));
}
/// wait and copy back
for(auto& it : ffts)
{
CHECK_HIP_ERR(hipStreamSynchronize(it.stream));
CHECK_HIP_ERR(hipMemcpy(it.cpu_buf, it.gpu_buf, total_bytes, hipMemcpyDeviceToHost));
}
/// clean up
for(auto& it : ffts)
{
CHECK_ROCFFT_ERR(rocfft_plan_destroy(it.plan));
CHECK_ROCFFT_ERR(rocfft_execution_info_destroy(it.info));
CHECK_HIP_ERR(hipStreamDestroy(it.stream));
CHECK_HIP_ERR(hipFree(it.gpu_buf));
delete[] it.cpu_buf;
}
return 0;
}
Krenimo od funkcije main()
. Prvo stvaramo novi objekt tipa rocfft_status
kojim program može javiti postoji li greška, i gdje se nalazi. U ovom slučaju postavljen je da javlja uspjeh.
Varijabla length
postavljena je na 8, te se definira način računanja za konačan broj bajtova total_bytes
. Nakon toga, inicijalizira se i polje tipa strukture fft_fixture_t
.
std::cout << "rocfft example of 2 inplace transforms with 2 streams.\n" << std::endl;
rocfft_status rc = rocfft_status_success;
size_t length = 8;
size_t total_bytes = length * sizeof(double2);
fft_fixture_t ffts[2];
Slijedi priprema; kreće petlja for
u kojoj, kao raspon, stoji izraz (auto& it : ffts)
, pri čemu auto& it
služi da se ne stvaraju kopije elemenata unutar ffts
, a operator :
govori da će petlja vrtjeti svaki element dok ne dođe do kraja ffts
.
Stvara se CPU buffer, a iza toga GPU buffer putem nama već poznatih funkcija, CHECK_HIP_ERR(hipMalloc(...))
.
for(auto& it : ffts)
{
it.cpu_buf = new double2[length];
CHECK_HIP_ERR(hipMalloc(&(it.gpu_buf), total_bytes));
Prebacujemo/kopiramo podatke sa domaćina na uređaj, iza čega stvaramo tok.
CHECK_HIP_ERR(hipMemcpy(it.gpu_buf, it.cpu_buf, total_bytes, hipMemcpyHostToDevice));
CHECK_HIP_ERR(hipStreamCreate(&(it.stream)));
Također, stvara se execution info
(za više informacija o ovom pojmu proučite službenu dokumentaciju).
Postavlja se tok, koji mora biti tipa hipStream_t (u suprotnom bi došlo do greške).
CHECK_ROCFFT_ERR(rocfft_execution_info_create(&(it.info)));
CHECK_ROCFFT_ERR(rocfft_execution_info_set_stream(it.info, it.stream));
Napslijetku, stvaramo plan računanja; u planu stoje parametri: objekt za plan, placement varijabla, tip transformacije, tip precision-a koji će se koristiti, dimenzije, duljina, broj transformacija i opis plana.
CHECK_ROCFFT_ERR(rocfft_plan_create(&it.plan,
rocfft_placement_inplace,
rocfft_transform_type_complex_forward,
rocfft_precision_double,
1,
&length,
1,
nullptr));
Veličina buffera koji radi se postavlja na 0, te se pokreće funkcija kojom se doseže njegova veličina.
size_t work_buf_size = 0;
CHECK_ROCFFT_ERR(rocfft_plan_get_work_buffer_size(it.plan, &work_buf_size));
assert(work_buf_size == 0);
}
Nakon postavljanja plana, kreće execution
koji koristi iste parametre za petlju for
, te provodi funkciju rocfft_execute()
(za više infromacija o ovoj funkciji pogledajte službenu dokumentaciju ROCm biblioteka):
for(auto& it : ffts)
{
CHECK_ROCFFT_ERR(
rocfft_execute(it.plan, (void**)&(it.gpu_buf), (void**)&(it.gpu_buf), nullptr));
}
Program čeka da se prijašnja funkcija izvrši, te zatim kopira podatke natrag na domaćina.
for(auto& it : ffts)
{
CHECK_HIP_ERR(hipStreamSynchronize(it.stream));
CHECK_HIP_ERR(hipMemcpy(it.cpu_buf, it.gpu_buf, total_bytes, hipMemcpyDeviceToHost));
}
Za kraj programa, slijedi pročišćivanje i oslobađanje memorije:
for(auto& it : ffts)
{
CHECK_ROCFFT_ERR(rocfft_plan_destroy(it.plan));
CHECK_ROCFFT_ERR(rocfft_execution_info_destroy(it.info));
CHECK_HIP_ERR(hipStreamDestroy(it.stream));
CHECK_HIP_ERR(hipFree(it.gpu_buf));
delete[] it.cpu_buf;
}
return 0;
}
Ako se vratimo na početak programa, definirani su CHECK_HIP_ERR
i CHECK_ROCFFT_ERR
sa vlastitim ispisima u slučaju da dođe do greške:
#define CHECK_HIP_ERR(err) \
if(err != hipSuccess) \
{ \
std::cerr << "hip error code : " << err << std::endl; \
exit(-1); \
}
#define CHECK_ROCFFT_ERR(err) \
if(err != rocfft_status_success) \
{ \
std::cerr << "rocFFT error code : " << err << std::endl; \
exit(-1); \
}
Isto tako, definirana je struktura fft_fixture_t
u kojoj su stvoreni elementi potrebni za transformacije.
struct fft_fixture_t
{
double2* cpu_buf;
double2* gpu_buf;
hipStream_t stream;
rocfft_execution_info info;
rocfft_plan plan;
};
Kraj programa.
Author: Mia Doričić, Vedran Miletić