mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-10-25 22:28:17 +08:00
Apply consistent code formatting across the repo. Add clang-format and pre-commit hooks.
This commit is contained in:
@@ -31,20 +31,20 @@
|
||||
* See supplied whitepaper for more explanations.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// includes, project
|
||||
#include <helper_functions.h> // Helper functions (utilities, parsing, timing)
|
||||
#include <helper_cuda.h> // helper functions (cuda error checking and initialization)
|
||||
#include <helper_cuda.h> // helper functions (cuda error checking and initialization)
|
||||
#include <helper_functions.h> // Helper functions (utilities, parsing, timing)
|
||||
#include <multithreading.h>
|
||||
|
||||
#include "MonteCarlo_common.h"
|
||||
|
||||
int *pArgc = NULL;
|
||||
int *pArgc = NULL;
|
||||
char **pArgv = NULL;
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -54,42 +54,43 @@ char **pArgv = NULL;
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Common functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
float randFloat(float low, float high) {
|
||||
float t = (float)rand() / (float)RAND_MAX;
|
||||
return (1.0f - t) * low + t * high;
|
||||
float randFloat(float low, float high)
|
||||
{
|
||||
float t = (float)rand() / (float)RAND_MAX;
|
||||
return (1.0f - t) * low + t * high;
|
||||
}
|
||||
|
||||
/// Utility function to tweak problem size for small GPUs
|
||||
int adjustProblemSize(int GPU_N, int default_nOptions) {
|
||||
int nOptions = default_nOptions;
|
||||
int adjustProblemSize(int GPU_N, int default_nOptions)
|
||||
{
|
||||
int nOptions = default_nOptions;
|
||||
|
||||
// select problem size
|
||||
for (int i = 0; i < GPU_N; i++) {
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, i));
|
||||
int cudaCores = _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) *
|
||||
deviceProp.multiProcessorCount;
|
||||
// select problem size
|
||||
for (int i = 0; i < GPU_N; i++) {
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, i));
|
||||
int cudaCores = _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount;
|
||||
|
||||
if (cudaCores <= 32) {
|
||||
nOptions = (nOptions < cudaCores / 2 ? nOptions : cudaCores / 2);
|
||||
if (cudaCores <= 32) {
|
||||
nOptions = (nOptions < cudaCores / 2 ? nOptions : cudaCores / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nOptions;
|
||||
return nOptions;
|
||||
}
|
||||
|
||||
int adjustGridSize(int GPUIndex, int defaultGridSize) {
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, GPUIndex));
|
||||
int maxGridSize = deviceProp.multiProcessorCount * 40;
|
||||
return ((defaultGridSize > maxGridSize) ? maxGridSize : defaultGridSize);
|
||||
int adjustGridSize(int GPUIndex, int defaultGridSize)
|
||||
{
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, GPUIndex));
|
||||
int maxGridSize = deviceProp.multiProcessorCount * 40;
|
||||
return ((defaultGridSize > maxGridSize) ? maxGridSize : defaultGridSize);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CPU reference functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
extern "C" void MonteCarloCPU(TOptionValue &callValue, TOptionData optionData,
|
||||
float *h_Random, int pathN);
|
||||
extern "C" void MonteCarloCPU(TOptionValue &callValue, TOptionData optionData, float *h_Random, int pathN);
|
||||
|
||||
// Black-Scholes formula for call options
|
||||
extern "C" void BlackScholesCall(float &CallResult, TOptionData optionData);
|
||||
@@ -100,98 +101,99 @@ extern "C" void BlackScholesCall(float &CallResult, TOptionData optionData);
|
||||
// Timer
|
||||
StopWatchInterface **hTimer = NULL;
|
||||
|
||||
static CUT_THREADPROC solverThread(TOptionPlan *plan) {
|
||||
// Init GPU
|
||||
checkCudaErrors(cudaSetDevice(plan->device));
|
||||
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, plan->device));
|
||||
|
||||
// Start the timer
|
||||
sdkStartTimer(&hTimer[plan->device]);
|
||||
|
||||
// Allocate intermediate memory for MC integrator and initialize
|
||||
// RNG states
|
||||
initMonteCarloGPU(plan);
|
||||
|
||||
// Main computation
|
||||
MonteCarloGPU(plan);
|
||||
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
// Stop the timer
|
||||
sdkStopTimer(&hTimer[plan->device]);
|
||||
|
||||
// Shut down this GPU
|
||||
closeMonteCarloGPU(plan);
|
||||
|
||||
cudaStreamSynchronize(0);
|
||||
|
||||
printf("solverThread() finished - GPU Device %d: %s\n", plan->device,
|
||||
deviceProp.name);
|
||||
|
||||
CUT_THREADEND;
|
||||
}
|
||||
|
||||
static void multiSolver(TOptionPlan *plan, int nPlans) {
|
||||
// allocate and initialize an array of stream handles
|
||||
cudaStream_t *streams = (cudaStream_t *)malloc(nPlans * sizeof(cudaStream_t));
|
||||
cudaEvent_t *events = (cudaEvent_t *)malloc(nPlans * sizeof(cudaEvent_t));
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
checkCudaErrors(cudaStreamCreate(&(streams[i])));
|
||||
checkCudaErrors(cudaEventCreate(&(events[i])));
|
||||
}
|
||||
|
||||
// Init Each GPU
|
||||
// In CUDA 4.0 we can call cudaSetDevice multiple times to target each device
|
||||
// Set the device desired, then perform initializations on that device
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
// set the target device to perform initialization on
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
static CUT_THREADPROC solverThread(TOptionPlan *plan)
|
||||
{
|
||||
// Init GPU
|
||||
checkCudaErrors(cudaSetDevice(plan->device));
|
||||
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, plan[i].device));
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, plan->device));
|
||||
|
||||
// Allocate intermediate memory for MC integrator
|
||||
// and initialize RNG state
|
||||
initMonteCarloGPU(&plan[i]);
|
||||
}
|
||||
// Start the timer
|
||||
sdkStartTimer(&hTimer[plan->device]);
|
||||
|
||||
// Allocate intermediate memory for MC integrator and initialize
|
||||
// RNG states
|
||||
initMonteCarloGPU(plan);
|
||||
|
||||
// Main computation
|
||||
MonteCarloGPU(plan);
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
// Start the timer
|
||||
sdkResetTimer(&hTimer[0]);
|
||||
sdkStartTimer(&hTimer[0]);
|
||||
// Stop the timer
|
||||
sdkStopTimer(&hTimer[plan->device]);
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
// Shut down this GPU
|
||||
closeMonteCarloGPU(plan);
|
||||
|
||||
// Main computations
|
||||
MonteCarloGPU(&plan[i], streams[i]);
|
||||
cudaStreamSynchronize(0);
|
||||
|
||||
checkCudaErrors(cudaEventRecord(events[i], streams[i]));
|
||||
}
|
||||
printf("solverThread() finished - GPU Device %d: %s\n", plan->device, deviceProp.name);
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
cudaEventSynchronize(events[i]);
|
||||
}
|
||||
CUT_THREADEND;
|
||||
}
|
||||
|
||||
// Stop the timer
|
||||
sdkStopTimer(&hTimer[0]);
|
||||
static void multiSolver(TOptionPlan *plan, int nPlans)
|
||||
{
|
||||
// allocate and initialize an array of stream handles
|
||||
cudaStream_t *streams = (cudaStream_t *)malloc(nPlans * sizeof(cudaStream_t));
|
||||
cudaEvent_t *events = (cudaEvent_t *)malloc(nPlans * sizeof(cudaEvent_t));
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
closeMonteCarloGPU(&plan[i]);
|
||||
checkCudaErrors(cudaStreamDestroy(streams[i]));
|
||||
checkCudaErrors(cudaEventDestroy(events[i]));
|
||||
}
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
checkCudaErrors(cudaStreamCreate(&(streams[i])));
|
||||
checkCudaErrors(cudaEventCreate(&(events[i])));
|
||||
}
|
||||
|
||||
// Init Each GPU
|
||||
// In CUDA 4.0 we can call cudaSetDevice multiple times to target each device
|
||||
// Set the device desired, then perform initializations on that device
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
// set the target device to perform initialization on
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, plan[i].device));
|
||||
|
||||
// Allocate intermediate memory for MC integrator
|
||||
// and initialize RNG state
|
||||
initMonteCarloGPU(&plan[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
// Start the timer
|
||||
sdkResetTimer(&hTimer[0]);
|
||||
sdkStartTimer(&hTimer[0]);
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
|
||||
// Main computations
|
||||
MonteCarloGPU(&plan[i], streams[i]);
|
||||
|
||||
checkCudaErrors(cudaEventRecord(events[i], streams[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
cudaEventSynchronize(events[i]);
|
||||
}
|
||||
|
||||
// Stop the timer
|
||||
sdkStopTimer(&hTimer[0]);
|
||||
|
||||
for (int i = 0; i < nPlans; i++) {
|
||||
checkCudaErrors(cudaSetDevice(plan[i].device));
|
||||
closeMonteCarloGPU(&plan[i]);
|
||||
checkCudaErrors(cudaStreamDestroy(streams[i]));
|
||||
checkCudaErrors(cudaEventDestroy(events[i]));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -203,285 +205,279 @@ static void multiSolver(TOptionPlan *plan, int nPlans) {
|
||||
#define PRINT_RESULTS
|
||||
#undef PRINT_RESULTS
|
||||
|
||||
void usage() {
|
||||
printf("--method=[threaded,streamed] --scaling=[strong,weak] [--help]\n");
|
||||
printf("Method=threaded: 1 CPU thread for each GPU [default]\n");
|
||||
printf(
|
||||
" streamed: 1 CPU thread handles all GPUs (requires CUDA 4.0 or "
|
||||
"newer)\n");
|
||||
printf("Scaling=strong : constant problem size\n");
|
||||
printf(
|
||||
" weak : problem size scales with number of available GPUs "
|
||||
"[default]\n");
|
||||
void usage()
|
||||
{
|
||||
printf("--method=[threaded,streamed] --scaling=[strong,weak] [--help]\n");
|
||||
printf("Method=threaded: 1 CPU thread for each GPU [default]\n");
|
||||
printf(" streamed: 1 CPU thread handles all GPUs (requires CUDA 4.0 or "
|
||||
"newer)\n");
|
||||
printf("Scaling=strong : constant problem size\n");
|
||||
printf(" weak : problem size scales with number of available GPUs "
|
||||
"[default]\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *multiMethodChoice = NULL;
|
||||
char *scalingChoice = NULL;
|
||||
bool use_threads = true;
|
||||
bool bqatest = false;
|
||||
bool strongScaling = false;
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *multiMethodChoice = NULL;
|
||||
char *scalingChoice = NULL;
|
||||
bool use_threads = true;
|
||||
bool bqatest = false;
|
||||
bool strongScaling = false;
|
||||
|
||||
pArgc = &argc;
|
||||
pArgv = argv;
|
||||
pArgc = &argc;
|
||||
pArgv = argv;
|
||||
|
||||
printf("%s Starting...\n\n", argv[0]);
|
||||
printf("%s Starting...\n\n", argv[0]);
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "qatest")) {
|
||||
bqatest = true;
|
||||
}
|
||||
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "method",
|
||||
&multiMethodChoice);
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "scaling",
|
||||
&scalingChoice);
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "h") ||
|
||||
checkCmdLineFlag(argc, (const char **)argv, "help")) {
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (multiMethodChoice == NULL) {
|
||||
use_threads = false;
|
||||
} else {
|
||||
if (!strcasecmp(multiMethodChoice, "threaded")) {
|
||||
use_threads = true;
|
||||
} else {
|
||||
use_threads = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_threads == false) {
|
||||
printf("Using single CPU thread for multiple GPUs\n");
|
||||
}
|
||||
|
||||
if (scalingChoice == NULL) {
|
||||
strongScaling = false;
|
||||
} else {
|
||||
if (!strcasecmp(scalingChoice, "strong")) {
|
||||
strongScaling = true;
|
||||
} else {
|
||||
strongScaling = false;
|
||||
}
|
||||
}
|
||||
|
||||
// GPU number present in the system
|
||||
int GPU_N;
|
||||
checkCudaErrors(cudaGetDeviceCount(&GPU_N));
|
||||
int nOptions = 8 * 1024;
|
||||
|
||||
nOptions = adjustProblemSize(GPU_N, nOptions);
|
||||
|
||||
// select problem size
|
||||
int scale = (strongScaling) ? 1 : GPU_N;
|
||||
int OPT_N = nOptions * scale;
|
||||
int PATH_N = 262144;
|
||||
|
||||
// initialize the timers
|
||||
hTimer = new StopWatchInterface *[GPU_N];
|
||||
|
||||
for (int i = 0; i < GPU_N; i++) {
|
||||
sdkCreateTimer(&hTimer[i]);
|
||||
sdkResetTimer(&hTimer[i]);
|
||||
}
|
||||
|
||||
// Input data array
|
||||
TOptionData *optionData = new TOptionData[OPT_N];
|
||||
// Final GPU MC results
|
||||
TOptionValue *callValueGPU = new TOptionValue[OPT_N];
|
||||
//"Theoretical" call values by Black-Scholes formula
|
||||
float *callValueBS = new float[OPT_N];
|
||||
// Solver config
|
||||
TOptionPlan *optionSolver = new TOptionPlan[GPU_N];
|
||||
// OS thread ID
|
||||
CUTThread *threadID = new CUTThread[GPU_N];
|
||||
|
||||
int gpuBase, gpuIndex;
|
||||
int i;
|
||||
|
||||
float time;
|
||||
|
||||
double delta, ref, sumDelta, sumRef, sumReserve;
|
||||
|
||||
printf("MonteCarloMultiGPU\n");
|
||||
printf("==================\n");
|
||||
printf("Parallelization method = %s\n",
|
||||
use_threads ? "threaded" : "streamed");
|
||||
printf("Problem scaling = %s\n", strongScaling ? "strong" : "weak");
|
||||
printf("Number of GPUs = %d\n", GPU_N);
|
||||
printf("Total number of options = %d\n", OPT_N);
|
||||
printf("Number of paths = %d\n", PATH_N);
|
||||
|
||||
printf("main(): generating input data...\n");
|
||||
srand(123);
|
||||
|
||||
for (i = 0; i < OPT_N; i++) {
|
||||
optionData[i].S = randFloat(5.0f, 50.0f);
|
||||
optionData[i].X = randFloat(10.0f, 25.0f);
|
||||
optionData[i].T = randFloat(1.0f, 5.0f);
|
||||
optionData[i].R = 0.06f;
|
||||
optionData[i].V = 0.10f;
|
||||
callValueGPU[i].Expected = -1.0f;
|
||||
callValueGPU[i].Confidence = -1.0f;
|
||||
}
|
||||
|
||||
printf("main(): starting %i host threads...\n", GPU_N);
|
||||
|
||||
// Get option count for each GPU
|
||||
for (i = 0; i < GPU_N; i++) {
|
||||
optionSolver[i].optionCount = OPT_N / GPU_N;
|
||||
}
|
||||
|
||||
// Take into account cases with "odd" option counts
|
||||
for (i = 0; i < (OPT_N % GPU_N); i++) {
|
||||
optionSolver[i].optionCount++;
|
||||
}
|
||||
|
||||
// Assign GPU option ranges
|
||||
gpuBase = 0;
|
||||
|
||||
for (i = 0; i < GPU_N; i++) {
|
||||
optionSolver[i].device = i;
|
||||
optionSolver[i].optionData = optionData + gpuBase;
|
||||
optionSolver[i].callValue = callValueGPU + gpuBase;
|
||||
optionSolver[i].pathN = PATH_N;
|
||||
optionSolver[i].gridSize =
|
||||
adjustGridSize(optionSolver[i].device, optionSolver[i].optionCount);
|
||||
gpuBase += optionSolver[i].optionCount;
|
||||
}
|
||||
|
||||
if (use_threads || bqatest) {
|
||||
// Start CPU thread for each GPU
|
||||
for (gpuIndex = 0; gpuIndex < GPU_N; gpuIndex++) {
|
||||
threadID[gpuIndex] = cutStartThread((CUT_THREADROUTINE)solverThread,
|
||||
&optionSolver[gpuIndex]);
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "qatest")) {
|
||||
bqatest = true;
|
||||
}
|
||||
|
||||
printf("main(): waiting for GPU results...\n");
|
||||
cutWaitForThreads(threadID, GPU_N);
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "method", &multiMethodChoice);
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "scaling", &scalingChoice);
|
||||
|
||||
printf("main(): GPU statistics, threaded\n");
|
||||
|
||||
for (i = 0; i < GPU_N; i++) {
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(
|
||||
cudaGetDeviceProperties(&deviceProp, optionSolver[i].device));
|
||||
printf("GPU Device #%i: %s\n", optionSolver[i].device, deviceProp.name);
|
||||
printf("Options : %i\n", optionSolver[i].optionCount);
|
||||
printf("Simulation paths: %i\n", optionSolver[i].pathN);
|
||||
time = sdkGetTimerValue(&hTimer[i]);
|
||||
printf("Total time (ms.): %f\n", time);
|
||||
printf("Options per sec.: %f\n", OPT_N / (time * 0.001));
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "h") || checkCmdLineFlag(argc, (const char **)argv, "help")) {
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
printf("main(): comparing Monte Carlo and Black-Scholes results...\n");
|
||||
sumDelta = 0;
|
||||
sumRef = 0;
|
||||
sumReserve = 0;
|
||||
if (multiMethodChoice == NULL) {
|
||||
use_threads = false;
|
||||
}
|
||||
else {
|
||||
if (!strcasecmp(multiMethodChoice, "threaded")) {
|
||||
use_threads = true;
|
||||
}
|
||||
else {
|
||||
use_threads = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_threads == false) {
|
||||
printf("Using single CPU thread for multiple GPUs\n");
|
||||
}
|
||||
|
||||
if (scalingChoice == NULL) {
|
||||
strongScaling = false;
|
||||
}
|
||||
else {
|
||||
if (!strcasecmp(scalingChoice, "strong")) {
|
||||
strongScaling = true;
|
||||
}
|
||||
else {
|
||||
strongScaling = false;
|
||||
}
|
||||
}
|
||||
|
||||
// GPU number present in the system
|
||||
int GPU_N;
|
||||
checkCudaErrors(cudaGetDeviceCount(&GPU_N));
|
||||
int nOptions = 8 * 1024;
|
||||
|
||||
nOptions = adjustProblemSize(GPU_N, nOptions);
|
||||
|
||||
// select problem size
|
||||
int scale = (strongScaling) ? 1 : GPU_N;
|
||||
int OPT_N = nOptions * scale;
|
||||
int PATH_N = 262144;
|
||||
|
||||
// initialize the timers
|
||||
hTimer = new StopWatchInterface *[GPU_N];
|
||||
|
||||
for (int i = 0; i < GPU_N; i++) {
|
||||
sdkCreateTimer(&hTimer[i]);
|
||||
sdkResetTimer(&hTimer[i]);
|
||||
}
|
||||
|
||||
// Input data array
|
||||
TOptionData *optionData = new TOptionData[OPT_N];
|
||||
// Final GPU MC results
|
||||
TOptionValue *callValueGPU = new TOptionValue[OPT_N];
|
||||
//"Theoretical" call values by Black-Scholes formula
|
||||
float *callValueBS = new float[OPT_N];
|
||||
// Solver config
|
||||
TOptionPlan *optionSolver = new TOptionPlan[GPU_N];
|
||||
// OS thread ID
|
||||
CUTThread *threadID = new CUTThread[GPU_N];
|
||||
|
||||
int gpuBase, gpuIndex;
|
||||
int i;
|
||||
|
||||
float time;
|
||||
|
||||
double delta, ref, sumDelta, sumRef, sumReserve;
|
||||
|
||||
printf("MonteCarloMultiGPU\n");
|
||||
printf("==================\n");
|
||||
printf("Parallelization method = %s\n", use_threads ? "threaded" : "streamed");
|
||||
printf("Problem scaling = %s\n", strongScaling ? "strong" : "weak");
|
||||
printf("Number of GPUs = %d\n", GPU_N);
|
||||
printf("Total number of options = %d\n", OPT_N);
|
||||
printf("Number of paths = %d\n", PATH_N);
|
||||
|
||||
printf("main(): generating input data...\n");
|
||||
srand(123);
|
||||
|
||||
for (i = 0; i < OPT_N; i++) {
|
||||
BlackScholesCall(callValueBS[i], optionData[i]);
|
||||
delta = fabs(callValueBS[i] - callValueGPU[i].Expected);
|
||||
ref = callValueBS[i];
|
||||
sumDelta += delta;
|
||||
sumRef += fabs(ref);
|
||||
|
||||
if (delta > 1e-6) {
|
||||
sumReserve += callValueGPU[i].Confidence / delta;
|
||||
}
|
||||
|
||||
#ifdef PRINT_RESULTS
|
||||
printf("BS: %f; delta: %E\n", callValueBS[i], delta);
|
||||
#endif
|
||||
optionData[i].S = randFloat(5.0f, 50.0f);
|
||||
optionData[i].X = randFloat(10.0f, 25.0f);
|
||||
optionData[i].T = randFloat(1.0f, 5.0f);
|
||||
optionData[i].R = 0.06f;
|
||||
optionData[i].V = 0.10f;
|
||||
callValueGPU[i].Expected = -1.0f;
|
||||
callValueGPU[i].Confidence = -1.0f;
|
||||
}
|
||||
|
||||
sumReserve /= OPT_N;
|
||||
}
|
||||
printf("main(): starting %i host threads...\n", GPU_N);
|
||||
|
||||
if (!use_threads || bqatest) {
|
||||
multiSolver(optionSolver, GPU_N);
|
||||
// Get option count for each GPU
|
||||
for (i = 0; i < GPU_N; i++) {
|
||||
optionSolver[i].optionCount = OPT_N / GPU_N;
|
||||
}
|
||||
|
||||
printf("main(): GPU statistics, streamed\n");
|
||||
// Take into account cases with "odd" option counts
|
||||
for (i = 0; i < (OPT_N % GPU_N); i++) {
|
||||
optionSolver[i].optionCount++;
|
||||
}
|
||||
|
||||
// Assign GPU option ranges
|
||||
gpuBase = 0;
|
||||
|
||||
for (i = 0; i < GPU_N; i++) {
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(
|
||||
cudaGetDeviceProperties(&deviceProp, optionSolver[i].device));
|
||||
printf("GPU Device #%i: %s\n", optionSolver[i].device, deviceProp.name);
|
||||
printf("Options : %i\n", optionSolver[i].optionCount);
|
||||
printf("Simulation paths: %i\n", optionSolver[i].pathN);
|
||||
optionSolver[i].device = i;
|
||||
optionSolver[i].optionData = optionData + gpuBase;
|
||||
optionSolver[i].callValue = callValueGPU + gpuBase;
|
||||
optionSolver[i].pathN = PATH_N;
|
||||
optionSolver[i].gridSize = adjustGridSize(optionSolver[i].device, optionSolver[i].optionCount);
|
||||
gpuBase += optionSolver[i].optionCount;
|
||||
}
|
||||
|
||||
time = sdkGetTimerValue(&hTimer[0]);
|
||||
printf("\nTotal time (ms.): %f\n", time);
|
||||
printf("\tNote: This is elapsed time for all to compute.\n");
|
||||
printf("Options per sec.: %f\n", OPT_N / (time * 0.001));
|
||||
if (use_threads || bqatest) {
|
||||
// Start CPU thread for each GPU
|
||||
for (gpuIndex = 0; gpuIndex < GPU_N; gpuIndex++) {
|
||||
threadID[gpuIndex] = cutStartThread((CUT_THREADROUTINE)solverThread, &optionSolver[gpuIndex]);
|
||||
}
|
||||
|
||||
printf("main(): comparing Monte Carlo and Black-Scholes results...\n");
|
||||
sumDelta = 0;
|
||||
sumRef = 0;
|
||||
sumReserve = 0;
|
||||
printf("main(): waiting for GPU results...\n");
|
||||
cutWaitForThreads(threadID, GPU_N);
|
||||
|
||||
for (i = 0; i < OPT_N; i++) {
|
||||
BlackScholesCall(callValueBS[i], optionData[i]);
|
||||
delta = fabs(callValueBS[i] - callValueGPU[i].Expected);
|
||||
ref = callValueBS[i];
|
||||
sumDelta += delta;
|
||||
sumRef += fabs(ref);
|
||||
printf("main(): GPU statistics, threaded\n");
|
||||
|
||||
if (delta > 1e-6) {
|
||||
sumReserve += callValueGPU[i].Confidence / delta;
|
||||
}
|
||||
for (i = 0; i < GPU_N; i++) {
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, optionSolver[i].device));
|
||||
printf("GPU Device #%i: %s\n", optionSolver[i].device, deviceProp.name);
|
||||
printf("Options : %i\n", optionSolver[i].optionCount);
|
||||
printf("Simulation paths: %i\n", optionSolver[i].pathN);
|
||||
time = sdkGetTimerValue(&hTimer[i]);
|
||||
printf("Total time (ms.): %f\n", time);
|
||||
printf("Options per sec.: %f\n", OPT_N / (time * 0.001));
|
||||
}
|
||||
|
||||
printf("main(): comparing Monte Carlo and Black-Scholes results...\n");
|
||||
sumDelta = 0;
|
||||
sumRef = 0;
|
||||
sumReserve = 0;
|
||||
|
||||
for (i = 0; i < OPT_N; i++) {
|
||||
BlackScholesCall(callValueBS[i], optionData[i]);
|
||||
delta = fabs(callValueBS[i] - callValueGPU[i].Expected);
|
||||
ref = callValueBS[i];
|
||||
sumDelta += delta;
|
||||
sumRef += fabs(ref);
|
||||
|
||||
if (delta > 1e-6) {
|
||||
sumReserve += callValueGPU[i].Confidence / delta;
|
||||
}
|
||||
|
||||
#ifdef PRINT_RESULTS
|
||||
printf("BS: %f; delta: %E\n", callValueBS[i], delta);
|
||||
printf("BS: %f; delta: %E\n", callValueBS[i], delta);
|
||||
#endif
|
||||
}
|
||||
|
||||
sumReserve /= OPT_N;
|
||||
}
|
||||
|
||||
sumReserve /= OPT_N;
|
||||
}
|
||||
if (!use_threads || bqatest) {
|
||||
multiSolver(optionSolver, GPU_N);
|
||||
|
||||
printf("main(): GPU statistics, streamed\n");
|
||||
|
||||
for (i = 0; i < GPU_N; i++) {
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, optionSolver[i].device));
|
||||
printf("GPU Device #%i: %s\n", optionSolver[i].device, deviceProp.name);
|
||||
printf("Options : %i\n", optionSolver[i].optionCount);
|
||||
printf("Simulation paths: %i\n", optionSolver[i].pathN);
|
||||
}
|
||||
|
||||
time = sdkGetTimerValue(&hTimer[0]);
|
||||
printf("\nTotal time (ms.): %f\n", time);
|
||||
printf("\tNote: This is elapsed time for all to compute.\n");
|
||||
printf("Options per sec.: %f\n", OPT_N / (time * 0.001));
|
||||
|
||||
printf("main(): comparing Monte Carlo and Black-Scholes results...\n");
|
||||
sumDelta = 0;
|
||||
sumRef = 0;
|
||||
sumReserve = 0;
|
||||
|
||||
for (i = 0; i < OPT_N; i++) {
|
||||
BlackScholesCall(callValueBS[i], optionData[i]);
|
||||
delta = fabs(callValueBS[i] - callValueGPU[i].Expected);
|
||||
ref = callValueBS[i];
|
||||
sumDelta += delta;
|
||||
sumRef += fabs(ref);
|
||||
|
||||
if (delta > 1e-6) {
|
||||
sumReserve += callValueGPU[i].Confidence / delta;
|
||||
}
|
||||
|
||||
#ifdef PRINT_RESULTS
|
||||
printf("BS: %f; delta: %E\n", callValueBS[i], delta);
|
||||
#endif
|
||||
}
|
||||
|
||||
sumReserve /= OPT_N;
|
||||
}
|
||||
|
||||
#ifdef DO_CPU
|
||||
printf("main(): running CPU MonteCarlo...\n");
|
||||
TOptionValue callValueCPU;
|
||||
sumDelta = 0;
|
||||
sumRef = 0;
|
||||
printf("main(): running CPU MonteCarlo...\n");
|
||||
TOptionValue callValueCPU;
|
||||
sumDelta = 0;
|
||||
sumRef = 0;
|
||||
|
||||
for (i = 0; i < OPT_N; i++) {
|
||||
MonteCarloCPU(callValueCPU, optionData[i], NULL, PATH_N);
|
||||
delta = fabs(callValueCPU.Expected - callValueGPU[i].Expected);
|
||||
ref = callValueCPU.Expected;
|
||||
sumDelta += delta;
|
||||
sumRef += fabs(ref);
|
||||
printf("Exp : %f | %f\t", callValueCPU.Expected, callValueGPU[i].Expected);
|
||||
printf("Conf: %f | %f\n", callValueCPU.Confidence,
|
||||
callValueGPU[i].Confidence);
|
||||
}
|
||||
for (i = 0; i < OPT_N; i++) {
|
||||
MonteCarloCPU(callValueCPU, optionData[i], NULL, PATH_N);
|
||||
delta = fabs(callValueCPU.Expected - callValueGPU[i].Expected);
|
||||
ref = callValueCPU.Expected;
|
||||
sumDelta += delta;
|
||||
sumRef += fabs(ref);
|
||||
printf("Exp : %f | %f\t", callValueCPU.Expected, callValueGPU[i].Expected);
|
||||
printf("Conf: %f | %f\n", callValueCPU.Confidence, callValueGPU[i].Confidence);
|
||||
}
|
||||
|
||||
printf("L1 norm: %E\n", sumDelta / sumRef);
|
||||
printf("L1 norm: %E\n", sumDelta / sumRef);
|
||||
#endif
|
||||
|
||||
printf("Shutting down...\n");
|
||||
printf("Shutting down...\n");
|
||||
|
||||
for (int i = 0; i < GPU_N; i++) {
|
||||
sdkStartTimer(&hTimer[i]);
|
||||
checkCudaErrors(cudaSetDevice(i));
|
||||
}
|
||||
for (int i = 0; i < GPU_N; i++) {
|
||||
sdkStartTimer(&hTimer[i]);
|
||||
checkCudaErrors(cudaSetDevice(i));
|
||||
}
|
||||
|
||||
delete[] optionSolver;
|
||||
delete[] callValueBS;
|
||||
delete[] callValueGPU;
|
||||
delete[] optionData;
|
||||
delete[] threadID;
|
||||
delete[] hTimer;
|
||||
delete[] optionSolver;
|
||||
delete[] callValueBS;
|
||||
delete[] callValueGPU;
|
||||
delete[] optionData;
|
||||
delete[] threadID;
|
||||
delete[] hTimer;
|
||||
|
||||
printf("Test Summary...\n");
|
||||
printf("L1 norm : %E\n", sumDelta / sumRef);
|
||||
printf("Average reserve: %f\n", sumReserve);
|
||||
printf(
|
||||
"\nNOTE: The CUDA Samples are not meant for performance measurements. "
|
||||
"Results may vary when GPU Boost is enabled.\n\n");
|
||||
printf(sumReserve > 1.0f ? "Test passed\n" : "Test failed!\n");
|
||||
exit(sumReserve > 1.0f ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
printf("Test Summary...\n");
|
||||
printf("L1 norm : %E\n", sumDelta / sumRef);
|
||||
printf("Average reserve: %f\n", sumReserve);
|
||||
printf("\nNOTE: The CUDA Samples are not meant for performance measurements. "
|
||||
"Results may vary when GPU Boost is enabled.\n\n");
|
||||
printf(sumReserve > 1.0f ? "Test passed\n" : "Test failed!\n");
|
||||
exit(sumReserve > 1.0f ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -27,68 +27,71 @@
|
||||
|
||||
#ifndef MONTECARLO_COMMON_H
|
||||
#define MONTECARLO_COMMON_H
|
||||
#include "realtype.h"
|
||||
#include "curand_kernel.h"
|
||||
#include "realtype.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Global types
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct {
|
||||
float S;
|
||||
float X;
|
||||
float T;
|
||||
float R;
|
||||
float V;
|
||||
typedef struct
|
||||
{
|
||||
float S;
|
||||
float X;
|
||||
float T;
|
||||
float R;
|
||||
float V;
|
||||
} TOptionData;
|
||||
|
||||
typedef struct
|
||||
//#ifdef __CUDACC__
|
||||
//__align__(8)
|
||||
//#endif
|
||||
{
|
||||
float Expected;
|
||||
float Confidence;
|
||||
// #ifdef __CUDACC__
|
||||
//__align__(8)
|
||||
// #endif
|
||||
{
|
||||
float Expected;
|
||||
float Confidence;
|
||||
} TOptionValue;
|
||||
|
||||
// GPU outputs before CPU postprocessing
|
||||
typedef struct {
|
||||
real Expected;
|
||||
real Confidence;
|
||||
typedef struct
|
||||
{
|
||||
real Expected;
|
||||
real Confidence;
|
||||
} __TOptionValue;
|
||||
|
||||
typedef struct {
|
||||
// Device ID for multi-GPU version
|
||||
int device;
|
||||
// Option count for this plan
|
||||
int optionCount;
|
||||
typedef struct
|
||||
{
|
||||
// Device ID for multi-GPU version
|
||||
int device;
|
||||
// Option count for this plan
|
||||
int optionCount;
|
||||
|
||||
// Host-side data source and result destination
|
||||
TOptionData *optionData;
|
||||
TOptionValue *callValue;
|
||||
// Host-side data source and result destination
|
||||
TOptionData *optionData;
|
||||
TOptionValue *callValue;
|
||||
|
||||
// Temporary Host-side pinned memory for async + faster data transfers
|
||||
__TOptionValue *h_CallValue;
|
||||
// Temporary Host-side pinned memory for async + faster data transfers
|
||||
__TOptionValue *h_CallValue;
|
||||
|
||||
// Device- and host-side option data
|
||||
void *d_OptionData;
|
||||
void *h_OptionData;
|
||||
// Device- and host-side option data
|
||||
void *d_OptionData;
|
||||
void *h_OptionData;
|
||||
|
||||
// Device-side option values
|
||||
void *d_CallValue;
|
||||
// Device-side option values
|
||||
void *d_CallValue;
|
||||
|
||||
// Intermediate device-side buffers
|
||||
void *d_Buffer;
|
||||
// Intermediate device-side buffers
|
||||
void *d_Buffer;
|
||||
|
||||
// random number generator states
|
||||
curandState *rngStates;
|
||||
// random number generator states
|
||||
curandState *rngStates;
|
||||
|
||||
// Pseudorandom samples count
|
||||
int pathN;
|
||||
// Pseudorandom samples count
|
||||
int pathN;
|
||||
|
||||
// Time stamp
|
||||
float time;
|
||||
// Time stamp
|
||||
float time;
|
||||
|
||||
int gridSize;
|
||||
int gridSize;
|
||||
} TOptionPlan;
|
||||
|
||||
extern "C" void initMonteCarloGPU(TOptionPlan *plan);
|
||||
|
||||
@@ -25,13 +25,12 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <curand.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <curand.h>
|
||||
|
||||
//#include "curand_kernel.h"
|
||||
// #include "curand_kernel.h"
|
||||
#include "helper_cuda.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -42,99 +41,101 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Black-Scholes formula for Monte Carlo results validation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define A1 0.31938153
|
||||
#define A2 -0.356563782
|
||||
#define A3 1.781477937
|
||||
#define A4 -1.821255978
|
||||
#define A5 1.330274429
|
||||
#define A1 0.31938153
|
||||
#define A2 -0.356563782
|
||||
#define A3 1.781477937
|
||||
#define A4 -1.821255978
|
||||
#define A5 1.330274429
|
||||
#define RSQRT2PI 0.39894228040143267793994605993438
|
||||
|
||||
// Polynomial approximation of
|
||||
// cumulative normal distribution function
|
||||
double CND(double d) {
|
||||
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
|
||||
double CND(double d)
|
||||
{
|
||||
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
|
||||
|
||||
double cnd = RSQRT2PI * exp(-0.5 * d * d) *
|
||||
(K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
|
||||
double cnd = RSQRT2PI * exp(-0.5 * d * d) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
|
||||
|
||||
if (d > 0) cnd = 1.0 - cnd;
|
||||
if (d > 0)
|
||||
cnd = 1.0 - cnd;
|
||||
|
||||
return cnd;
|
||||
return cnd;
|
||||
}
|
||||
|
||||
// Black-Scholes formula for call value
|
||||
extern "C" void BlackScholesCall(float &callValue, TOptionData optionData) {
|
||||
double S = optionData.S;
|
||||
double X = optionData.X;
|
||||
double T = optionData.T;
|
||||
double R = optionData.R;
|
||||
double V = optionData.V;
|
||||
extern "C" void BlackScholesCall(float &callValue, TOptionData optionData)
|
||||
{
|
||||
double S = optionData.S;
|
||||
double X = optionData.X;
|
||||
double T = optionData.T;
|
||||
double R = optionData.R;
|
||||
double V = optionData.V;
|
||||
|
||||
double sqrtT = sqrt(T);
|
||||
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
|
||||
double d2 = d1 - V * sqrtT;
|
||||
double CNDD1 = CND(d1);
|
||||
double CNDD2 = CND(d2);
|
||||
double expRT = exp(-R * T);
|
||||
double sqrtT = sqrt(T);
|
||||
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
|
||||
double d2 = d1 - V * sqrtT;
|
||||
double CNDD1 = CND(d1);
|
||||
double CNDD2 = CND(d2);
|
||||
double expRT = exp(-R * T);
|
||||
|
||||
callValue = (float)(S * CNDD1 - X * expRT * CNDD2);
|
||||
callValue = (float)(S * CNDD1 - X * expRT * CNDD2);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// CPU Monte Carlo
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static double endCallValue(double S, double X, double r, double MuByT,
|
||||
double VBySqrtT) {
|
||||
double callValue = S * exp(MuByT + VBySqrtT * r) - X;
|
||||
return (callValue > 0) ? callValue : 0;
|
||||
static double endCallValue(double S, double X, double r, double MuByT, double VBySqrtT)
|
||||
{
|
||||
double callValue = S * exp(MuByT + VBySqrtT * r) - X;
|
||||
return (callValue > 0) ? callValue : 0;
|
||||
}
|
||||
|
||||
extern "C" void MonteCarloCPU(TOptionValue &callValue, TOptionData optionData,
|
||||
float *h_Samples, int pathN) {
|
||||
const double S = optionData.S;
|
||||
const double X = optionData.X;
|
||||
const double T = optionData.T;
|
||||
const double R = optionData.R;
|
||||
const double V = optionData.V;
|
||||
const double MuByT = (R - 0.5 * V * V) * T;
|
||||
const double VBySqrtT = V * sqrt(T);
|
||||
extern "C" void MonteCarloCPU(TOptionValue &callValue, TOptionData optionData, float *h_Samples, int pathN)
|
||||
{
|
||||
const double S = optionData.S;
|
||||
const double X = optionData.X;
|
||||
const double T = optionData.T;
|
||||
const double R = optionData.R;
|
||||
const double V = optionData.V;
|
||||
const double MuByT = (R - 0.5 * V * V) * T;
|
||||
const double VBySqrtT = V * sqrt(T);
|
||||
|
||||
float *samples;
|
||||
curandGenerator_t gen;
|
||||
float *samples;
|
||||
curandGenerator_t gen;
|
||||
|
||||
checkCudaErrors(curandCreateGeneratorHost(&gen, CURAND_RNG_PSEUDO_DEFAULT));
|
||||
unsigned long long seed = 1234ULL;
|
||||
checkCudaErrors(curandSetPseudoRandomGeneratorSeed(gen, seed));
|
||||
checkCudaErrors(curandCreateGeneratorHost(&gen, CURAND_RNG_PSEUDO_DEFAULT));
|
||||
unsigned long long seed = 1234ULL;
|
||||
checkCudaErrors(curandSetPseudoRandomGeneratorSeed(gen, seed));
|
||||
|
||||
if (h_Samples != NULL) {
|
||||
samples = h_Samples;
|
||||
} else {
|
||||
samples = (float *)malloc(pathN * sizeof(float));
|
||||
checkCudaErrors(curandGenerateNormal(gen, samples, pathN, 0.0, 1.0));
|
||||
}
|
||||
if (h_Samples != NULL) {
|
||||
samples = h_Samples;
|
||||
}
|
||||
else {
|
||||
samples = (float *)malloc(pathN * sizeof(float));
|
||||
checkCudaErrors(curandGenerateNormal(gen, samples, pathN, 0.0, 1.0));
|
||||
}
|
||||
|
||||
// for(int i=0; i<10; i++) printf("CPU sample = %f\n", samples[i]);
|
||||
// for(int i=0; i<10; i++) printf("CPU sample = %f\n", samples[i]);
|
||||
|
||||
double sum = 0, sum2 = 0;
|
||||
double sum = 0, sum2 = 0;
|
||||
|
||||
for (int pos = 0; pos < pathN; pos++) {
|
||||
double sample = samples[pos];
|
||||
double callValue = endCallValue(S, X, sample, MuByT, VBySqrtT);
|
||||
sum += callValue;
|
||||
sum2 += callValue * callValue;
|
||||
}
|
||||
for (int pos = 0; pos < pathN; pos++) {
|
||||
double sample = samples[pos];
|
||||
double callValue = endCallValue(S, X, sample, MuByT, VBySqrtT);
|
||||
sum += callValue;
|
||||
sum2 += callValue * callValue;
|
||||
}
|
||||
|
||||
if (h_Samples == NULL) free(samples);
|
||||
if (h_Samples == NULL)
|
||||
free(samples);
|
||||
|
||||
checkCudaErrors(curandDestroyGenerator(gen));
|
||||
checkCudaErrors(curandDestroyGenerator(gen));
|
||||
|
||||
// Derive average from the total sum and discount by riskfree rate
|
||||
callValue.Expected = (float)(exp(-R * T) * sum / (double)pathN);
|
||||
// Standard deviation
|
||||
double stdDev = sqrt(((double)pathN * sum2 - sum * sum) /
|
||||
((double)pathN * (double)(pathN - 1)));
|
||||
// Confidence width; in 95% of all cases theoretical value lies within these
|
||||
// borders
|
||||
callValue.Confidence =
|
||||
(float)(exp(-R * T) * 1.96 * stdDev / sqrt((double)pathN));
|
||||
// Derive average from the total sum and discount by riskfree rate
|
||||
callValue.Expected = (float)(exp(-R * T) * sum / (double)pathN);
|
||||
// Standard deviation
|
||||
double stdDev = sqrt(((double)pathN * sum2 - sum * sum) / ((double)pathN * (double)(pathN - 1)));
|
||||
// Confidence width; in 95% of all cases theoretical value lies within these
|
||||
// borders
|
||||
callValue.Confidence = (float)(exp(-R * T) * 1.96 * stdDev / sqrt((double)pathN));
|
||||
}
|
||||
|
||||
@@ -28,13 +28,14 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Global types
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <cooperative_groups.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
#include <helper_cuda.h>
|
||||
#include <curand_kernel.h>
|
||||
#include <helper_cuda.h>
|
||||
|
||||
#include "MonteCarlo_common.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -49,26 +50,27 @@ namespace cg = cooperative_groups;
|
||||
#define MAX_OPTIONS (1024 * 1024)
|
||||
|
||||
// Preprocessed input option data
|
||||
typedef struct {
|
||||
real S;
|
||||
real X;
|
||||
real MuByT;
|
||||
real VBySqrtT;
|
||||
typedef struct
|
||||
{
|
||||
real S;
|
||||
real X;
|
||||
real MuByT;
|
||||
real VBySqrtT;
|
||||
} __TOptionData;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Overloaded shortcut payoff functions for different precision modes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
__device__ inline float endCallValue(float S, float X, float r, float MuByT,
|
||||
float VBySqrtT) {
|
||||
float callValue = S * __expf(MuByT + VBySqrtT * r) - X;
|
||||
return (callValue > 0.0F) ? callValue : 0.0F;
|
||||
__device__ inline float endCallValue(float S, float X, float r, float MuByT, float VBySqrtT)
|
||||
{
|
||||
float callValue = S * __expf(MuByT + VBySqrtT * r) - X;
|
||||
return (callValue > 0.0F) ? callValue : 0.0F;
|
||||
}
|
||||
|
||||
__device__ inline double endCallValue(double S, double X, double r,
|
||||
double MuByT, double VBySqrtT) {
|
||||
double callValue = S * exp(MuByT + VBySqrtT * r) - X;
|
||||
return (callValue > 0.0) ? callValue : 0.0;
|
||||
__device__ inline double endCallValue(double S, double X, double r, double MuByT, double VBySqrtT)
|
||||
{
|
||||
double callValue = S * exp(MuByT + VBySqrtT * r) - X;
|
||||
return (callValue > 0.0) ? callValue : 0.0;
|
||||
}
|
||||
|
||||
#define THREAD_N 256
|
||||
@@ -78,149 +80,145 @@ __device__ inline double endCallValue(double S, double X, double r,
|
||||
// per option. It is fastest when the number of thread blocks times the work per
|
||||
// block is high enough to keep the GPU busy.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static __global__ void MonteCarloOneBlockPerOption(
|
||||
curandState *__restrict rngStates,
|
||||
const __TOptionData *__restrict d_OptionData,
|
||||
__TOptionValue *__restrict d_CallValue, int pathN, int optionN) {
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);
|
||||
static __global__ void MonteCarloOneBlockPerOption(curandState *__restrict rngStates,
|
||||
const __TOptionData *__restrict d_OptionData,
|
||||
__TOptionValue *__restrict d_CallValue,
|
||||
int pathN,
|
||||
int optionN)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);
|
||||
|
||||
const int SUM_N = THREAD_N;
|
||||
__shared__ real s_SumCall[SUM_N];
|
||||
__shared__ real s_Sum2Call[SUM_N];
|
||||
const int SUM_N = THREAD_N;
|
||||
__shared__ real s_SumCall[SUM_N];
|
||||
__shared__ real s_Sum2Call[SUM_N];
|
||||
|
||||
// determine global thread id
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
// determine global thread id
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
// Copy random number state to local memory for efficiency
|
||||
curandState localState = rngStates[tid];
|
||||
for (int optionIndex = blockIdx.x; optionIndex < optionN;
|
||||
optionIndex += gridDim.x) {
|
||||
const real S = d_OptionData[optionIndex].S;
|
||||
const real X = d_OptionData[optionIndex].X;
|
||||
const real MuByT = d_OptionData[optionIndex].MuByT;
|
||||
const real VBySqrtT = d_OptionData[optionIndex].VBySqrtT;
|
||||
// Copy random number state to local memory for efficiency
|
||||
curandState localState = rngStates[tid];
|
||||
for (int optionIndex = blockIdx.x; optionIndex < optionN; optionIndex += gridDim.x) {
|
||||
const real S = d_OptionData[optionIndex].S;
|
||||
const real X = d_OptionData[optionIndex].X;
|
||||
const real MuByT = d_OptionData[optionIndex].MuByT;
|
||||
const real VBySqrtT = d_OptionData[optionIndex].VBySqrtT;
|
||||
|
||||
// Cycle through the entire samples array:
|
||||
// derive end stock price for each path
|
||||
// accumulate partial integrals into intermediate shared memory buffer
|
||||
for (int iSum = threadIdx.x; iSum < SUM_N; iSum += blockDim.x) {
|
||||
__TOptionValue sumCall = {0, 0};
|
||||
// Cycle through the entire samples array:
|
||||
// derive end stock price for each path
|
||||
// accumulate partial integrals into intermediate shared memory buffer
|
||||
for (int iSum = threadIdx.x; iSum < SUM_N; iSum += blockDim.x) {
|
||||
__TOptionValue sumCall = {0, 0};
|
||||
|
||||
#pragma unroll 8
|
||||
for (int i = iSum; i < pathN; i += SUM_N) {
|
||||
real r = curand_normal(&localState);
|
||||
real callValue = endCallValue(S, X, r, MuByT, VBySqrtT);
|
||||
sumCall.Expected += callValue;
|
||||
sumCall.Confidence += callValue * callValue;
|
||||
}
|
||||
for (int i = iSum; i < pathN; i += SUM_N) {
|
||||
real r = curand_normal(&localState);
|
||||
real callValue = endCallValue(S, X, r, MuByT, VBySqrtT);
|
||||
sumCall.Expected += callValue;
|
||||
sumCall.Confidence += callValue * callValue;
|
||||
}
|
||||
|
||||
s_SumCall[iSum] = sumCall.Expected;
|
||||
s_Sum2Call[iSum] = sumCall.Confidence;
|
||||
s_SumCall[iSum] = sumCall.Expected;
|
||||
s_Sum2Call[iSum] = sumCall.Confidence;
|
||||
}
|
||||
|
||||
// Reduce shared memory accumulators
|
||||
// and write final result to global memory
|
||||
cg::sync(cta);
|
||||
sumReduce<real, SUM_N, THREAD_N>(s_SumCall, s_Sum2Call, cta, tile32, &d_CallValue[optionIndex]);
|
||||
}
|
||||
|
||||
// Reduce shared memory accumulators
|
||||
// and write final result to global memory
|
||||
cg::sync(cta);
|
||||
sumReduce<real, SUM_N, THREAD_N>(s_SumCall, s_Sum2Call, cta, tile32,
|
||||
&d_CallValue[optionIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
static __global__ void rngSetupStates(curandState *rngState, int device_id) {
|
||||
// determine global thread id
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
// Each threadblock gets different seed,
|
||||
// Threads within a threadblock get different sequence numbers
|
||||
curand_init(blockIdx.x + gridDim.x * device_id, threadIdx.x, 0,
|
||||
&rngState[tid]);
|
||||
static __global__ void rngSetupStates(curandState *rngState, int device_id)
|
||||
{
|
||||
// determine global thread id
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
// Each threadblock gets different seed,
|
||||
// Threads within a threadblock get different sequence numbers
|
||||
curand_init(blockIdx.x + gridDim.x * device_id, threadIdx.x, 0, &rngState[tid]);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Host-side interface to GPU Monte Carlo
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern "C" void initMonteCarloGPU(TOptionPlan *plan) {
|
||||
checkCudaErrors(cudaMalloc(&plan->d_OptionData,
|
||||
sizeof(__TOptionData) * (plan->optionCount)));
|
||||
checkCudaErrors(cudaMalloc(&plan->d_CallValue,
|
||||
sizeof(__TOptionValue) * (plan->optionCount)));
|
||||
checkCudaErrors(cudaMallocHost(&plan->h_OptionData,
|
||||
sizeof(__TOptionData) * (plan->optionCount)));
|
||||
// Allocate internal device memory
|
||||
checkCudaErrors(cudaMallocHost(&plan->h_CallValue,
|
||||
sizeof(__TOptionValue) * (plan->optionCount)));
|
||||
// Allocate states for pseudo random number generators
|
||||
checkCudaErrors(cudaMalloc((void **)&plan->rngStates,
|
||||
plan->gridSize * THREAD_N * sizeof(curandState)));
|
||||
checkCudaErrors(cudaMemset(plan->rngStates, 0,
|
||||
plan->gridSize * THREAD_N * sizeof(curandState)));
|
||||
extern "C" void initMonteCarloGPU(TOptionPlan *plan)
|
||||
{
|
||||
checkCudaErrors(cudaMalloc(&plan->d_OptionData, sizeof(__TOptionData) * (plan->optionCount)));
|
||||
checkCudaErrors(cudaMalloc(&plan->d_CallValue, sizeof(__TOptionValue) * (plan->optionCount)));
|
||||
checkCudaErrors(cudaMallocHost(&plan->h_OptionData, sizeof(__TOptionData) * (plan->optionCount)));
|
||||
// Allocate internal device memory
|
||||
checkCudaErrors(cudaMallocHost(&plan->h_CallValue, sizeof(__TOptionValue) * (plan->optionCount)));
|
||||
// Allocate states for pseudo random number generators
|
||||
checkCudaErrors(cudaMalloc((void **)&plan->rngStates, plan->gridSize * THREAD_N * sizeof(curandState)));
|
||||
checkCudaErrors(cudaMemset(plan->rngStates, 0, plan->gridSize * THREAD_N * sizeof(curandState)));
|
||||
|
||||
// place each device pathN random numbers apart on the random number sequence
|
||||
rngSetupStates<<<plan->gridSize, THREAD_N>>>(plan->rngStates, plan->device);
|
||||
getLastCudaError("rngSetupStates kernel failed.\n");
|
||||
// place each device pathN random numbers apart on the random number sequence
|
||||
rngSetupStates<<<plan->gridSize, THREAD_N>>>(plan->rngStates, plan->device);
|
||||
getLastCudaError("rngSetupStates kernel failed.\n");
|
||||
}
|
||||
|
||||
// Compute statistics and deallocate internal device memory
|
||||
extern "C" void closeMonteCarloGPU(TOptionPlan *plan) {
|
||||
for (int i = 0; i < plan->optionCount; i++) {
|
||||
const double RT = plan->optionData[i].R * plan->optionData[i].T;
|
||||
const double sum = plan->h_CallValue[i].Expected;
|
||||
const double sum2 = plan->h_CallValue[i].Confidence;
|
||||
const double pathN = plan->pathN;
|
||||
// Derive average from the total sum and discount by riskfree rate
|
||||
plan->callValue[i].Expected = (float)(exp(-RT) * sum / pathN);
|
||||
// Standard deviation
|
||||
double stdDev = sqrt((pathN * sum2 - sum * sum) / (pathN * (pathN - 1)));
|
||||
// Confidence width; in 95% of all cases theoretical value lies within these
|
||||
// borders
|
||||
plan->callValue[i].Confidence =
|
||||
(float)(exp(-RT) * 1.96 * stdDev / sqrt(pathN));
|
||||
}
|
||||
extern "C" void closeMonteCarloGPU(TOptionPlan *plan)
|
||||
{
|
||||
for (int i = 0; i < plan->optionCount; i++) {
|
||||
const double RT = plan->optionData[i].R * plan->optionData[i].T;
|
||||
const double sum = plan->h_CallValue[i].Expected;
|
||||
const double sum2 = plan->h_CallValue[i].Confidence;
|
||||
const double pathN = plan->pathN;
|
||||
// Derive average from the total sum and discount by riskfree rate
|
||||
plan->callValue[i].Expected = (float)(exp(-RT) * sum / pathN);
|
||||
// Standard deviation
|
||||
double stdDev = sqrt((pathN * sum2 - sum * sum) / (pathN * (pathN - 1)));
|
||||
// Confidence width; in 95% of all cases theoretical value lies within these
|
||||
// borders
|
||||
plan->callValue[i].Confidence = (float)(exp(-RT) * 1.96 * stdDev / sqrt(pathN));
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaFree(plan->rngStates));
|
||||
checkCudaErrors(cudaFreeHost(plan->h_CallValue));
|
||||
checkCudaErrors(cudaFreeHost(plan->h_OptionData));
|
||||
checkCudaErrors(cudaFree(plan->d_CallValue));
|
||||
checkCudaErrors(cudaFree(plan->d_OptionData));
|
||||
checkCudaErrors(cudaFree(plan->rngStates));
|
||||
checkCudaErrors(cudaFreeHost(plan->h_CallValue));
|
||||
checkCudaErrors(cudaFreeHost(plan->h_OptionData));
|
||||
checkCudaErrors(cudaFree(plan->d_CallValue));
|
||||
checkCudaErrors(cudaFree(plan->d_OptionData));
|
||||
}
|
||||
|
||||
// Main computations
|
||||
extern "C" void MonteCarloGPU(TOptionPlan *plan, cudaStream_t stream) {
|
||||
__TOptionValue *h_CallValue = plan->h_CallValue;
|
||||
extern "C" void MonteCarloGPU(TOptionPlan *plan, cudaStream_t stream)
|
||||
{
|
||||
__TOptionValue *h_CallValue = plan->h_CallValue;
|
||||
|
||||
if (plan->optionCount <= 0 || plan->optionCount > MAX_OPTIONS) {
|
||||
printf("MonteCarloGPU(): bad option count.\n");
|
||||
return;
|
||||
}
|
||||
if (plan->optionCount <= 0 || plan->optionCount > MAX_OPTIONS) {
|
||||
printf("MonteCarloGPU(): bad option count.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
__TOptionData *h_OptionData = (__TOptionData *)plan->h_OptionData;
|
||||
__TOptionData *h_OptionData = (__TOptionData *)plan->h_OptionData;
|
||||
|
||||
for (int i = 0; i < plan->optionCount; i++) {
|
||||
const double T = plan->optionData[i].T;
|
||||
const double R = plan->optionData[i].R;
|
||||
const double V = plan->optionData[i].V;
|
||||
const double MuByT = (R - 0.5 * V * V) * T;
|
||||
const double VBySqrtT = V * sqrt(T);
|
||||
h_OptionData[i].S = (real)plan->optionData[i].S;
|
||||
h_OptionData[i].X = (real)plan->optionData[i].X;
|
||||
h_OptionData[i].MuByT = (real)MuByT;
|
||||
h_OptionData[i].VBySqrtT = (real)VBySqrtT;
|
||||
}
|
||||
for (int i = 0; i < plan->optionCount; i++) {
|
||||
const double T = plan->optionData[i].T;
|
||||
const double R = plan->optionData[i].R;
|
||||
const double V = plan->optionData[i].V;
|
||||
const double MuByT = (R - 0.5 * V * V) * T;
|
||||
const double VBySqrtT = V * sqrt(T);
|
||||
h_OptionData[i].S = (real)plan->optionData[i].S;
|
||||
h_OptionData[i].X = (real)plan->optionData[i].X;
|
||||
h_OptionData[i].MuByT = (real)MuByT;
|
||||
h_OptionData[i].VBySqrtT = (real)VBySqrtT;
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(plan->d_OptionData, h_OptionData,
|
||||
plan->optionCount * sizeof(__TOptionData),
|
||||
cudaMemcpyHostToDevice, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(
|
||||
plan->d_OptionData, h_OptionData, plan->optionCount * sizeof(__TOptionData), cudaMemcpyHostToDevice, stream));
|
||||
|
||||
MonteCarloOneBlockPerOption<<<plan->gridSize, THREAD_N, 0, stream>>>(
|
||||
plan->rngStates, (__TOptionData *)(plan->d_OptionData),
|
||||
(__TOptionValue *)(plan->d_CallValue), plan->pathN, plan->optionCount);
|
||||
getLastCudaError("MonteCarloOneBlockPerOption() execution failed\n");
|
||||
MonteCarloOneBlockPerOption<<<plan->gridSize, THREAD_N, 0, stream>>>(plan->rngStates,
|
||||
(__TOptionData *)(plan->d_OptionData),
|
||||
(__TOptionValue *)(plan->d_CallValue),
|
||||
plan->pathN,
|
||||
plan->optionCount);
|
||||
getLastCudaError("MonteCarloOneBlockPerOption() execution failed\n");
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(h_CallValue, plan->d_CallValue,
|
||||
plan->optionCount * sizeof(__TOptionValue),
|
||||
cudaMemcpyDeviceToHost, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(
|
||||
h_CallValue, plan->d_CallValue, plan->optionCount * sizeof(__TOptionValue), cudaMemcpyDeviceToHost, stream));
|
||||
|
||||
// cudaDeviceSynchronize();
|
||||
// cudaDeviceSynchronize();
|
||||
}
|
||||
|
||||
@@ -40,40 +40,40 @@ namespace cg = cooperative_groups;
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T, int SUM_N, int blockSize>
|
||||
__device__ void sumReduce(T *sum, T *sum2, cg::thread_block &cta,
|
||||
cg::thread_block_tile<32> &tile32,
|
||||
__TOptionValue *d_CallValue) {
|
||||
const int VEC = 32;
|
||||
const int tid = cta.thread_rank();
|
||||
__device__ void
|
||||
sumReduce(T *sum, T *sum2, cg::thread_block &cta, cg::thread_block_tile<32> &tile32, __TOptionValue *d_CallValue)
|
||||
{
|
||||
const int VEC = 32;
|
||||
const int tid = cta.thread_rank();
|
||||
|
||||
T beta = sum[tid];
|
||||
T beta2 = sum2[tid];
|
||||
T temp, temp2;
|
||||
T beta = sum[tid];
|
||||
T beta2 = sum2[tid];
|
||||
T temp, temp2;
|
||||
|
||||
for (int i = VEC / 2; i > 0; i >>= 1) {
|
||||
if (tile32.thread_rank() < i) {
|
||||
temp = sum[tid + i];
|
||||
temp2 = sum2[tid + i];
|
||||
beta += temp;
|
||||
beta2 += temp2;
|
||||
sum[tid] = beta;
|
||||
sum2[tid] = beta2;
|
||||
for (int i = VEC / 2; i > 0; i >>= 1) {
|
||||
if (tile32.thread_rank() < i) {
|
||||
temp = sum[tid + i];
|
||||
temp2 = sum2[tid + i];
|
||||
beta += temp;
|
||||
beta2 += temp2;
|
||||
sum[tid] = beta;
|
||||
sum2[tid] = beta2;
|
||||
}
|
||||
cg::sync(tile32);
|
||||
}
|
||||
cg::sync(tile32);
|
||||
}
|
||||
cg::sync(cta);
|
||||
cg::sync(cta);
|
||||
|
||||
if (tid == 0) {
|
||||
beta = 0;
|
||||
beta2 = 0;
|
||||
for (int i = 0; i < blockDim.x; i += VEC) {
|
||||
beta += sum[i];
|
||||
beta2 += sum2[i];
|
||||
if (tid == 0) {
|
||||
beta = 0;
|
||||
beta2 = 0;
|
||||
for (int i = 0; i < blockDim.x; i += VEC) {
|
||||
beta += sum[i];
|
||||
beta2 += sum2[i];
|
||||
}
|
||||
__TOptionValue t = {beta, beta2};
|
||||
*d_CallValue = t;
|
||||
}
|
||||
__TOptionValue t = {beta, beta2};
|
||||
*d_CallValue = t;
|
||||
}
|
||||
cg::sync(cta);
|
||||
cg::sync(cta);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -29,41 +29,46 @@
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||
// Create thread
|
||||
CUTThread cutStartThread(CUT_THREADROUTINE func, void *data) {
|
||||
return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, data, 0, NULL);
|
||||
CUTThread cutStartThread(CUT_THREADROUTINE func, void *data)
|
||||
{
|
||||
return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, data, 0, NULL);
|
||||
}
|
||||
|
||||
// Wait for thread to finish
|
||||
void cutEndThread(CUTThread thread) {
|
||||
WaitForSingleObject(thread, INFINITE);
|
||||
CloseHandle(thread);
|
||||
void cutEndThread(CUTThread thread)
|
||||
{
|
||||
WaitForSingleObject(thread, INFINITE);
|
||||
CloseHandle(thread);
|
||||
}
|
||||
|
||||
// Wait for multiple threads
|
||||
void cutWaitForThreads(const CUTThread *threads, int num) {
|
||||
WaitForMultipleObjects(num, threads, true, INFINITE);
|
||||
void cutWaitForThreads(const CUTThread *threads, int num)
|
||||
{
|
||||
WaitForMultipleObjects(num, threads, true, INFINITE);
|
||||
|
||||
for (int i = 0; i < num; i++) {
|
||||
CloseHandle(threads[i]);
|
||||
}
|
||||
for (int i = 0; i < num; i++) {
|
||||
CloseHandle(threads[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
// Create thread
|
||||
CUTThread cutStartThread(CUT_THREADROUTINE func, void *data) {
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, func, data);
|
||||
return thread;
|
||||
CUTThread cutStartThread(CUT_THREADROUTINE func, void *data)
|
||||
{
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, func, data);
|
||||
return thread;
|
||||
}
|
||||
|
||||
// Wait for thread to finish
|
||||
void cutEndThread(CUTThread thread) { pthread_join(thread, NULL); }
|
||||
|
||||
// Wait for multiple threads
|
||||
void cutWaitForThreads(const CUTThread *threads, int num) {
|
||||
for (int i = 0; i < num; i++) {
|
||||
cutEndThread(threads[i]);
|
||||
}
|
||||
void cutWaitForThreads(const CUTThread *threads, int num)
|
||||
{
|
||||
for (int i = 0; i < num; i++) {
|
||||
cutEndThread(threads[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,7 +38,7 @@ typedef HANDLE CUTThread;
|
||||
typedef unsigned(WINAPI *CUT_THREADROUTINE)(void *);
|
||||
|
||||
#define CUT_THREADPROC unsigned WINAPI
|
||||
#define CUT_THREADEND return 0
|
||||
#define CUT_THREADEND return 0
|
||||
|
||||
#else
|
||||
// POSIX threads.
|
||||
@@ -52,20 +52,21 @@ typedef void *(*CUT_THREADROUTINE)(void *);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// Create thread.
|
||||
CUTThread cutStartThread(CUT_THREADROUTINE, void *data);
|
||||
// Create thread.
|
||||
CUTThread cutStartThread(CUT_THREADROUTINE, void *data);
|
||||
|
||||
// Wait for thread to finish.
|
||||
void cutEndThread(CUTThread thread);
|
||||
// Wait for thread to finish.
|
||||
void cutEndThread(CUTThread thread);
|
||||
|
||||
// Wait for multiple threads.
|
||||
void cutWaitForThreads(const CUTThread *threads, int num);
|
||||
// Wait for multiple threads.
|
||||
void cutWaitForThreads(const CUTThread *threads, int num);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // MULTITHREADING_H
|
||||
#endif // MULTITHREADING_H
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#ifndef REALTYPE_H
|
||||
#define REALTYPE_H
|
||||
|
||||
//#define DOUBLE_PRECISION
|
||||
// #define DOUBLE_PRECISION
|
||||
|
||||
#ifndef DOUBLE_PRECISION
|
||||
typedef float real;
|
||||
|
||||
Reference in New Issue
Block a user