| // Copyright (c) 2007 Intel Corp. |
| |
| // Black-Scholes |
| // Analytical method for calculating European Options |
| // |
| // |
| // Reference Source: Options, Futures, and Other Derivatives, 3rd Edition, Prentice |
| // Hall, John C. Hull, |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <math.h> |
| #include <string.h> |
| |
| #ifndef WIN32 |
| #include <pmmintrin.h> |
| #else |
| #include <xmmintrin.h> |
| #endif |
| |
| #ifdef ENABLE_PARSEC_HOOKS |
| #include <hooks.h> |
| #endif |
| |
| // Multi-threaded pthreads header |
| #ifdef ENABLE_THREADS |
| // Add the following line so that icc 9.0 is compatible with pthread lib. |
| #define __thread __threadp |
| MAIN_ENV |
| #undef __thread |
| #endif |
| |
| // Multi-threaded OpenMP header |
| #ifdef ENABLE_OPENMP |
| #include <omp.h> |
| #endif |
| |
| #ifdef ENABLE_TBB |
| #include "tbb/blocked_range.h" |
| #include "tbb/parallel_for.h" |
| #include "tbb/task_scheduler_init.h" |
| #include "tbb/tick_count.h" |
| |
| using namespace std; |
| using namespace tbb; |
| #endif //ENABLE_TBB |
| |
| // Multi-threaded header for Windows |
| #ifdef WIN32 |
| #pragma warning(disable : 4305) |
| #pragma warning(disable : 4244) |
| #include <windows.h> |
| #endif |
| |
| #ifdef __GNUC__ |
| #define _MM_ALIGN16 __attribute__((aligned (16))) |
| #define MUSTINLINE __attribute__((always_inline)) |
| #else |
| #define MUSTINLINE __forceinline |
| #endif |
| |
| // NCO = Number of Concurrent Options = SIMD Width |
| // NCO is currently set in the Makefile. |
| #ifndef NCO |
| #error NCO must be defined. |
| #endif |
| |
| #if (NCO==2) |
| #define fptype double |
| #define SIMD_WIDTH 2 |
| #define _MMR __m128d |
| #define _MM_LOAD _mm_load_pd |
| #define _MM_STORE _mm_store_pd |
| #define _MM_MUL _mm_mul_pd |
| #define _MM_ADD _mm_add_pd |
| #define _MM_SUB _mm_sub_pd |
| #define _MM_DIV _mm_div_pd |
| #define _MM_SQRT _mm_sqrt_pd |
| #define _MM_SET(A) _mm_set_pd(A,A) |
| #define _MM_SETR _mm_set_pd |
| #endif |
| |
| #if (NCO==4) |
| #define fptype float |
| #define SIMD_WIDTH 4 |
| #define _MMR __m128 |
| #define _MM_LOAD _mm_load_ps |
| #define _MM_STORE _mm_store_ps |
| #define _MM_MUL _mm_mul_ps |
| #define _MM_ADD _mm_add_ps |
| #define _MM_SUB _mm_sub_ps |
| #define _MM_DIV _mm_div_ps |
| #define _MM_SQRT _mm_sqrt_ps |
| #define _MM_SET(A) _mm_set_ps(A,A,A,A) |
| #define _MM_SETR _mm_set_ps |
| #endif |
| |
| #define NUM_RUNS 100 |
| |
| typedef struct OptionData_ { |
| fptype s; // spot price |
| fptype strike; // strike price |
| fptype r; // risk-free interest rate |
| fptype divq; // dividend rate |
| fptype v; // volatility |
| fptype t; // time to maturity or option expiration in years |
| // (1yr = 1.0, 6mos = 0.5, 3mos = 0.25, ..., etc) |
| char OptionType; // Option type. "P"=PUT, "C"=CALL |
| fptype divs; // dividend vals (not used in this test) |
| fptype DGrefval; // DerivaGem Reference Value |
| } OptionData; |
| |
| _MM_ALIGN16 OptionData* data; |
| _MM_ALIGN16 fptype* prices; |
| int numOptions; |
| |
| int * otype; |
| fptype * sptprice; |
| fptype * strike; |
| fptype * rate; |
| fptype * volatility; |
| fptype * otime; |
| int numError = 0; |
| int nThreads; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| /////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| // Cumulative Normal Distribution Function |
| // See Hull, Section 11.8, P.243-244 |
| #define inv_sqrt_2xPI 0.39894228040143270286 |
| |
| MUSTINLINE void CNDF ( fptype * OutputX, fptype * InputX ) |
| { |
| int sign[SIMD_WIDTH]; |
| int i; |
| _MMR xInput; |
| _MMR xNPrimeofX; |
| _MM_ALIGN16 fptype expValues[SIMD_WIDTH]; |
| _MMR xK2; |
| _MMR xK2_2, xK2_3, xK2_4, xK2_5; |
| _MMR xLocal, xLocal_1, xLocal_2, xLocal_3; |
| |
| for (i=0; i<SIMD_WIDTH; i++) { |
| // Check for negative value of InputX |
| if (InputX[i] < 0.0) { |
| InputX[i] = -InputX[i]; |
| sign[i] = 1; |
| } else |
| sign[i] = 0; |
| } |
| // printf("InputX[0]=%lf\n", InputX[0]); |
| // printf("InputX[1]=%lf\n", InputX[1]); |
| |
| xInput = _MM_LOAD(InputX); |
| |
| // local vars |
| |
| // Compute NPrimeX term common to both four & six decimal accuracy calcs |
| |
| for (i=0; i<SIMD_WIDTH; i++) { |
| expValues[i] = exp(-0.5f * InputX[i] * InputX[i]); |
| // printf("exp[%d]: %f\n", i, expValues[i]); |
| } |
| |
| xNPrimeofX = _MM_LOAD(expValues); |
| xNPrimeofX = _MM_MUL(xNPrimeofX, _MM_SET(inv_sqrt_2xPI)); |
| |
| xK2 = _MM_MUL(_MM_SET(0.2316419), xInput); |
| xK2 = _MM_ADD(xK2, _MM_SET(1.0)); |
| xK2 = _MM_DIV(_MM_SET(1.0), xK2); |
| // xK2 = _mm_rcp_pd(xK2); // No rcp function for double-precision |
| |
| xK2_2 = _MM_MUL(xK2, xK2); |
| xK2_3 = _MM_MUL(xK2_2, xK2); |
| xK2_4 = _MM_MUL(xK2_3, xK2); |
| xK2_5 = _MM_MUL(xK2_4, xK2); |
| |
| xLocal_1 = _MM_MUL(xK2, _MM_SET(0.319381530)); |
| xLocal_2 = _MM_MUL(xK2_2, _MM_SET(-0.356563782)); |
| xLocal_3 = _MM_MUL(xK2_3, _MM_SET(1.781477937)); |
| xLocal_2 = _MM_ADD(xLocal_2, xLocal_3); |
| xLocal_3 = _MM_MUL(xK2_4, _MM_SET(-1.821255978)); |
| xLocal_2 = _MM_ADD(xLocal_2, xLocal_3); |
| xLocal_3 = _MM_MUL(xK2_5, _MM_SET(1.330274429)); |
| xLocal_2 = _MM_ADD(xLocal_2, xLocal_3); |
| |
| xLocal_1 = _MM_ADD(xLocal_2, xLocal_1); |
| xLocal = _MM_MUL(xLocal_1, xNPrimeofX); |
| xLocal = _MM_SUB(_MM_SET(1.0), xLocal); |
| |
| _MM_STORE(OutputX, xLocal); |
| // _mm_storel_pd(&OutputX[0], xLocal); |
| // _mm_storeh_pd(&OutputX[1], xLocal); |
| |
| for (i=0; i<SIMD_WIDTH; i++) { |
| if (sign[i]) { |
| OutputX[i] = (1.0 - OutputX[i]); |
| } |
| } |
| } |
| |
| // For debugging |
| void print_xmm(_MMR in, char* s) { |
| int i; |
| _MM_ALIGN16 fptype val[SIMD_WIDTH]; |
| |
| _MM_STORE(val, in); |
| printf("%s: ", s); |
| for (i=0; i<SIMD_WIDTH; i++) { |
| printf("%f ", val[i]); |
| } |
| printf("\n"); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////////////// |
| void BlkSchlsEqEuroNoDiv (fptype * OptionPrice, int numOptions, fptype * sptprice, |
| fptype * strike, fptype * rate, fptype * volatility, |
| fptype * time, int * otype, float timet) |
| { |
| int i; |
| // local private working variables for the calculation |
| _MMR xStockPrice; |
| _MMR xStrikePrice; |
| _MMR xRiskFreeRate; |
| _MMR xVolatility; |
| _MMR xTime; |
| _MMR xSqrtTime; |
| |
| _MM_ALIGN16 fptype logValues[NCO]; |
| _MMR xLogTerm; |
| _MMR xD1, xD2; |
| _MMR xPowerTerm; |
| _MMR xDen; |
| _MM_ALIGN16 fptype d1[SIMD_WIDTH]; |
| _MM_ALIGN16 fptype d2[SIMD_WIDTH]; |
| _MM_ALIGN16 fptype FutureValueX[SIMD_WIDTH]; |
| _MM_ALIGN16 fptype NofXd1[SIMD_WIDTH]; |
| _MM_ALIGN16 fptype NofXd2[SIMD_WIDTH]; |
| _MM_ALIGN16 fptype NegNofXd1[SIMD_WIDTH]; |
| _MM_ALIGN16 fptype NegNofXd2[SIMD_WIDTH]; |
| |
| xStockPrice = _MM_LOAD(sptprice); |
| xStrikePrice = _MM_LOAD(strike); |
| xRiskFreeRate = _MM_LOAD(rate); |
| xVolatility = _MM_LOAD(volatility); |
| xTime = _MM_LOAD(time); |
| |
| xSqrtTime = _MM_SQRT(xTime); |
| |
| for(i=0; i<SIMD_WIDTH;i ++) { |
| logValues[i] = log(sptprice[i] / strike[i]); |
| } |
| |
| xLogTerm = _MM_LOAD(logValues); |
| |
| xPowerTerm = _MM_MUL(xVolatility, xVolatility); |
| xPowerTerm = _MM_MUL(xPowerTerm, _MM_SET(0.5)); |
| xD1 = _MM_ADD(xRiskFreeRate, xPowerTerm); |
| |
| xD1 = _MM_MUL(xD1, xTime); |
| |
| xD1 = _MM_ADD(xD1, xLogTerm); |
| xDen = _MM_MUL(xVolatility, xSqrtTime); |
| xD1 = _MM_DIV(xD1, xDen); |
| xD2 = _MM_SUB(xD1, xDen); |
| |
| _MM_STORE(d1, xD1); |
| _MM_STORE(d2, xD2); |
| |
| CNDF( NofXd1, d1 ); |
| CNDF( NofXd2, d2 ); |
| |
| for (i=0; i<SIMD_WIDTH; i++) { |
| FutureValueX[i] = strike[i] * (exp(-(rate[i])*(time[i]))); |
| // printf("FV=%lf\n", FutureValueX[i]); |
| |
| // NofXd1[i] = NofX(d1[i]); |
| // NofXd2[i] = NofX(d2[i]); |
| // printf("NofXd1=%lf\n", NofXd1[i]); |
| // printf("NofXd2=%lf\n", NofXd2[i]); |
| |
| if (otype[i] == 0) { |
| OptionPrice[i] = (sptprice[i] * NofXd1[i]) - (FutureValueX[i] * NofXd2[i]); |
| } |
| else { |
| NegNofXd1[i] = (1.0 - (NofXd1[i])); |
| NegNofXd2[i] = (1.0 - (NofXd2[i])); |
| OptionPrice[i] = (FutureValueX[i] * NegNofXd2[i]) - (sptprice[i] * NegNofXd1[i]); |
| } |
| // printf("OptionPrice[0] = %lf\n", OptionPrice[i]); |
| } |
| |
| } |
| |
| #ifdef ENABLE_TBB |
| struct mainWork { |
| mainWork(){} |
| mainWork(mainWork &w, tbb::split){} |
| |
| void operator()(const tbb::blocked_range<int> &range) const { |
| fptype price[NCO]; |
| fptype priceDelta; |
| int begin = range.begin(); |
| int end = range.end(); |
| |
| for (int i=begin; i!=end; i+=NCO) { |
| /* Calling main function to calculate option value based on |
| * Black & Scholes's equation. |
| */ |
| |
| BlkSchlsEqEuroNoDiv( price, NCO, &(sptprice[i]), &(strike[i]), |
| &(rate[i]), &(volatility[i]), &(otime[i]), |
| &(otype[i]), 0); |
| for (int k=0; k<NCO; k++) { |
| prices[i+k] = price[k]; |
| |
| #ifdef ERR_CHK |
| priceDelta = data[i+k].DGrefval - price[k]; |
| if( fabs(priceDelta) >= 1e-5 ){ |
| fprintf(stderr,"Error on %d. Computed=%.5f, Ref=%.5f, Delta=%.5f\n", |
| i+k, price, data[i+k].DGrefval, priceDelta); |
| numError ++; |
| } |
| #endif |
| } |
| } |
| } |
| }; |
| |
| #endif // ENABLE_TBB |
| |
| ////////////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////////////// |
| |
| #ifdef ENABLE_TBB |
| int bs_thread(void *tid_ptr) { |
| int j; |
| tbb::affinity_partitioner a; |
| |
| mainWork doall; |
| for (j=0; j<NUM_RUNS; j++) { |
| tbb::parallel_for(tbb::blocked_range<int>(0, numOptions), doall, a); |
| } |
| |
| return 0; |
| } |
| #else // !ENABLE_TBB |
| |
| #ifdef WIN32 |
| DWORD WINAPI bs_thread(LPVOID tid_ptr){ |
| #else |
| int bs_thread(void *tid_ptr) { |
| #endif |
| int i, j, k; |
| fptype price[NCO]; |
| fptype priceDelta; |
| int tid = *(int *)tid_ptr; |
| int start = tid * (numOptions / nThreads); |
| int end = start + (numOptions / nThreads); |
| |
| for (j=0; j<NUM_RUNS; j++) { |
| #ifdef ENABLE_OPENMP |
| #pragma omp parallel for private(i, price, priceDelta) |
| for (i=0; i<numOptions; i += NCO) { |
| #else //ENABLE_OPENMP |
| for (i=start; i<end; i += NCO) { |
| #endif //ENABLE_OPENMP |
| // Calling main function to calculate option value based on Black & Scholes's |
| // equation. |
| BlkSchlsEqEuroNoDiv(price, NCO, &(sptprice[i]), &(strike[i]), |
| &(rate[i]), &(volatility[i]), &(otime[i]), &(otype[i]), 0); |
| for (k=0; k<NCO; k++) { |
| prices[i+k] = price[k]; |
| } |
| #ifdef ERR_CHK |
| for (k=0; k<NCO; k++) { |
| priceDelta = data[i+k].DGrefval - price[k]; |
| if (fabs(priceDelta) >= 1e-4) { |
| printf("Error on %d. Computed=%.5f, Ref=%.5f, Delta=%.5f\n", |
| i + k, price[k], data[i+k].DGrefval, priceDelta); |
| numError ++; |
| } |
| } |
| #endif |
| } |
| } |
| |
| return 0; |
| } |
| #endif //ENABLE_TBB |
| |
| int main (int argc, char **argv) |
| { |
| FILE *file; |
| int i; |
| int loopnum; |
| fptype * buffer; |
| int * buffer2; |
| int rv; |
| |
| #ifdef PARSEC_VERSION |
| #define __PARSEC_STRING(x) #x |
| #define __PARSEC_XSTRING(x) __PARSEC_STRING(x) |
| printf("PARSEC Benchmark Suite Version "__PARSEC_XSTRING(PARSEC_VERSION)"\n"); |
| fflush(NULL); |
| #else |
| printf("PARSEC Benchmark Suite\n"); |
| fflush(NULL); |
| #endif //PARSEC_VERSION |
| #ifdef ENABLE_PARSEC_HOOKS |
| __parsec_bench_begin(__parsec_blackscholes); |
| #endif |
| |
| if (argc != 4) |
| { |
| printf("Usage:\n\t%s <nthreads> <inputFile> <outputFile>\n", argv[0]); |
| exit(1); |
| } |
| nThreads = atoi(argv[1]); |
| char *inputFile = argv[2]; |
| char *outputFile = argv[3]; |
| |
| //Read input data from file |
| file = fopen(inputFile, "r"); |
| if(file == NULL) { |
| printf("ERROR: Unable to open file `%s'.\n", inputFile); |
| exit(1); |
| } |
| rv = fscanf(file, "%i", &numOptions); |
| if(rv != 1) { |
| printf("ERROR: Unable to read from file `%s'.\n", inputFile); |
| fclose(file); |
| exit(1); |
| } |
| if(NCO > numOptions) { |
| printf("ERROR: Not enough work for SIMD operation.\n"); |
| fclose(file); |
| exit(1); |
| } |
| if(nThreads > numOptions/NCO) { |
| printf("WARNING: Not enough work, reducing number of threads to match number of SIMD options packets.\n"); |
| nThreads = numOptions/NCO; |
| } |
| |
| #if !defined(ENABLE_THREADS) && !defined(ENABLE_OPENMP) && !defined(ENABLE_TBB) |
| if(nThreads != 1) { |
| printf("Error: <nthreads> must be 1 (serial version)\n"); |
| exit(1); |
| } |
| #endif |
| |
| data = (OptionData*)malloc(numOptions*sizeof(OptionData)); |
| prices = (fptype*)malloc(numOptions*sizeof(fptype)); |
| for ( loopnum = 0; loopnum < numOptions; ++ loopnum ) |
| { |
| rv = fscanf(file, "%f %f %f %f %f %f %c %f %f", &data[loopnum].s, &data[loopnum].strike, &data[loopnum].r, &data[loopnum].divq, &data[loopnum].v, &data[loopnum].t, &data[loopnum].OptionType, &data[loopnum].divs, &data[loopnum].DGrefval); |
| if(rv != 9) { |
| printf("ERROR: Unable to read from file `%s'.\n", inputFile); |
| fclose(file); |
| exit(1); |
| } |
| } |
| rv = fclose(file); |
| if(rv != 0) { |
| printf("ERROR: Unable to close file `%s'.\n", inputFile); |
| exit(1); |
| } |
| |
| #ifdef ENABLE_THREADS |
| MAIN_INITENV(,8000000,nThreads); |
| #endif |
| printf("Num of Options: %d\n", numOptions); |
| printf("Num of Runs: %d\n", NUM_RUNS); |
| |
| #define PAD 256 |
| #define LINESIZE 64 |
| |
| buffer = (fptype *) malloc(5 * numOptions * sizeof(fptype) + PAD); |
| sptprice = (fptype *) (((unsigned long long)buffer + PAD) & ~(LINESIZE - 1)); |
| strike = sptprice + numOptions; |
| rate = strike + numOptions; |
| volatility = rate + numOptions; |
| otime = volatility + numOptions; |
| |
| buffer2 = (int *) malloc(numOptions * sizeof(fptype) + PAD); |
| otype = (int *) (((unsigned long long)buffer2 + PAD) & ~(LINESIZE - 1)); |
| |
| for (i=0; i<numOptions; i++) { |
| otype[i] = (data[i].OptionType == 'P') ? 1 : 0; |
| sptprice[i] = data[i].s; |
| strike[i] = data[i].strike; |
| rate[i] = data[i].r; |
| volatility[i] = data[i].v; |
| otime[i] = data[i].t; |
| } |
| |
| printf("Size of data: %d\n", numOptions * (sizeof(OptionData) + sizeof(int))); |
| |
| #ifdef ENABLE_PARSEC_HOOKS |
| __parsec_roi_begin(); |
| #endif |
| |
| #ifdef ENABLE_THREADS |
| #ifdef WIN32 |
| HANDLE *threads; |
| int *nums; |
| threads = (HANDLE *) malloc (nThreads * sizeof(HANDLE)); |
| nums = (int *) malloc (nThreads * sizeof(int)); |
| |
| for(i=0; i<nThreads; i++) { |
| nums[i] = i; |
| threads[i] = CreateThread(0, 0, bs_thread, &nums[i], 0, 0); |
| } |
| WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); |
| free(threads); |
| free(nums); |
| #else |
| int *tids; |
| tids = (int *) malloc (nThreads * sizeof(int)); |
| |
| for(i=0; i<nThreads; i++) { |
| tids[i]=i; |
| CREATE_WITH_ARG(bs_thread, &tids[i]); |
| } |
| WAIT_FOR_END(nThreads); |
| free(tids); |
| #endif //WIN32 |
| #else //ENABLE_THREADS |
| #ifdef ENABLE_OPENMP |
| { |
| int tid=0; |
| omp_set_num_threads(nThreads); |
| bs_thread(&tid); |
| } |
| #else //ENABLE_OPENMP |
| #ifdef ENABLE_TBB |
| tbb::task_scheduler_init init(nThreads); |
| |
| int tid=0; |
| bs_thread(&tid); |
| #else //ENABLE_TBB |
| //serial version |
| int tid=0; |
| bs_thread(&tid); |
| #endif //ENABLE_TBB |
| #endif //ENABLE_OPENMP |
| #endif //ENABLE_THREADS |
| |
| #ifdef ENABLE_PARSEC_HOOKS |
| __parsec_roi_end(); |
| #endif |
| |
| //Write prices to output file |
| file = fopen(outputFile, "w"); |
| if(file == NULL) { |
| printf("ERROR: Unable to open file `%s'.\n", outputFile); |
| exit(1); |
| } |
| rv = fprintf(file, "%i\n", numOptions); |
| if(rv < 0) { |
| printf("ERROR: Unable to write to file `%s'.\n", outputFile); |
| fclose(file); |
| exit(1); |
| } |
| for(i=0; i<numOptions; i++) { |
| rv = fprintf(file, "%.18f\n", prices[i]); |
| if(rv < 0) { |
| printf("ERROR: Unable to write to file `%s'.\n", outputFile); |
| fclose(file); |
| exit(1); |
| } |
| } |
| rv = fclose(file); |
| if(rv != 0) { |
| printf("ERROR: Unable to close file `%s'.\n", outputFile); |
| exit(1); |
| } |
| |
| #ifdef ERR_CHK |
| printf("Num Errors: %d\n", numError); |
| #endif |
| free(data); |
| free(prices); |
| |
| #ifdef ENABLE_PARSEC_HOOKS |
| __parsec_bench_end(); |
| #endif |
| |
| return 0; |
| } |
| |