rocRAND: ROCm RANDom number generator
U nastavku koristimo kod iz repozitorija rocRAND (službena dokumentacija).
rocRAND nudi funkcije za generiranje pseudoslučajnih i kvazislučajnih brojeva.
Podržani generatori su:
Pregled dostupne funkcionalnosti u rocRAND-u
Funkcije koje rade na domaćinu
(Za više informacija proučite rocRAND host API.)
rocrand_create_generator()
stvara novi generator slučajnih brojevarocrand_destroy_generator()
uništava generator slučajnih brojevarocrand_initialize_generator()
incijalizira stanje generatora slučajnih brojeva
Generiranje slučajnih cijelih brojeva:
-
rocrand_generate()
stvara uniformno distribuirane 32-bitne cijele brojeverocrand_generate_short()
stvara 16-bitnerocrand_generate_char()
stvara 8-bitne
Generiranje slučajnih brojeva s pomičnim zarezom:
-
rocrand_generate_log_normal()
stvara 32-bitne logaritamski normalno distribuirane brojeve s pomičnim zarezomrocrand_generate_log_normal_half()
stvara 16-bitnerocrand_generate_log_normal_double()
stvara 64-bitne
-
rocrand_generate_normal()
stvara 32-bitne normalno distribuirane brojeve s pomičnim zarezomrocrand_generate_normal_half()
stvara 16-bitnerocrand_generate_normal_double()
stvara 64-bitne
-
rocrand_generate_poisson()
stvara 32-bitne Poissonovo distribuirane brojeve s pomičnim zarezomrocrand_generate_poisson_half()
stvara 16-bitnerocrand_generate_poisson_double()
stvara 64-bitne
-
rocrand_generate_uniform()
stvara 32-bitne uniformno distribuirane brojeve s pomičnim zarezomrocrand_generate_uniform_half()
stvara 16-bitnerocrand_generate_uniform_double()
stvara 64-bitne
Ostale funkcije:
rocrand_set_stream()
postavlja trenutni tok u kojem se pokreću zrnarocrand_set_seed()
postavlja sjemenku generatora slučajnih brojevarocrand_set_offset()
postavlja odmak generatora slučajnih brojevarocrand_set_quasi_random_generator_dimensions()
postavlja broj dimenzija generatora kvazislučajnih brojevarocrand_get_version()
vraća verziju biblioteke
Funkcije koje rade na uređaju
(Za više informacija proučite rocRAND device functions.)
Primjer korištenja
Službeni primjer benchmark/benchmark_rocrand_generate.cpp
(datoteka u repozitoriju rocRAND na GitHubu) za prevođenje u izvršni kod zahtijeva i datoteku zaglavlja benchmark/cmdparser.hpp
(datoteka u repozitoriju rocRAND na GitHubu). Dodatni primjer je benchmark/benchmark_rocrand_kernel.cpp
(datoteka u repozitoriju rocRAND na GitHubu).
Ovaj program prikazuje kako se koriste funkcije za generiranje slučajnih brojeva.
Pogledajmo funkciju main()
od koje program kreće:
int main(int argc, char *argv[])
{
cli::Parser parser(argc, argv);
const std::string distribution_desc =
"space-separated list of distributions:" +
std::accumulate(all_distributions.begin(), all_distributions.end(), std::string(),
[](std::string a, std::string b) {
return a + "\n " + b;
}
) +
"\n or all";
const std::string engine_desc =
"space-separated list of random number engines:" +
std::accumulate(all_engines.begin(), all_engines.end(), std::string(),
[](std::string a, std::string b) {
return a + "\n " + b;
}
) +
"\n or all";
parser.set_optional<size_t>("size", "size", DEFAULT_RAND_N, "number of values");
parser.set_optional<size_t>("dimensions", "dimensions", 1, "number of dimensions of quasi-random values");
parser.set_optional<size_t>("trials", "trials", 20, "number of trials");
parser.set_optional<std::vector<std::string>>("dis", "dis", {"uniform-uint"}, distribution_desc.c_str());
parser.set_optional<std::vector<std::string>>("engine", "engine", {"philox"}, engine_desc.c_str());
parser.set_optional<std::vector<double>>("lambda", "lambda", {10.0}, "space-separated list of lambdas of Poisson distribution");
parser.run_and_exit_if_error();
std::vector<std::string> engines;
{
auto es = parser.get<std::vector<std::string>>("engine");
if (std::find(es.begin(), es.end(), "all") != es.end())
{
engines = all_engines;
}
else
{
for (auto e : all_engines)
{
if (std::find(es.begin(), es.end(), e) != es.end())
engines.push_back(e);
}
}
}
std::vector<std::string> distributions;
{
auto ds = parser.get<std::vector<std::string>>("dis");
if (std::find(ds.begin(), ds.end(), "all") != ds.end())
{
distributions = all_distributions;
}
else
{
for (auto d : all_distributions)
{
if (std::find(ds.begin(), ds.end(), d) != ds.end())
distributions.push_back(d);
}
}
}
int version;
ROCRAND_CHECK(rocrand_get_version(&version));
int runtime_version;
HIP_CHECK(hipRuntimeGetVersion(&runtime_version));
int device_id;
HIP_CHECK(hipGetDevice(&device_id));
hipDeviceProp_t props;
HIP_CHECK(hipGetDeviceProperties(&props, device_id));
std::cout << "rocRAND: " << version << " ";
std::cout << "Runtime: " << runtime_version << " ";
std::cout << "Device: " << props.name;
std::cout << std::endl << std::endl;
for (auto engine : engines)
{
rng_type_t rng_type = ROCRAND_RNG_PSEUDO_XORWOW;
if (engine == "xorwow")
rng_type = ROCRAND_RNG_PSEUDO_XORWOW;
else if (engine == "mrg32k3a")
rng_type = ROCRAND_RNG_PSEUDO_MRG32K3A;
else if (engine == "philox")
rng_type = ROCRAND_RNG_PSEUDO_PHILOX4_32_10;
else if (engine == "sobol32")
rng_type = ROCRAND_RNG_QUASI_SOBOL32;
else if (engine == "mtgp32")
rng_type = ROCRAND_RNG_PSEUDO_MTGP32;
else
{
std::cout << "Wrong engine name" << std::endl;
exit(1);
}
std::cout << engine << ":" << std::endl;
for (auto distribution : distributions)
{
std::cout << " " << distribution << ":" << std::endl;
run_benchmarks(parser, rng_type, distribution);
}
std::cout << std::endl;
}
return 0;
}
Program počinje inicijalizacijom varijable parser
tipa cli::Parser
koji je definiran u ranije spomenutoj datoteci cmdparser.hpp
; ta će se varijabla koristiti za rasčlanjivanje raznih vrsta naredbi.
Postavljaju se konstante tipa std::string
koje će pohranjivati informacije o razdiobama i generatorima i to tako da su informacije odvojene znakom razmaka. Posebno se postavljaju početak i kraj tog znakovnog niza pomoću funkcije std::accumulate
(za više informacija proučite std::accumulate na cppreference.com):
const std::string distribution_desc =
"space-separated list of distributions:" +
std::accumulate(all_distributions.begin(), all_distributions.end(), std::string(),
[](std::string a, std::string b) {
return a + "\n " + b;
}
) +
"\n or all";
const std::string engine_desc =
"space-separated list of random number engines:" +
std::accumulate(all_engines.begin(), all_engines.end(), std::string(),
[](std::string a, std::string b) {
return a + "\n " + b;
}
) +
"\n or all";
Slijedi postavljanje informacija kao što su veličina, dimenzije, zadatci, razdiobe, generatori i lambda (čiju ćemo svrhu objasniti u nastavku) u rasčlanjivač naredbi.
Kao zadnja naredba stoji pokretanje i izlaz u slučaju greške.
parser.set_optional<size_t>("size", "size", DEFAULT_RAND_N, "number of values");
parser.set_optional<size_t>("dimensions", "dimensions", 1, "number of dimensions of quasi-random values");
parser.set_optional<size_t>("trials", "trials", 20, "number of trials");
parser.set_optional<std::vector<std::string>>("dis", "dis", {"uniform-uint"}, distribution_desc.c_str());
parser.set_optional<std::vector<std::string>>("engine", "engine", {"philox"}, engine_desc.c_str());
parser.set_optional<std::vector<double>>("lambda", "lambda", {10.0}, "space-separated list of lambdas of Poisson distribution");
parser.run_and_exit_if_error();
Nastavljamo na dio koji puni vektore s imenima pogona za generiranje slučajnih brojeva engines
i razdioba slučajnih brojeva distributions
. Kao uvjet stoji da funkcija std::find
(za više informacija proučite std::find na cppreference.com), sa određenim početkom i krajem, traži sve elemente u vektoru koji nisu jednaki kao zadnji, da bi na taj način upisala sve generatore iz prethodno definiranog vektora all_engines
u vektor engines
. Jednake naredbe stoje i za popis razdioba:
std::vector<std::string> engines;
{
auto es = parser.get<std::vector<std::string>>("engine");
if (std::find(es.begin(), es.end(), "all") != es.end())
{
engines = all_engines;
}
else
{
for (auto e : all_engines)
{
if (std::find(es.begin(), es.end(), e) != es.end())
engines.push_back(e);
}
}
}
std::vector<std::string> distributions;
{
auto ds = parser.get<std::vector<std::string>>("dis");
if (std::find(ds.begin(), ds.end(), "all") != ds.end())
{
distributions = all_distributions;
}
else
{
for (auto d : all_distributions)
{
if (std::find(ds.begin(), ds.end(), d) != ds.end())
distributions.push_back(d);
}
}
}
Vektori znakovnih nizova all_engines
i all_distributions
izvan funkcije main()
su oblika:
const std::vector<std::string> all_engines = {
"xorwow",
"mrg32k3a",
"mtgp32",
"philox",
"sobol32",
};
const std::vector<std::string> all_distributions = {
"uniform-uint",
"uniform-uchar",
"uniform-ushort",
"uniform-half",
// "uniform-long-long",
"uniform-float",
"uniform-double",
"normal-half",
"normal-float",
"normal-double",
"log-normal-half",
"log-normal-float",
"log-normal-double",
"poisson"
};
Slijedi provjera funkcijom ROCRAND_CHECK
u kojoj se dobavlja verzija biblioteke putem funkcije rocrand_get_version()
spomenute na početku. Uz ovu provjeru, slijede još tri HIP_CHECK
koji dohvaćaju runtime verziju, identifikacijski broj uređaja i svojstava tog uređaja.
int version;
ROCRAND_CHECK(rocrand_get_version(&version));
int runtime_version;
HIP_CHECK(hipRuntimeGetVersion(&runtime_version));
int device_id;
HIP_CHECK(hipGetDevice(&device_id));
hipDeviceProp_t props;
HIP_CHECK(hipGetDeviceProperties(&props, device_id));
std::cout << "rocRAND: " << version << " ";
std::cout << "Runtime: " << runtime_version << " ";
std::cout << "Device: " << props.name;
std::cout << std::endl << std::endl;
Nakon što su se ispisale iznad navedene informacije, nastavlja se petlja for
u kojoj se na temelju znakovnog niza određuje tip generatora koji će se koristiti. Za svaki pojedini pogon se izvodi mjerenje performansi sa svakom od razdioba, što vidimo u dodatnoj petlji for.
for (auto engine : engines)
{
rng_type_t rng_type = ROCRAND_RNG_PSEUDO_XORWOW;
if (engine == "xorwow")
rng_type = ROCRAND_RNG_PSEUDO_XORWOW;
else if (engine == "mrg32k3a")
rng_type = ROCRAND_RNG_PSEUDO_MRG32K3A;
else if (engine == "philox")
rng_type = ROCRAND_RNG_PSEUDO_PHILOX4_32_10;
else if (engine == "sobol32")
rng_type = ROCRAND_RNG_QUASI_SOBOL32;
else if (engine == "mtgp32")
rng_type = ROCRAND_RNG_PSEUDO_MTGP32;
else
{
std::cout << "Wrong engine name" << std::endl;
exit(1);
}
std::cout << engine << ":" << std::endl;
for (auto distribution : distributions)
{
std::cout << " " << distribution << ":" << std::endl;
run_benchmarks(parser, rng_type, distribution);
}
std::cout << std::endl;
}
Došli smo do kraja funkcije main()
.
Pogledajmo malo detaljnije funkciju koja se poziva u dvije petlje for
opisane iznad, funkciju run_benchmarks()
(uočite množinu):
void run_benchmarks(const cli::Parser& parser,
const rng_type_t rng_type,
const std::string& distribution)
{
if (distribution == "uniform-uint")
{
run_benchmark<unsigned int>(parser, rng_type,
[](rocrand_generator gen, unsigned int * data, size_t size) {
return rocrand_generate(gen, data, size);
}
);
}
if (distribution == "uniform-uchar")
{
run_benchmark<unsigned char>(parser, rng_type,
[](rocrand_generator gen, unsigned char * data, size_t size) {
return rocrand_generate_char(gen, data, size);
}
);
}
if (distribution == "uniform-ushort")
{
run_benchmark<unsigned short>(parser, rng_type,
[](rocrand_generator gen, unsigned short * data, size_t size) {
return rocrand_generate_short(gen, data, size);
}
);
}
if (distribution == "uniform-half")
{
run_benchmark<__half>(parser, rng_type,
[](rocrand_generator gen, __half * data, size_t size) {
return rocrand_generate_uniform_half(gen, data, size);
}
);
}
if (distribution == "uniform-float")
{
run_benchmark<float>(parser, rng_type,
[](rocrand_generator gen, float * data, size_t size) {
return rocrand_generate_uniform(gen, data, size);
}
);
}
if (distribution == "uniform-double")
{
run_benchmark<double>(parser, rng_type,
[](rocrand_generator gen, double * data, size_t size) {
return rocrand_generate_uniform_double(gen, data, size);
}
);
}
if (distribution == "normal-half")
{
run_benchmark<__half>(parser, rng_type,
[](rocrand_generator gen, __half * data, size_t size) {
return rocrand_generate_normal_half(gen, data, size, 0.0f, 1.0f);
}
);
}
if (distribution == "normal-float")
{
run_benchmark<float>(parser, rng_type,
[](rocrand_generator gen, float * data, size_t size) {
return rocrand_generate_normal(gen, data, size, 0.0f, 1.0f);
}
);
}
if (distribution == "normal-double")
{
run_benchmark<double>(parser, rng_type,
[](rocrand_generator gen, double * data, size_t size) {
return rocrand_generate_normal_double(gen, data, size, 0.0, 1.0);
}
);
}
if (distribution == "log-normal-half")
{
run_benchmark<__half>(parser, rng_type,
[](rocrand_generator gen, __half * data, size_t size) {
return rocrand_generate_log_normal_half(gen, data, size, 0.0f, 1.0f);
}
);
}
if (distribution == "log-normal-float")
{
run_benchmark<float>(parser, rng_type,
[](rocrand_generator gen, float * data, size_t size) {
return rocrand_generate_log_normal(gen, data, size, 0.0f, 1.0f);
}
);
}
if (distribution == "log-normal-double")
{
run_benchmark<double>(parser, rng_type,
[](rocrand_generator gen, double * data, size_t size) {
return rocrand_generate_log_normal_double(gen, data, size, 0.0, 1.0);
}
);
}
if (distribution == "poisson")
{
const auto lambdas = parser.get<std::vector<double>>("lambda");
for (double lambda : lambdas)
{
std::cout << " " << "lambda "
<< std::fixed << std::setprecision(1) << lambda << std::endl;
run_benchmark<unsigned int>(parser, rng_type,
[lambda](rocrand_generator gen, unsigned int * data, size_t size) {
return rocrand_generate_poisson(gen, data, size, lambda);
}
);
}
}
}
Ova je funkcija u kojoj se ispituje o kojoj je razdiobi riječ. Sastoji se od trinaest grananja naredbom if
, svaka od kojih uspoređuje trenutnu vrijednost varijable distribution
sa svakom vrijednosti iz liste all_distributions
. Unutar svakog if
-a poziva se funkcija generatora slučajnih brojeva koju ćemo detaljnije razmotriti u nastavku.
Za primjer uzmimo naredbu if
koja se odnosi na uniformnu razdiobu prirodnih brojeva:
if (distribution == "uniform-uint")
{
run_benchmark<unsigned int>(parser, rng_type,
[](rocrand_generator gen, unsigned int * data, size_t size) {
return rocrand_generate(gen, data, size);
}
);
}
Da bi funkcija run_benchmark()
(uočite jedninu) bila pozvana, mora biti zadovoljen uvjet usporedbe. Ako je uvjet zadovoljen, funkcija run_benchmark()
poziva rocRAND funkciju rocrand_generate_...()
s generatorom slučajnih brojeva gen
tipa rocrand_generator
, mjestom za pohranu podataka data
i brojem slučajnih brojeva koji će biti generirani size
.
U ovom slučaju rocrand_generate()
vratiti će nepredznačenu cjelobrojnu vrijednost obzirom da razdioba tako nalaže (za više informacija o uniformnoj cjelobrojnoj razdiobi pogledajte std::uniform_int_distribution na cppreference.com).
Ako malo obratite pozornost na svaku od razdioba, primjetiti ćete da svaka traži drugačiji tip vrijednosti:
"uniform-uchar"
tražiunsigned char
:
if (distribution == "uniform-uchar")
{
run_benchmark<unsigned char>(parser, rng_type,
[](rocrand_generator gen, unsigned char * data, size_t size) {
return rocrand_generate_char(gen, data, size);
}
);
}
uniform-ushort"
tražiunsigned short
:
if (distribution == "uniform-ushort")
{
run_benchmark<unsigned short>(parser, rng_type,
[](rocrand_generator gen, unsigned short * data, size_t size) {
return rocrand_generate_short(gen, data, size);
}
);
}
Ako pogledate svaku sljedeću, primjetiti ćete da se pojavljuju i tipovi "normal-half"
, "normal-float"
, "normal-double"
(za više informacija o normalnoj razdiobi proučite std::normal_distribution na cppreference.com). Ovdje se traži normalna razdioba 32-bitnih brojeva s pomičnim zarezom.
if (distribution == "normal-float")
{
run_benchmark<float>(parser, rng_type,
[](rocrand_generator gen, float * data, size_t size) {
return rocrand_generate_normal(gen, data, size, 0.0f, 1.0f);
}
);
}
Također, pojavljuju se znakovni nizovi "log-normal-*"
s različitim tipovima koji označavaju logaritamsku normalnu razdiobu za brojeve s pomičnim zarezom (za više informacija o logaritamskoj normalnoj razdiobi proučite std::lognormal_distribution na cppreference.com).
if (distribution == "log-normal-float")
{
run_benchmark<float>(parser, rng_type,
[](rocrand_generator gen, float * data, size_t size) {
return rocrand_generate_log_normal(gen, data, size, 0.0f, 1.0f);
}
);
}
Posljednja razdioba koji vidimo jest "poisson"
, odnosno Poissonova.
if (distribution == "poisson")
{
const auto lambdas = parser.get<std::vector<double>>("lambda");
for (double lambda : lambdas)
{
std::cout << " " << "lambda "
<< std::fixed << std::setprecision(1) << lambda << std::endl;
run_benchmark<unsigned int>(parser, rng_type,
[lambda](rocrand_generator gen, unsigned int * data, size_t size) {
return rocrand_generate_poisson(gen, data, size, lambda);
}
);
}
}
Poissonova razdioba se koristi za opis rijetkih događaja, odnosno događaja koji imaju veliki uzorak i malu vjerojatnost i ovisi samo o parametru lambda
. Promjenom parametra lambda
mijenja se oblik razdiobe (za više informacija o Poissonovoj razdiobi proučite std::poisson_distribution na cppreference.com).
Pogledajmo sada detaljnije funkciju run_benchmark
:
void run_benchmark(const cli::Parser& parser,
const rng_type_t rng_type,
generate_func_type<T> generate_func)
{
const size_t size0 = parser.get<size_t>("size");
const size_t trials = parser.get<size_t>("trials");
const size_t dimensions = parser.get<size_t>("dimensions");
const size_t size = (size0 / dimensions) * dimensions;
T * data;
HIP_CHECK(hipMalloc((void **)&data, size * sizeof(T)));
rocrand_generator generator;
ROCRAND_CHECK(rocrand_create_generator(&generator, rng_type));
rocrand_status status = rocrand_set_quasi_random_generator_dimensions(generator, dimensions);
if (status != ROCRAND_STATUS_TYPE_ERROR) // If the RNG is not quasi-random
{
ROCRAND_CHECK(status);
}
// Warm-up
for (size_t i = 0; i < 5; i++)
{
ROCRAND_CHECK(generate_func(generator, data, size));
}
HIP_CHECK(hipDeviceSynchronize());
// Measurement
auto start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < trials; i++)
{
ROCRAND_CHECK(generate_func(generator, data, size));
}
HIP_CHECK(hipDeviceSynchronize());
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
std::cout << std::fixed << std::setprecision(3)
<< " "
<< "Throughput = "
<< std::setw(8) << (trials * size * sizeof(T)) /
(elapsed.count() / 1e3 * (1 << 30))
<< " GB/s, Samples = "
<< std::setw(8) << (trials * size) /
(elapsed.count() / 1e3 * (1 << 30))
<< " GSample/s, AvgTime (1 trial) = "
<< std::setw(8) << elapsed.count() / trials
<< " ms, Time (all) = "
<< std::setw(8) << elapsed.count()
<< " ms, Size = " << size
<< std::endl;
ROCRAND_CHECK(rocrand_destroy_generator(generator));
HIP_CHECK(hipFree(data));
}
Ako pogledate pomnije u kod ovog programa, primjetiti ćete da je na početku samog koda definiran predložak:
template<typename T>
using generate_func_type = std::function<rocrand_status(rocrand_generator, T *, size_t)>;
U deklaraciji funkcije run_benchmark
stoji generate_func
koji je proizašao upravo iz tog definiranog predloška, kojeg se koristi u nastavku koda.
Slijedi definicija varijabli size0
, trials
, dimensions
i size
gdje su svi dobiveni pomoću Parser dohvaćanja, osim veličine size
koja je umnožak vrijednosti dimenzije i rezultata dijeljenja size0
i dimensions
.
const size_t size0 = parser.get<size_t>("size");
const size_t trials = parser.get<size_t>("trials");
const size_t dimensions = parser.get<size_t>("dimensions");
const size_t size = (size0 / dimensions) * dimensions;
Nastavljamo sa HIP_CHECK provjerom alocirane memorije, i ROCRAND_CHECK provjerom generatora, i tipa generiranog broja.
rocrand_generator generator;
ROCRAND_CHECK(rocrand_create_generator(&generator, rng_type));
Nakon toga definiramo status
, čija će vrijednost biti jednaka rezultatu rocRAND funkcije rocrand_set_quasi_random_generator_dimensions
koji je zapravo broj dimenzija generatora quasi-slučajnih brojeva. (Za više informacija o ovoj funkciji možete pogledati na početak ovog teksta):
rocrand_status status = rocrand_set_quasi_random_generator_dimensions(generator, dimensions);
Slijedi provjera; ako vrijednost dobivena u status
nije quasi-nasumičan broj, pokreće se ROCRAND_CHECK provjera za status
.
if (status != ROCRAND_STATUS_TYPE_ERROR) // If the RNG is not quasi-random
{
ROCRAND_CHECK(status);
}
Sljedeće se pokreće petlja for
u kojoj se provodi ROCRAND_CHECK provjera generirane funkcije.
for (size_t i = 0; i < 5; i++)
{
ROCRAND_CHECK(generate_func(generator, data, size));
}
HIP_CHECK(hipDeviceSynchronize());
Započinje mjerenje vremena izvršavanja provjere u for
petlji koja ovaj puta broji do vrijednosti trials
, nakon čega slijedi HIP_CHECK provjera sinkronizacije uređaja:
for (size_t i = 0; i < trials; i++)
{
ROCRAND_CHECK(generate_func(generator, data, size));
}
HIP_CHECK(hipDeviceSynchronize());
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
Nakon svega slijedi ispis svih dobivenih rezultata tokom ove funkcije u obliku definiranom putem std::setprecision
(za više informacija o funkciji pogledajte std::setprecision na cppreference.com):
std::cout << std::fixed << std::setprecision(3)
<< " "
<< "Throughput = "
<< std::setw(8) << (trials * size * sizeof(T)) /
(elapsed.count() / 1e3 * (1 << 30))
<< " GB/s, Samples = "
<< std::setw(8) << (trials * size) /
(elapsed.count() / 1e3 * (1 << 30))
<< " GSample/s, AvgTime (1 trial) = "
<< std::setw(8) << elapsed.count() / trials
<< " ms, Time (all) = "
<< std::setw(8) << elapsed.count()
<< " ms, Size = " << size
<< std::endl;
Za kraj funkcije uništava se generator, i oslobađa se prethodno alocirana memorija.
ROCRAND_CHECK(rocrand_destroy_generator(generator));
HIP_CHECK(hipFree(data));
Uspješno smo prošli primjer programa koji koristi funkcije biblioteke rocRAND.
Author: Mia Doričić, Vedran Miletić