Skip to content

Commit

Permalink
Hide boost rng behind facade for osx compatibility
Browse files Browse the repository at this point in the history
Split boost random number generation from the common Caffe singleton and
add a helper function for rng. This resolves a build conflict in OSX
between boost rng and nvcc compilation of cuda code.

Refer to #165 for a full discussion.

Thanks to @satol for suggesting a random number generation facade rather
than a total split of cpp and cu code, which is far more involved.
  • Loading branch information
shelhamer committed Mar 22, 2014
1 parent aaa2646 commit 19bcf2b
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 51 deletions.
97 changes: 56 additions & 41 deletions include/caffe/common.hpp
Original file line number Diff line number Diff line change
@@ -1,33 +1,16 @@
// Copyright 2013 Yangqing Jia
// Copyright 2014 Evan Shelhamer

#ifndef CAFFE_COMMON_HPP_
#define CAFFE_COMMON_HPP_

#include <boost/random/mersenne_twister.hpp>
#include <boost/shared_ptr.hpp>
#include <cublas_v2.h>
#include <cuda.h>
#include <curand.h>
#include <driver_types.h> // cuda driver types
#include <glog/logging.h>

// various checks for different function calls.
#define CUDA_CHECK(condition) CHECK_EQ((condition), cudaSuccess)
#define CUBLAS_CHECK(condition) CHECK_EQ((condition), CUBLAS_STATUS_SUCCESS)
#define CURAND_CHECK(condition) CHECK_EQ((condition), CURAND_STATUS_SUCCESS)

#define CUDA_KERNEL_LOOP(i, n) \
for (int i = blockIdx.x * blockDim.x + threadIdx.x; \
i < (n); \
i += blockDim.x * gridDim.x)

// After a kernel is executed, this will check the error and if there is one,
// exit loudly.
#define CUDA_POST_KERNEL_CHECK \
if (cudaSuccess != cudaPeekAtLastError()) \
LOG(FATAL) << "Cuda kernel failed. Error: " \
<< cudaGetErrorString(cudaPeekAtLastError())

// Disable the copy and assignment operator for a class.
#define DISABLE_COPY_AND_ASSIGN(classname) \
private:\
Expand All @@ -43,24 +26,29 @@ private:\
// is executed we will see a fatal log.
#define NOT_IMPLEMENTED LOG(FATAL) << "Not Implemented Yet"

