5.7 KiB
Applications Monte Carlo Pi Example
Description
This example demonstrates how the mathematical constant pi ($\pi$) can be approximated using Monte Carlo integration. Monte Carlo integration approximates integration of a function by generating random values over a domain that is the superset of the function's domain. Using the ratio between the number of samples in both domains and the range of the random values, the integral is approximated.
The area of a disk is given by $r^2\pi$, where $r$ is the radius of a disk. Uniform random values are typically generated in the range $(0,1]$. Using a disk of radius $1$ centered on the origin, a sample point is in the disk if it's distance to the origin is less than $1$. The ratio between the number of sample points within the disk and the total sample points is an approximation of the ratio between the area of the disk and the quadrant $(0,1]\times(0,1]$, which is $\frac{\pi}{4}$. Multiplying the sample point ratio by $4$ approximates the value of pi.
To generate a large number of random samples we use hipRAND, a platform-independent library for GPU-based random number generation. hipRAND offers a choice of different generators, belonging to one of two categories: pseudorandom and quasirandom. Pseudorandom-number generators output a stream of numbers that appears to be statistically random, but is deterministic based on a seed. Quasirandom-number generators output a stream of values that cover the output domain evenly, for each given domain. For Monte Carlo integration, is it assumed that a quasirandom-number generator will provide a better approximation with a low number of points, because the sample points are of a guaranteed statistical distribution.
To compute the number of sample points that lie within the disk, we use hipCUB, which is a platform-independent library providing GPU primitives. For each sample, we are looking to compute whether it lies in the disk, and to count the number of samples for which this is the case. Using and indicator function and TransformInputIterator
, an iterator is created which outputs a zero or one for each sample. Using DeviceReduce::Sum
, the sum over the iterator's values is computed.
Application flow
-
Parse and validate user input.
-
Allocate device memory to store the random values. Since the samples are two-dimensional, two random values are required per sample.
-
Initialize hipRAND's default pseudorandom-number generator and generate the required number of values.
-
Allocate and initialize the input and output for hipCUB's
DeviceReduce::Sum
:a) Create a
hipcub::CountingInputIterator
that starts from0
, which will represent the sample index.b) Create a
hipcub::TransformInputIterator
that uses the sample index to obtain the sample's coordinates from the array of random numbers, and computes whether it lies within the disk. This iterator will be the input for the device function.c) Allocate device memory for the variable that stores the output of the function.
-
Calculate the required amount of temporary storage, and allocate it.
-
Calculate the number of samples within the disk with
hipcub::DeviceReduce::Sum
. -
Copy the result back to the host and calculate pi.
-
Clean up the generator and print the result.
-
Initialize hipRAND's default quasirandom-number generator, set the dimensions to two, and generate the required number of values.
Note that the first half of the array will be the first dimension, the second half will be the second dimension.
-
Repeat steps 4. - 8. for the quasirandom values.
Command line interface
-s <sample_count>
or-sample_count <sample_count>
sets the number of samples used, the default is $2^{20}$.
Key APIs and Concepts
-
To start using hipRAND, a call to
hiprandCreateGenerator
with a generator type is made.- To pick any of hipRAND's pseudorandom-number generators, we use type
HIPRAND_RNG_PSEUDO_DEFAULT
. For pseudorandom-number generators, the seed can be set withhiprandSetPseudoRandomGeneratorSeed
. - We use type
HIPRAND_RNG_QUASI_DEFAULT
to create a quasirandom-number generator. For quasirandom-number generators, the number of dimensions can be set withhiprandSetQuasiRandomGeneratorDimensions
. For this example, we calculate an area, so our domain consists of two dimensions.
Destroying the hipRAND generator is done with
hiprandDestroyGenerator
. - To pick any of hipRAND's pseudorandom-number generators, we use type
-
hipCUB itself requires no initialization, but each of its functions must be called twice. The first call must have a null-valued temporary storage argument, the call sets the required storage size. The second call performs the actual operation with the user-allocated memory.
-
hipCUB offers a number of iterators for convenience:
hipcub::CountingInputIterator
will act as an incrementing sequence starting from a specified index.hipcub::TransformInputIterator
takes an iterator and applies a user-defined function on it.
-
hipCUB's
DeviceReduce::Sum
computes the sum over the input iterator and outputs a single value to the output iterator.
Demonstrated API Calls
HIP runtime
__device__
__forceinline__
__host__
hipError_t
hipEventCreate
hipEventDestroy
hipEventElapsedTime
hipEventRecord
hipEventSynchronize
hipGetErrorString
hipMalloc
hipMemcpy
hipMemcpyDeviceToHost
hipMemcpyHostToDevice
hipStreamDefault
hipRAND
HIPRAND_RNG_PSEUDO_DEFAULT
HIPRAND_RNG_QUASI_DEFAULT
HIPRAND_STATUS_SUCCESS
hiprandCreateGenerator
hiprandDestroyGenerator
hiprandGenerateUniform
hiprandGenerator_t
hiprandSetPseudoRandomGeneratorSeed
hiprandSetQuasiRandomGeneratorDimensions
hiprandStatus_t
hipCUB
hipcub::CountingInputIterator
hipcub::DeviceReduce::Sum
hipcub::TransformInputIterator