Program Listing for File rand.cpp¶
↰ Return to documentation for file (src/tensors/rand.cpp
)
#include "tensors/rand.h"
#include "tensors/tensor.h"
#include "tensors/tensor_operators.h"
#ifdef CUDA_FOUND
#include "gpu/cuda_helpers.h"
#include <curand.h>
#endif
namespace marian {
class StdlibRandomGenerator : public RandomGenerator {
private:
std::mt19937 engine_;
public:
StdlibRandomGenerator(size_t seed)
: RandomGenerator(seed), engine_((unsigned int)seed) {}
virtual void uniform(Tensor tensor, float a, float b) override;
virtual void normal(Tensor, float mean, float stddev) override;
};
#ifdef CUDA_FOUND
class CurandRandomGenerator : public RandomGenerator {
private:
DeviceId deviceId_;
curandGenerator_t generator_;
public:
CurandRandomGenerator(size_t seed, DeviceId deviceId);
~CurandRandomGenerator();
virtual void uniform(Tensor, float a, float b) override;
virtual void normal(Tensor, float mean, float stddev) override;
};
#endif
void StdlibRandomGenerator::uniform(Tensor tensor, float a, float b) {
matchOrAbort<float>(tensor->type());
ABORT_IF(tensor->getBackend()->getDeviceId().type != DeviceType::cpu,
"StdlibRandomGenerator can only be used for CPU tensors");
auto dist = std::uniform_real_distribution<float>(a, b);
auto gen = bind(dist, std::ref(engine_)); // does not change engine state without std::ref
auto begin = tensor->data<float>();
auto end = tensor->data<float>() + tensor->size();
std::generate(begin, end, gen);
}
void StdlibRandomGenerator::normal(Tensor tensor, float mean, float stddev) {
matchOrAbort<float>(tensor->type());
ABORT_IF(tensor->getBackend()->getDeviceId().type != DeviceType::cpu,
"StdlibRandomGenerator can only be used for CPU tensors");
auto dist = std::normal_distribution<float>(mean, stddev);
auto gen = bind(dist, std::ref(engine_)); // does not change engine state without std::ref
auto begin = tensor->data<float>();
auto end = tensor->data<float>() + tensor->size();
std::generate(begin, end, gen);
}
#ifdef CUDA_FOUND
CurandRandomGenerator::CurandRandomGenerator(size_t seed, DeviceId deviceId)
: RandomGenerator(seed), deviceId_(deviceId) {
if(deviceId_.type == DeviceType::gpu) {
cudaSetDevice((int)deviceId_.no);
CURAND_CHECK(curandCreateGenerator(&generator_, CURAND_RNG_PSEUDO_DEFAULT));
}
else {
CURAND_CHECK(curandCreateGeneratorHost(&generator_, CURAND_RNG_PSEUDO_DEFAULT));
}
CURAND_CHECK(curandSetPseudoRandomGeneratorSeed(generator_, seed_));
}
CurandRandomGenerator::~CurandRandomGenerator() {
// No CUDA error checking as this is a destructor and we cannot do anything about errors anyway.
if(deviceId_.type == DeviceType::gpu)
cudaSetDevice((int)deviceId_.no);
curandDestroyGenerator(generator_);
}
void CurandRandomGenerator::uniform(Tensor tensor, float a, float b) {
matchOrAbort<float>(tensor->type());
tensor->getBackend()->setDevice();
CURAND_CHECK(curandGenerateUniform(generator_, tensor->data(), tensor->size()));
// curandGenerateUniform has no range parameters (why?) so we need to
// scale and shift inplace if range is different than [0, 1).
using namespace functional;
if(a != 0.f || b != 1.f)
Element(_1 = (b - a) * _1 + a, tensor);
}
void CurandRandomGenerator::normal(Tensor tensor, float mean, float stddev) {
matchOrAbort<float>(tensor->type());
tensor->getBackend()->setDevice();
CURAND_CHECK(curandGenerateNormal(generator_, tensor->data(), tensor->size(), mean, stddev));
}
#endif
Ptr<RandomGenerator> createRandomGenerator(size_t seed, DeviceId deviceId) {
#ifdef CUDA_FOUND
return New<CurandRandomGenerator>(seed, deviceId);
#else
ABORT_IF(deviceId.type != DeviceType::cpu,
"StdlibRandomGenerator can only be used for CPU tensors");
return New<StdlibRandomGenerator>(seed);
#endif
}
}