namespace caffe {
// CUDA: various checks for different function calls.
#define CUDA_CHECK(condition) CHECK_EQ((condition), cudaSuccess)
#define CUBLAS_CHECK(condition) CHECK_EQ((condition), CUBLAS_STATUS_SUCCESS)
#define CURAND_CHECK(condition) CHECK_EQ((condition), CURAND_STATUS_SUCCESS)

// We will use the boost shared_ptr instead of the new C++11 one mainly
// because cuda does not work (at least now) well with C++11 features.
using boost::shared_ptr;
// CUDA: grid stride looping
#define CUDA_KERNEL_LOOP(i, n) \
for (int i = blockIdx.x * blockDim.x + threadIdx.x; \
i < (n); \
i += blockDim.x * gridDim.x)

// CUDA: check for error after kernel execution and exit loudly if there is one.
#define CUDA_POST_KERNEL_CHECK \
if (cudaSuccess != cudaPeekAtLastError()) \
LOG(FATAL) << "Cuda kernel failed. Error: " \
<< cudaGetErrorString(cudaPeekAtLastError())

// We will use 1024 threads per block, which requires cuda sm_2x or above.
#if __CUDA_ARCH__ >= 200
const int CAFFE_CUDA_NUM_THREADS = 1024;
#else
const int CAFFE_CUDA_NUM_THREADS = 512;
#endif

namespace caffe {

inline int CAFFE_GET_BLOCKS(const int N) {
return (N + CAFFE_CUDA_NUM_THREADS - 1) / CAFFE_CUDA_NUM_THREADS;
}
// We will use the boost shared_ptr instead of the new C++11 one mainly
// because cuda does not work (at least now) well with C++11 features.
using boost::shared_ptr;


// A singleton class to hold common caffe stuff, such as the handler that
Expand All @@ -77,20 +65,32 @@ class Caffe {
enum Brew { CPU, GPU };
enum Phase { TRAIN, TEST };

// The getters for the variables.
// Returns the cublas handle.

// This random number generator facade hides boost and CUDA rng
// implementation from one another (for cross-platform compatibility).
class RNG {
public:
RNG();
explicit RNG(unsigned int seed);
~RNG();
RNG(const RNG&);
RNG& operator=(const RNG&);
const void* generator() const;
void* generator();
private:
class Generator;
Generator* generator_;
};

// Getters for boost rng, curand, and cublas handles
inline static RNG &rng_stream() {
return Get().random_generator_;
}
inline static cublasHandle_t cublas_handle() { return Get().cublas_handle_; }
// Returns the curand generator.
inline static curandGenerator_t curand_generator() {
return Get().curand_generator_;
}

// boost RNG
typedef boost::mt19937 random_generator_t;
inline static random_generator_t &rng_stream() {
return Get().random_generator_;
}

// Returns the mode: running on CPU or GPU.
inline static Brew mode() { return Get().mode_; }
// Returns the phase: TRAIN or TEST.
Expand All @@ -114,7 +114,7 @@ class Caffe {
protected:
cublasHandle_t cublas_handle_;
curandGenerator_t curand_generator_;
random_generator_t random_generator_;
RNG random_generator_;

Brew mode_;
Phase phase_;
Expand All @@ -128,6 +128,21 @@ class Caffe {
};


// CUDA: thread number configuration.
// Use 1024 threads per block, which requires cuda sm_2x or above,
// or fall back to attempt compatibility (best of luck to you).
#if __CUDA_ARCH__ >= 200
const int CAFFE_CUDA_NUM_THREADS = 1024;
#else
const int CAFFE_CUDA_NUM_THREADS = 512;
#endif

// CUDA: number of blocks for threads.
inline int CAFFE_GET_BLOCKS(const int N) {
return (N + CAFFE_CUDA_NUM_THREADS - 1) / CAFFE_CUDA_NUM_THREADS;
}


} // namespace caffe

#endif // CAFFE_COMMON_HPP_
19 changes: 19 additions & 0 deletions include/caffe/util/rng.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2014 Evan Shelhamer

#ifndef CAFFE_RNG_CPP_HPP_
#define CAFFE_RNG_CPP_HPP_

#include <boost/random/mersenne_twister.hpp>
#include "caffe/common.hpp"

namespace caffe {

typedef boost::mt19937 rng_t;
inline rng_t& caffe_rng() {
Caffe::RNG &generator = Caffe::rng_stream();
return *(caffe::rng_t*) generator.generator();
}

} // namespace caffe

#endif // CAFFE_RNG_HPP_
38 changes: 37 additions & 1 deletion src/caffe/common.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
// Copyright 2013 Yangqing Jia
// Copyright 2014 Evan Shelhamer

#include <cstdio>
#include <ctime>

#include "caffe/common.hpp"
#include "caffe/util/rng.hpp"

namespace caffe {

shared_ptr<Caffe> Caffe::singleton_;


// curand seeding
int64_t cluster_seedgen(void) {
int64_t s, seed, pid;
pid = getpid();
Expand Down Expand Up @@ -58,7 +61,7 @@ void Caffe::set_random_seed(const unsigned int seed) {
LOG(ERROR) << "Curand not available. Skipping setting the curand seed.";
}
// RNG seed
Get().random_generator_ = random_generator_t(seed);
Get().random_generator_ = RNG(seed);
}

void Caffe::SetDevice(const int device_id) {
Expand Down Expand Up @@ -112,4 +115,37 @@ void Caffe::DeviceQuery() {
return;
}


class Caffe::RNG::Generator {
public:
caffe::rng_t rng;
};

Caffe::RNG::RNG()
: generator_(new Generator) { }

Caffe::RNG::RNG(unsigned int seed)
: generator_(new Generator) {
generator_->rng = caffe::rng_t(seed);
}

Caffe::RNG::~RNG() { delete generator_; }

Caffe::RNG::RNG(const RNG& other) : generator_(new Generator) {
*generator_ = *other.generator_;
}

Caffe::RNG& Caffe::RNG::operator=(const RNG& other) {
*generator_ = *other.generator_;
return *this;
}

void* Caffe::RNG::generator() {
return &generator_->rng;
}

const void* Caffe::RNG::generator() const {
return &generator_->rng;
}

} // namespace caffe
17 changes: 8 additions & 9 deletions src/caffe/util/math_functions.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2013 Yangqing Jia
// Copyright 2014 kloudkl@github
// Copyright 2014 Evan Shelhamer

#include <boost/math/special_functions/next.hpp>
#include <boost/random.hpp>
Expand All @@ -9,6 +10,7 @@

#include "caffe/common.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp"

namespace caffe {

Expand Down Expand Up @@ -287,10 +289,9 @@ void caffe_vRngUniform(const int n, Dtype* r,

boost::uniform_real<Dtype> random_distribution(
a, caffe_nextafter<Dtype>(b));
Caffe::random_generator_t &generator = Caffe::rng_stream();
boost::variate_generator<Caffe::random_generator_t,
boost::variate_generator<caffe::rng_t,
boost::uniform_real<Dtype> > variate_generator(
generator, random_distribution);
caffe_rng(), random_distribution);

for (int i = 0; i < n; ++i) {
r[i] = variate_generator();
Expand All @@ -311,10 +312,9 @@ void caffe_vRngGaussian(const int n, Dtype* r, const Dtype a,
CHECK(r);
CHECK_GT(sigma, 0);
boost::normal_distribution<Dtype> random_distribution(a, sigma);
Caffe::random_generator_t &generator = Caffe::rng_stream();
boost::variate_generator<Caffe::random_generator_t,
boost::variate_generator<caffe::rng_t,
boost::normal_distribution<Dtype> > variate_generator(
generator, random_distribution);
caffe_rng(), random_distribution);

for (int i = 0; i < n; ++i) {
r[i] = variate_generator();
Expand All @@ -336,10 +336,9 @@ void caffe_vRngBernoulli(const int n, Dtype* r, const double p) {
CHECK_GE(p, 0);
CHECK_LE(p, 1);
boost::bernoulli_distribution<double> random_distribution(p);
Caffe::random_generator_t &generator = Caffe::rng_stream();
boost::variate_generator<Caffe::random_generator_t,
boost::variate_generator<caffe::rng_t,
boost::bernoulli_distribution<double> > variate_generator(
generator, random_distribution);
caffe_rng(), random_distribution);

for (int i = 0; i < n; ++i) {
r[i] = variate_generator();
Expand Down

0 comments on commit 19bcf2b

Please sign in to comment.