private static void GeneratePaths(float[] paths, float r, float sigma, float dt, int numSims, int numTimesteps) { // int tid = (BlockIndex.X * BlockDimension.X) + ThreadIndex.X; int step = GridDimension.X * BlockDimension.X; // Compute parameters float drift = (r - (0.5f * sigma * sigma)) * dt; float diffusion = sigma * DeviceMath.Sqrt(dt); // Simulate the paths for (int i = tid; i < numSims; i += step) { // Current output index int output = i; // Simulate the path float s = 1.0f; for (int t = 0; t < numTimesteps; t++, output += numSims) { s *= DeviceMath.Exp(drift + (diffusion * NormalRNG.NextFloat())); paths[output] = s; } } }
private static void ComputeValue(float[] values, float[] paths, float optionSpotPrice, float optionStrikePrice, OptionType optionType, int numSims, int numTimesteps) { // Determine thread id int bid = BlockIndex.X; int tid = (BlockIndex.X * BlockDimension.X) + ThreadIndex.X; int step = GridDimension.X * BlockDimension.X; // float sumPayoffs = 0f; for (int i = tid; i < numSims; i += step) { // int pathIndex = i; // Compute the arithmatic average. float avg = 0f; for (int t = 0; t < numTimesteps; t++, pathIndex += numSims) { avg += paths[pathIndex]; } avg = avg * optionSpotPrice / numTimesteps; // Compute the payoff float payoff = DeviceMath.Max(0f, optionType == OptionType.Call ? avg - optionStrikePrice : optionStrikePrice - avg); // Accumulate payoff locally sumPayoffs += payoff; } // Reduce within the block // Perform first level of reduction: // - Write to shared memory int ltid = ThreadIndex.X; m_reductionScratchPad[ltid] = sumPayoffs; Kernel.SyncThreads(); // Do reduction in shared mem for (int s = BlockDimension.X / 2; s > 0; s >>= 1) { if (ltid < s) { m_reductionScratchPad[ltid] += m_reductionScratchPad[ltid + s]; } Kernel.SyncThreads(); } sumPayoffs = m_reductionScratchPad[0]; // Store the result if (ThreadIndex.X == 0) { values[bid] = sumPayoffs; } }
private static void Abs(int[] input, int[] output) { // Thread index int ThreadId = BlockDimension.X * BlockIndex.X + ThreadIndex.X; // Total number of threads in execution grid int TotalThreads = BlockDimension.X * GridDimension.X; // Loop over the test cases (input data), process them, and store the results for comparison against the reference values. for (int CaseIndex = ThreadId; CaseIndex < input.Length; CaseIndex += TotalThreads) { output[CaseIndex] = DeviceMath.Abs(input[CaseIndex]); } }
private static void MultiplyAddGpu(float[] a, float[] b, float[] c, float[] d) { // Get the thread id and total number of threads int ThreadId = BlockDimension.X * BlockIndex.X + ThreadIndex.X; int TotalThreads = BlockDimension.X * GridDimension.X; // Loop over the vectors 'a', 'b', and 'c', multiplying the elements from 'a' and 'b', adding the product to the element from 'c', then storing the result in 'd'. for (int ElementIndex = ThreadId; ElementIndex < a.Length; ElementIndex += TotalThreads) { // Within kernel methods, use the DeviceMath.MultiplyAdd(...) call instead of separate multiply/add steps whenever possible, // as this allows the GPU.NET compiler to emit the most-efficient (i.e., fastest-executing) code for your specific device. d[ElementIndex] = DeviceMath.MultiplyAdd(a[ElementIndex], b[ElementIndex], c[ElementIndex]); //d[ElementIndex] = (a[ElementIndex] * b[ElementIndex]) + c[ElementIndex]; } }
private static void BlackScholesGPUKernel(float[] callResult, float[] putResult, float[] stockPrice, float[] optionStrike, float[] optionYears, float riskFree, float volatility) { // Thread index int ThreadId = BlockDimension.X * BlockIndex.X + ThreadIndex.X; // Total number of threads in execution grid int TotalThreads = BlockDimension.X * GridDimension.X; // No matter how small execution grid is, or how many options we're processing, // by using this loop we'll get perfect memory coalescing for (int OptionIndex = ThreadId; OptionIndex < callResult.Length; OptionIndex += TotalThreads) { float s = stockPrice[OptionIndex]; float x = optionStrike[OptionIndex]; float t = optionYears[OptionIndex]; // Calculate the square root of the time to option expiration, in years float SqrtT = DeviceMath.Sqrt(t); // Calculate the Black-Scholes parameters float d1 = (DeviceMath.Log(s / x) + (riskFree + 0.5f * volatility * volatility) * t) / (volatility * SqrtT); float d2 = d1 - volatility * SqrtT; // Plug the parameters into the Cumulative Normal Distribution (CND) float K1 = 1.0f / (1.0f + 0.2316419f * DeviceMath.Abs(d1)); float CndD1 = RSQRT2PI * DeviceMath.Exp(-0.5f * d1 * d1) * (K1 * (A1 + K1 * (A2 + K1 * (A3 + K1 * (A4 + K1 * A5))))); if (d1 > 0) { CndD1 = 1.0f - CndD1; } float K2 = 1.0f / (1.0f + 0.2316419f * DeviceMath.Abs(d2)); float CndD2 = RSQRT2PI * DeviceMath.Exp(-0.5f * d2 * d2) * (K2 * (A1 + K2 * (A2 + K2 * (A3 + K2 * (A4 + K2 * A5))))); if (d2 > 0) { CndD2 = 1.0f - CndD2; } // Calculate the discount rate float ExpRT = DeviceMath.Exp(-1.0f * riskFree * t); // Calculate the values of the call and put options callResult[OptionIndex] = s * CndD1 - x * ExpRT * CndD2; putResult[OptionIndex] = x * ExpRT * (1.0f - CndD2) - s * (1.0f - CndD1); } }