Apply consistent code formatting across the repo. Add clang-format and pre-commit hooks.

This commit is contained in:
Rob Armstrong
2025-03-27 10:30:07 -07:00
parent 2cd58fbc9a
commit ceab6e8bcc
782 changed files with 107230 additions and 106548 deletions

View File

@@ -39,196 +39,201 @@
///////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <iomanip>
#include <stdexcept>
#include <cuda_runtime.h>
#include <helper_timer.h>
#include <helper_cuda.h>
#include <helper_timer.h>
#include <iomanip>
#include <iostream>
#include <math.h>
#include <stdexcept>
#include "../inc/test.h"
// Forward declarations
void showHelp(const int argc, const char **argv);
template <typename Real>
void runTest(int argc, const char **argv);
void showHelp(const int argc, const char **argv);
template <typename Real> void runTest(int argc, const char **argv);
int main(int argc, char **argv) {
using std::invalid_argument;
using std::string;
int main(int argc, char **argv)
{
using std::invalid_argument;
using std::string;
// Open the log file
printf("Monte Carlo Estimate Pi (with inline QRNG)\n");
printf("==========================================\n\n");
// Open the log file
printf("Monte Carlo Estimate Pi (with inline QRNG)\n");
printf("==========================================\n\n");
// If help flag is set, display help and exit immediately
if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
printf("Displaying help on console\n");
showHelp(argc, (const char **)argv);
exit(EXIT_SUCCESS);
}
// Check the precision (checked against the device capability later)
try {
char *value;
if (getCmdLineArgumentString(argc, (const char **)argv, "precision",
&value)) {
// Check requested precision is valid
string prec(value);
if (prec.compare("single") == 0 || prec.compare("\"single\"") == 0) {
runTest<float>(argc, (const char **)argv);
} else if (prec.compare("double") == 0 ||
prec.compare("\"double\"") == 0) {
runTest<double>(argc, (const char **)argv);
} else {
printf(
"specified precision (%s) is invalid, must be \"single\" or "
"\"double\".\n",
value);
throw invalid_argument("precision");
}
} else {
runTest<float>(argc, (const char **)argv);
// If help flag is set, display help and exit immediately
if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
printf("Displaying help on console\n");
showHelp(argc, (const char **)argv);
exit(EXIT_SUCCESS);
}
} catch (invalid_argument &e) {
printf("invalid command line argument (%s)\n", e.what());
exit(EXIT_FAILURE);
}
// Finish
exit(EXIT_SUCCESS);
// Check the precision (checked against the device capability later)
try {
char *value;
if (getCmdLineArgumentString(argc, (const char **)argv, "precision", &value)) {
// Check requested precision is valid
string prec(value);
if (prec.compare("single") == 0 || prec.compare("\"single\"") == 0) {
runTest<float>(argc, (const char **)argv);
}
else if (prec.compare("double") == 0 || prec.compare("\"double\"") == 0) {
runTest<double>(argc, (const char **)argv);
}
else {
printf("specified precision (%s) is invalid, must be \"single\" or "
"\"double\".\n",
value);
throw invalid_argument("precision");
}
}
else {
runTest<float>(argc, (const char **)argv);
}
}
catch (invalid_argument &e) {
printf("invalid command line argument (%s)\n", e.what());
exit(EXIT_FAILURE);
}
// Finish
exit(EXIT_SUCCESS);
}
template <typename Real>
void runTest(int argc, const char **argv) {
using std::invalid_argument;
using std::runtime_error;
template <typename Real> void runTest(int argc, const char **argv)
{
using std::invalid_argument;
using std::runtime_error;
StopWatchInterface *timer = NULL;
StopWatchInterface *timer = NULL;
try {
Test<Real> test;
int deviceCount = 0;
cudaError_t cudaResult = cudaSuccess;
try {
Test<Real> test;
int deviceCount = 0;
cudaError_t cudaResult = cudaSuccess;
// by default specify GPU Device == 0
test.device = 0;
// by default specify GPU Device == 0
test.device = 0;
// Get number of available devices
cudaResult = cudaGetDeviceCount(&deviceCount);
if (cudaResult != cudaSuccess) {
printf("could not get device count.\n");
throw runtime_error("cudaGetDeviceCount");
}
// (default parameters)
test.numSims = k_sims_qa;
test.threadBlockSize = k_bsize_qa;
{
char *value = 0;
if (getCmdLineArgumentString(argc, argv, "device", &value)) {
test.device = (int)atoi(value);
if (test.device >= deviceCount) {
printf(
"invalid target device specified on command line (device %d does "
"not exist).\n",
test.device);
throw invalid_argument("device");
}
} else {
test.device = gpuGetMaxGflopsDeviceId();
}
if (getCmdLineArgumentString(argc, argv, "sims", &value)) {
test.numSims = (unsigned int)atoi(value);
if (test.numSims < k_sims_min || test.numSims > k_sims_max) {
printf(
"specified number of simulations (%d) is invalid, must be "
"between %d and %d.\n",
test.numSims, k_sims_min, k_sims_max);
throw invalid_argument("sims");
}
} else {
test.numSims = k_sims_def;
}
if (getCmdLineArgumentString(argc, argv, "block-size", &value)) {
// Determine max threads per block
cudaDeviceProp deviceProperties;
cudaResult = cudaGetDeviceProperties(&deviceProperties, test.device);
// Get number of available devices
cudaResult = cudaGetDeviceCount(&deviceCount);
if (cudaResult != cudaSuccess) {
printf("cound not get device properties for device %d.\n",
test.device);
throw runtime_error("cudaGetDeviceProperties");
printf("could not get device count.\n");
throw runtime_error("cudaGetDeviceCount");
}
// Check requested size is valid
test.threadBlockSize = (unsigned int)atoi(value);
// (default parameters)
test.numSims = k_sims_qa;
test.threadBlockSize = k_bsize_qa;
if (test.threadBlockSize < k_bsize_min ||
test.threadBlockSize > static_cast<unsigned int>(
deviceProperties.maxThreadsPerBlock)) {
printf(
"specified block size (%d) is invalid, must be between %d and %d "
"for device %d.\n",
test.threadBlockSize, k_bsize_min,
deviceProperties.maxThreadsPerBlock, test.device);
throw invalid_argument("block-size");
}
{
char *value = 0;
if (test.threadBlockSize & test.threadBlockSize - 1) {
printf(
"specified block size (%d) is invalid, must be a power of two "
"(see reduction function).\n",
test.threadBlockSize);
throw invalid_argument("block-size");
if (getCmdLineArgumentString(argc, argv, "device", &value)) {
test.device = (int)atoi(value);
if (test.device >= deviceCount) {
printf("invalid target device specified on command line (device %d does "
"not exist).\n",
test.device);
throw invalid_argument("device");
}
}
else {
test.device = gpuGetMaxGflopsDeviceId();
}
if (getCmdLineArgumentString(argc, argv, "sims", &value)) {
test.numSims = (unsigned int)atoi(value);
if (test.numSims < k_sims_min || test.numSims > k_sims_max) {
printf("specified number of simulations (%d) is invalid, must be "
"between %d and %d.\n",
test.numSims,
k_sims_min,
k_sims_max);
throw invalid_argument("sims");
}
}
else {
test.numSims = k_sims_def;
}
if (getCmdLineArgumentString(argc, argv, "block-size", &value)) {
// Determine max threads per block
cudaDeviceProp deviceProperties;
cudaResult = cudaGetDeviceProperties(&deviceProperties, test.device);
if (cudaResult != cudaSuccess) {
printf("cound not get device properties for device %d.\n", test.device);
throw runtime_error("cudaGetDeviceProperties");
}
// Check requested size is valid
test.threadBlockSize = (unsigned int)atoi(value);
if (test.threadBlockSize < k_bsize_min
|| test.threadBlockSize > static_cast<unsigned int>(deviceProperties.maxThreadsPerBlock)) {
printf("specified block size (%d) is invalid, must be between %d and %d "
"for device %d.\n",
test.threadBlockSize,
k_bsize_min,
deviceProperties.maxThreadsPerBlock,
test.device);
throw invalid_argument("block-size");
}
if (test.threadBlockSize & test.threadBlockSize - 1) {
printf("specified block size (%d) is invalid, must be a power of two "
"(see reduction function).\n",
test.threadBlockSize);
throw invalid_argument("block-size");
}
}
else {
test.threadBlockSize = k_bsize_def;
}
}
} else {
test.threadBlockSize = k_bsize_def;
}
// Execute
test();
}
catch (invalid_argument &e) {
printf("invalid command line argument (%s)\n", e.what());
exit(EXIT_FAILURE);
}
catch (runtime_error &e) {
printf("runtime error (%s)\n", e.what());
exit(EXIT_FAILURE);
}
// Execute
test();
} catch (invalid_argument &e) {
printf("invalid command line argument (%s)\n", e.what());
exit(EXIT_FAILURE);
} catch (runtime_error &e) {
printf("runtime error (%s)\n", e.what());
exit(EXIT_FAILURE);
}
}
void showHelp(int argc, const char **argv) {
using std::cout;
using std::endl;
using std::left;
using std::setw;
void showHelp(int argc, const char **argv)
{
using std::cout;
using std::endl;
using std::left;
using std::setw;
if (argc > 0) {
cout << endl << argv[0] << endl;
}
if (argc > 0) {
cout << endl << argv[0] << endl;
}
cout << endl << "Syntax:" << endl;
cout << left;
cout << " " << setw(20) << "--device=<device>"
<< "Specify device to use for execution" << endl;
cout << " " << setw(20) << "--sims=<N>"
<< "Specify number of Monte Carlo simulations" << endl;
cout << " " << setw(20) << "--block-size=<N>"
<< "Specify number of threads per block" << endl;
cout << " " << setw(20) << "--precision=<P>"
<< "Specify the precision (\"single\" or \"double\")" << endl;
cout << endl;
cout << " " << setw(20) << "--noprompt"
<< "Skip prompt before exit" << endl;
cout << endl;
cout << endl << "Syntax:" << endl;
cout << left;
cout << " " << setw(20) << "--device=<device>"
<< "Specify device to use for execution" << endl;
cout << " " << setw(20) << "--sims=<N>"
<< "Specify number of Monte Carlo simulations" << endl;
cout << " " << setw(20) << "--block-size=<N>"
<< "Specify number of threads per block" << endl;
cout << " " << setw(20) << "--precision=<P>"
<< "Specify the precision (\"single\" or \"double\")" << endl;
cout << endl;
cout << " " << setw(20) << "--noprompt"
<< "Skip prompt before exit" << endl;
cout << endl;
}

View File

@@ -26,15 +26,15 @@
*/
#include "../inc/piestimator.h"
#include <string>
#include <vector>
#include <cooperative_groups.h>
#include <cuda_runtime.h>
#include <numeric>
#include <stdexcept>
#include <string>
#include <typeinfo>
#include <cuda_runtime.h>
#include <cooperative_groups.h>
#include <vector>
#include "../inc/piestimator.h"
namespace cg = cooperative_groups;
#include <curand.h>
@@ -46,346 +46,329 @@ using std::string;
using std::vector;
// Helper templates to support float and double in same code
template <typename L, typename R>
struct TYPE_IS {
static const bool test = false;
template <typename L, typename R> struct TYPE_IS
{
static const bool test = false;
};
template <typename L>
struct TYPE_IS<L, L> {
static const bool test = true;
template <typename L> struct TYPE_IS<L, L>
{
static const bool test = true;
};
template <bool, class L, class R>
struct IF {
typedef R type;
template <bool, class L, class R> struct IF
{
typedef R type;
};
template <class L, class R>
struct IF<true, L, R> {
typedef L type;
template <class L, class R> struct IF<true, L, R>
{
typedef L type;
};
// RNG init kernel
template <typename rngState_t, typename rngDirectionVectors_t>
__global__ void initRNG(rngState_t *const rngStates,
rngDirectionVectors_t *const rngDirections,
unsigned int numDrawsPerDirection) {
// Determine thread ID
unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;
unsigned int step = gridDim.x * blockDim.x;
__global__ void
initRNG(rngState_t *const rngStates, rngDirectionVectors_t *const rngDirections, unsigned int numDrawsPerDirection)
{
// Determine thread ID
unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;
unsigned int step = gridDim.x * blockDim.x;
// Determine offset to avoid overlapping sub-sequences
unsigned int offset = tid * ((numDrawsPerDirection + step - 1) / step);
// Determine offset to avoid overlapping sub-sequences
unsigned int offset = tid * ((numDrawsPerDirection + step - 1) / step);
// Initialise the RNG
curand_init(rngDirections[0], offset, &rngStates[tid]);
curand_init(rngDirections[1], offset, &rngStates[tid + step]);
// Initialise the RNG
curand_init(rngDirections[0], offset, &rngStates[tid]);
curand_init(rngDirections[1], offset, &rngStates[tid + step]);
}
__device__ unsigned int reduce_sum(unsigned int in, cg::thread_block cta) {
extern __shared__ unsigned int sdata[];
__device__ unsigned int reduce_sum(unsigned int in, cg::thread_block cta)
{
extern __shared__ unsigned int sdata[];
// Perform first level of reduction:
// - Write to shared memory
unsigned int ltid = threadIdx.x;
// Perform first level of reduction:
// - Write to shared memory
unsigned int ltid = threadIdx.x;
sdata[ltid] = in;
cg::sync(cta);
sdata[ltid] = in;
cg::sync(cta);
// Do reduction in shared mem
for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
if (ltid < s) {
sdata[ltid] += sdata[ltid + s];
// Do reduction in shared mem
for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
if (ltid < s) {
sdata[ltid] += sdata[ltid + s];
}
cg::sync(cta);
}
cg::sync(cta);
}
return sdata[0];
return sdata[0];
}
__device__ inline void getPoint(float &x, float &y, curandStateSobol32 &state1,
curandStateSobol32 &state2) {
x = curand_uniform(&state1);
y = curand_uniform(&state2);
__device__ inline void getPoint(float &x, float &y, curandStateSobol32 &state1, curandStateSobol32 &state2)
{
x = curand_uniform(&state1);
y = curand_uniform(&state2);
}
__device__ inline void getPoint(double &x, double &y,
curandStateSobol64 &state1,
curandStateSobol64 &state2) {
x = curand_uniform_double(&state1);
y = curand_uniform_double(&state2);
__device__ inline void getPoint(double &x, double &y, curandStateSobol64 &state1, curandStateSobol64 &state2)
{
x = curand_uniform_double(&state1);
y = curand_uniform_double(&state2);
}
// Estimator kernel
template <typename Real, typename rngState_t>
__global__ void computeValue(unsigned int *const results,
rngState_t *const rngStates,
const unsigned int numSims) {
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
// Determine thread ID
unsigned int bid = blockIdx.x;
unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;
unsigned int step = gridDim.x * blockDim.x;
__global__ void computeValue(unsigned int *const results, rngState_t *const rngStates, const unsigned int numSims)
{
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
// Determine thread ID
unsigned int bid = blockIdx.x;
unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;
unsigned int step = gridDim.x * blockDim.x;
// Initialise the RNG
rngState_t localState1 = rngStates[tid];
rngState_t localState2 = rngStates[tid + step];
// Initialise the RNG
rngState_t localState1 = rngStates[tid];
rngState_t localState2 = rngStates[tid + step];
// Count the number of points which lie inside the unit quarter-circle
unsigned int pointsInside = 0;
// Count the number of points which lie inside the unit quarter-circle
unsigned int pointsInside = 0;
for (unsigned int i = tid; i < numSims; i += step) {
Real x;
Real y;
getPoint(x, y, localState1, localState2);
Real l2norm2 = x * x + y * y;
for (unsigned int i = tid; i < numSims; i += step) {
Real x;
Real y;
getPoint(x, y, localState1, localState2);
Real l2norm2 = x * x + y * y;
if (l2norm2 < static_cast<Real>(1)) {
pointsInside++;
if (l2norm2 < static_cast<Real>(1)) {
pointsInside++;
}
}
}
// Reduce within the block
pointsInside = reduce_sum(pointsInside, cta);
// Reduce within the block
pointsInside = reduce_sum(pointsInside, cta);
// Store the result
if (threadIdx.x == 0) {
results[bid] = pointsInside;
}
// Store the result
if (threadIdx.x == 0) {
results[bid] = pointsInside;
}
}
template <typename Real>
PiEstimator<Real>::PiEstimator(unsigned int numSims, unsigned int device,
unsigned int threadBlockSize)
: m_numSims(numSims),
m_device(device),
m_threadBlockSize(threadBlockSize) {}
PiEstimator<Real>::PiEstimator(unsigned int numSims, unsigned int device, unsigned int threadBlockSize)
: m_numSims(numSims)
, m_device(device)
, m_threadBlockSize(threadBlockSize)
{
}
template <typename Real>
Real PiEstimator<Real>::operator()() {
cudaError_t cudaResult = cudaSuccess;
struct cudaDeviceProp deviceProperties;
struct cudaFuncAttributes funcAttributes;
template <typename Real> Real PiEstimator<Real>::operator()()
{
cudaError_t cudaResult = cudaSuccess;
struct cudaDeviceProp deviceProperties;
struct cudaFuncAttributes funcAttributes;
// Determine type of generator to use (32- or 64-bit)
typedef typename IF<TYPE_IS<Real, double>::test, curandStateSobol64_t,
curandStateSobol32_t>::type curandStateSobol_sz;
typedef
typename IF<TYPE_IS<Real, double>::test, curandDirectionVectors64_t,
curandDirectionVectors32_t>::type curandDirectionVectors_sz;
// Determine type of generator to use (32- or 64-bit)
typedef
typename IF<TYPE_IS<Real, double>::test, curandStateSobol64_t, curandStateSobol32_t>::type curandStateSobol_sz;
typedef typename IF<TYPE_IS<Real, double>::test, curandDirectionVectors64_t, curandDirectionVectors32_t>::type
curandDirectionVectors_sz;
// Get device properties
cudaResult = cudaGetDeviceProperties(&deviceProperties, m_device);
if (cudaResult != cudaSuccess) {
string msg("Could not get device properties: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
// Check precision is valid
if (typeid(Real) == typeid(double) &&
(deviceProperties.major < 1 ||
(deviceProperties.major == 1 && deviceProperties.minor < 3))) {
throw std::runtime_error("Device does not have double precision support");
}
// Attach to GPU
cudaResult = cudaSetDevice(m_device);
if (cudaResult != cudaSuccess) {
string msg("Could not set CUDA device: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
// Determine how to divide the work between cores
dim3 block;
dim3 grid;
block.x = m_threadBlockSize;
grid.x = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;
// Aim to launch around ten or more times as many blocks as there
// are multiprocessors on the target device.
unsigned int blocksPerSM = 10;
unsigned int numSMs = deviceProperties.multiProcessorCount;
while (grid.x > 2 * blocksPerSM * numSMs) {
grid.x >>= 1;
}
// Get initRNG function properties and check the maximum block size
cudaResult = cudaFuncGetAttributes(
&funcAttributes, initRNG<curandStateSobol_sz, curandDirectionVectors_sz>);
if (cudaResult != cudaSuccess) {
string msg("Could not get function attributes: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {
throw std::runtime_error(
"Block X dimension is too large for initRNG kernel");
}
// Get computeValue function properties and check the maximum block size
cudaResult = cudaFuncGetAttributes(&funcAttributes,
computeValue<Real, curandStateSobol_sz>);
if (cudaResult != cudaSuccess) {
string msg("Could not get function attributes: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {
throw std::runtime_error(
"Block X dimension is too large for computeValue kernel");
}
// Check the dimensions are valid
if (block.x > (unsigned int)deviceProperties.maxThreadsDim[0]) {
throw std::runtime_error("Block X dimension is too large for device");
}
if (grid.x > (unsigned int)deviceProperties.maxGridSize[0]) {
throw std::runtime_error("Grid X dimension is too large for device");
}
// Allocate memory for RNG states and direction vectors
curandStateSobol_sz *d_rngStates = 0;
curandDirectionVectors_sz *d_rngDirections = 0;
cudaResult = cudaMalloc((void **)&d_rngStates,
2 * grid.x * block.x * sizeof(curandStateSobol_sz));
if (cudaResult != cudaSuccess) {
string msg("Could not allocate memory on device for RNG states: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
cudaResult = cudaMalloc((void **)&d_rngDirections,
2 * sizeof(curandDirectionVectors_sz));
if (cudaResult != cudaSuccess) {
string msg(
"Could not allocate memory on device for RNG direction vectors: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
// Allocate memory for result
// Each thread block will produce one result
unsigned int *d_results = 0;
cudaResult = cudaMalloc((void **)&d_results, grid.x * sizeof(unsigned int));
if (cudaResult != cudaSuccess) {
string msg("Could not allocate memory on device for partial results: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
// Generate direction vectors on the host and copy to the device
if (typeid(Real) == typeid(float)) {
curandDirectionVectors32_t *rngDirections;
curandStatus_t curandResult = curandGetDirectionVectors32(
&rngDirections, CURAND_DIRECTION_VECTORS_32_JOEKUO6);
if (curandResult != CURAND_STATUS_SUCCESS) {
string msg(
"Could not get direction vectors for quasi-random number "
"generator: ");
msg += curandResult;
throw std::runtime_error(msg);
}
cudaResult = cudaMemcpy(d_rngDirections, rngDirections,
2 * sizeof(curandDirectionVectors32_t),
cudaMemcpyHostToDevice);
// Get device properties
cudaResult = cudaGetDeviceProperties(&deviceProperties, m_device);
if (cudaResult != cudaSuccess) {
string msg("Could not copy direction vectors to device: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
} else if (typeid(Real) == typeid(double)) {
curandDirectionVectors64_t *rngDirections;
curandStatus_t curandResult = curandGetDirectionVectors64(
&rngDirections, CURAND_DIRECTION_VECTORS_64_JOEKUO6);
if (curandResult != CURAND_STATUS_SUCCESS) {
string msg(
"Could not get direction vectors for quasi-random number "
"generator: ");
msg += curandResult;
throw std::runtime_error(msg);
string msg("Could not get device properties: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
cudaResult = cudaMemcpy(d_rngDirections, rngDirections,
2 * sizeof(curandDirectionVectors64_t),
cudaMemcpyHostToDevice);
// Check precision is valid
if (typeid(Real) == typeid(double)
&& (deviceProperties.major < 1 || (deviceProperties.major == 1 && deviceProperties.minor < 3))) {
throw std::runtime_error("Device does not have double precision support");
}
// Attach to GPU
cudaResult = cudaSetDevice(m_device);
if (cudaResult != cudaSuccess) {
string msg("Could not copy direction vectors to device: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
string msg("Could not set CUDA device: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
} else {
string msg(
"Could not get direction vectors for random number generator of "
"specified type");
throw std::runtime_error(msg);
}
// Initialise RNG
initRNG<<<grid, block>>>(d_rngStates, d_rngDirections, m_numSims);
// Determine how to divide the work between cores
dim3 block;
dim3 grid;
block.x = m_threadBlockSize;
grid.x = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;
// Count the points inside unit quarter-circle
computeValue<Real><<<grid, block, block.x * sizeof(unsigned int)>>>(
d_results, d_rngStates, m_numSims);
// Aim to launch around ten or more times as many blocks as there
// are multiprocessors on the target device.
unsigned int blocksPerSM = 10;
unsigned int numSMs = deviceProperties.multiProcessorCount;
// Copy partial results back
vector<unsigned int> results(grid.x);
cudaResult = cudaMemcpy(&results[0], d_results, grid.x * sizeof(unsigned int),
cudaMemcpyDeviceToHost);
while (grid.x > 2 * blocksPerSM * numSMs) {
grid.x >>= 1;
}
if (cudaResult != cudaSuccess) {
string msg("Could not copy partial results to host: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
// Get initRNG function properties and check the maximum block size
cudaResult = cudaFuncGetAttributes(&funcAttributes, initRNG<curandStateSobol_sz, curandDirectionVectors_sz>);
// Complete sum-reduction on host
Real value =
static_cast<Real>(std::accumulate(results.begin(), results.end(), 0));
if (cudaResult != cudaSuccess) {
string msg("Could not get function attributes: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
// Determine the proportion of points inside the quarter-circle,
// i.e. the area of the unit quarter-circle
value /= m_numSims;
if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {
throw std::runtime_error("Block X dimension is too large for initRNG kernel");
}
// Value is currently an estimate of the area of a unit quarter-circle, so we
// can scale to a full circle by multiplying by four. Now since the area of a
// circle is pi * r^2, and r is one, the value will be an estimate for the
// value of pi.
value *= 4;
// Get computeValue function properties and check the maximum block size
cudaResult = cudaFuncGetAttributes(&funcAttributes, computeValue<Real, curandStateSobol_sz>);
// Cleanup
if (d_rngStates) {
cudaFree(d_rngStates);
d_rngStates = 0;
}
if (cudaResult != cudaSuccess) {
string msg("Could not get function attributes: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
if (d_rngDirections) {
cudaFree(d_rngDirections);
d_rngDirections = 0;
}
if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {
throw std::runtime_error("Block X dimension is too large for computeValue kernel");
}
if (d_results) {
cudaFree(d_results);
d_results = 0;
}
// Check the dimensions are valid
if (block.x > (unsigned int)deviceProperties.maxThreadsDim[0]) {
throw std::runtime_error("Block X dimension is too large for device");
}
return value;
if (grid.x > (unsigned int)deviceProperties.maxGridSize[0]) {
throw std::runtime_error("Grid X dimension is too large for device");
}
// Allocate memory for RNG states and direction vectors
curandStateSobol_sz *d_rngStates = 0;
curandDirectionVectors_sz *d_rngDirections = 0;
cudaResult = cudaMalloc((void **)&d_rngStates, 2 * grid.x * block.x * sizeof(curandStateSobol_sz));
if (cudaResult != cudaSuccess) {
string msg("Could not allocate memory on device for RNG states: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
cudaResult = cudaMalloc((void **)&d_rngDirections, 2 * sizeof(curandDirectionVectors_sz));
if (cudaResult != cudaSuccess) {
string msg("Could not allocate memory on device for RNG direction vectors: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
// Allocate memory for result
// Each thread block will produce one result
unsigned int *d_results = 0;
cudaResult = cudaMalloc((void **)&d_results, grid.x * sizeof(unsigned int));
if (cudaResult != cudaSuccess) {
string msg("Could not allocate memory on device for partial results: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
// Generate direction vectors on the host and copy to the device
if (typeid(Real) == typeid(float)) {
curandDirectionVectors32_t *rngDirections;
curandStatus_t curandResult = curandGetDirectionVectors32(&rngDirections, CURAND_DIRECTION_VECTORS_32_JOEKUO6);
if (curandResult != CURAND_STATUS_SUCCESS) {
string msg("Could not get direction vectors for quasi-random number "
"generator: ");
msg += curandResult;
throw std::runtime_error(msg);
}
cudaResult =
cudaMemcpy(d_rngDirections, rngDirections, 2 * sizeof(curandDirectionVectors32_t), cudaMemcpyHostToDevice);
if (cudaResult != cudaSuccess) {
string msg("Could not copy direction vectors to device: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
}
else if (typeid(Real) == typeid(double)) {
curandDirectionVectors64_t *rngDirections;
curandStatus_t curandResult = curandGetDirectionVectors64(&rngDirections, CURAND_DIRECTION_VECTORS_64_JOEKUO6);
if (curandResult != CURAND_STATUS_SUCCESS) {
string msg("Could not get direction vectors for quasi-random number "
"generator: ");
msg += curandResult;
throw std::runtime_error(msg);
}
cudaResult =
cudaMemcpy(d_rngDirections, rngDirections, 2 * sizeof(curandDirectionVectors64_t), cudaMemcpyHostToDevice);
if (cudaResult != cudaSuccess) {
string msg("Could not copy direction vectors to device: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
}
else {
string msg("Could not get direction vectors for random number generator of "
"specified type");
throw std::runtime_error(msg);
}
// Initialise RNG
initRNG<<<grid, block>>>(d_rngStates, d_rngDirections, m_numSims);
// Count the points inside unit quarter-circle
computeValue<Real><<<grid, block, block.x * sizeof(unsigned int)>>>(d_results, d_rngStates, m_numSims);
// Copy partial results back
vector<unsigned int> results(grid.x);
cudaResult = cudaMemcpy(&results[0], d_results, grid.x * sizeof(unsigned int), cudaMemcpyDeviceToHost);
if (cudaResult != cudaSuccess) {
string msg("Could not copy partial results to host: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
// Complete sum-reduction on host
Real value = static_cast<Real>(std::accumulate(results.begin(), results.end(), 0));
// Determine the proportion of points inside the quarter-circle,
// i.e. the area of the unit quarter-circle
value /= m_numSims;
// Value is currently an estimate of the area of a unit quarter-circle, so we
// can scale to a full circle by multiplying by four. Now since the area of a
// circle is pi * r^2, and r is one, the value will be an estimate for the
// value of pi.
value *= 4;
// Cleanup
if (d_rngStates) {
cudaFree(d_rngStates);
d_rngStates = 0;
}
if (d_rngDirections) {
cudaFree(d_rngDirections);
d_rngDirections = 0;
}
if (d_results) {
cudaFree(d_results);
d_results = 0;
}
return value;
}
// Explicit template instantiation

View File

@@ -28,84 +28,84 @@
#include "../inc/test.h"
#include <sstream>
#include <iomanip>
#include <stdexcept>
#include <memory>
#include <iostream>
#include <cassert>
#include <typeinfo>
#include <stdio.h>
#include <helper_timer.h>
#include <cuda_runtime.h>
#include <helper_timer.h>
#include <iomanip>
#include <iostream>
#include <math.h>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <stdio.h>
#include <typeinfo>
#include "../inc/piestimator.h"
template <typename Real>
bool Test<Real>::operator()() {
using std::endl;
using std::setw;
using std::stringstream;
template <typename Real> bool Test<Real>::operator()()
{
using std::endl;
using std::setw;
using std::stringstream;
StopWatchInterface *timer = NULL;
sdkCreateTimer(&timer);
StopWatchInterface *timer = NULL;
sdkCreateTimer(&timer);
// Get device properties
struct cudaDeviceProp deviceProperties;
cudaError_t cudaResult = cudaGetDeviceProperties(&deviceProperties, device);
// Get device properties
struct cudaDeviceProp deviceProperties;
cudaError_t cudaResult = cudaGetDeviceProperties(&deviceProperties, device);
if (cudaResult != cudaSuccess) {
std::string msg("Could not get device properties: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
if (cudaResult != cudaSuccess) {
std::string msg("Could not get device properties: ");
msg += cudaGetErrorString(cudaResult);
throw std::runtime_error(msg);
}
// Evaluate on GPU
printf("Estimating Pi on GPU (%s)\n\n", deviceProperties.name);
PiEstimator<Real> estimator(numSims, device, threadBlockSize);
sdkStartTimer(&timer);
Real result = estimator();
sdkStopTimer(&timer);
elapsedTime = sdkGetAverageTimerValue(&timer) / 1000.0f;
// Evaluate on GPU
printf("Estimating Pi on GPU (%s)\n\n", deviceProperties.name);
PiEstimator<Real> estimator(numSims, device, threadBlockSize);
sdkStartTimer(&timer);
Real result = estimator();
sdkStopTimer(&timer);
elapsedTime = sdkGetAverageTimerValue(&timer) / 1000.0f;
// Tolerance to compare result with expected
// This is just to check that nothing has gone very wrong with the
// test, the actual accuracy of the result depends on the number of
// Monte Carlo trials
const Real tolerance = static_cast<Real>(0.01);
// Tolerance to compare result with expected
// This is just to check that nothing has gone very wrong with the
// test, the actual accuracy of the result depends on the number of
// Monte Carlo trials
const Real tolerance = static_cast<Real>(0.01);
// Display results
Real abserror = fabs(result - static_cast<Real>(PI));
Real relerror = abserror / static_cast<Real>(PI);
printf("Precision: %s\n",
(typeid(Real) == typeid(double)) ? "double" : "single");
printf("Number of sims: %d\n", numSims);
printf("Tolerance: %e\n", tolerance);
printf("GPU result: %e\n", result);
printf("Expected: %e\n", PI);
printf("Absolute error: %e\n", abserror);
printf("Relative error: %e\n\n", relerror);
// Display results
Real abserror = fabs(result - static_cast<Real>(PI));
Real relerror = abserror / static_cast<Real>(PI);
printf("Precision: %s\n", (typeid(Real) == typeid(double)) ? "double" : "single");
printf("Number of sims: %d\n", numSims);
printf("Tolerance: %e\n", tolerance);
printf("GPU result: %e\n", result);
printf("Expected: %e\n", PI);
printf("Absolute error: %e\n", abserror);
printf("Relative error: %e\n\n", relerror);
// Check result
if (relerror > tolerance) {
printf("computed result (%e) does not match expected result (%e).\n",
result, PI);
pass = false;
} else {
pass = true;
}
// Check result
if (relerror > tolerance) {
printf("computed result (%e) does not match expected result (%e).\n", result, PI);
pass = false;
}
else {
pass = true;
}
// Print results
printf(
"MonteCarloEstimatePiInlineQ, Performance = %.2f sims/s, Time = "
"%.2f(ms), NumDevsUsed = %u, Blocksize = %u\n",
numSims / elapsedTime, elapsedTime * 1000.0f, 1, threadBlockSize);
// Print results
printf("MonteCarloEstimatePiInlineQ, Performance = %.2f sims/s, Time = "
"%.2f(ms), NumDevsUsed = %u, Blocksize = %u\n",
numSims / elapsedTime,
elapsedTime * 1000.0f,
1,
threadBlockSize);
sdkDeleteTimer(&timer);
sdkDeleteTimer(&timer);
return pass;
return pass;
}
// Explicit template instantiation