Skip to content

Commit d4a712e

Browse files
committed
Add Black-Scholes benchmark.
1 parent 85d95c9 commit d4a712e

9 files changed

Lines changed: 11667 additions & 1 deletion

File tree

CMakeLists.txt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,6 @@ target_link_libraries(
355355
)
356356
set_target_properties(matrix_multiplication PROPERTIES COMPILE_FLAGS ${OpenMP_CXX_FLAGS})
357357

358-
359358
## benchmark 8: Mandelbrot set
360359
message(STATUS "benchmark 8: Mandelbrot set")
361360
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${TF_BENCHMARK_DIR}/mandelbrot_set)
@@ -373,6 +372,23 @@ target_link_libraries(
373372
)
374373
set_target_properties(mandelbrot_set PROPERTIES COMPILE_FLAGS ${OpenMP_CXX_FLAGS})
375374

375+
## benchmark 9: Black–Scholes
376+
message(STATUS "benchmark 9: Black-Scholes Partial Differential Equation")
377+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${TF_BENCHMARK_DIR}/black_scholes)
378+
add_executable(
379+
black_scholes
380+
${TF_BENCHMARK_DIR}/black_scholes/main.cpp
381+
${TF_BENCHMARK_DIR}/black_scholes/omp.cpp
382+
${TF_BENCHMARK_DIR}/black_scholes/tbb.cpp
383+
${TF_BENCHMARK_DIR}/black_scholes/taskflow.cpp
384+
)
385+
target_include_directories(black_scholes PRIVATE ${PROJECT_SOURCE_DIR}/3rd-party/CLI11)
386+
target_link_libraries(
387+
black_scholes
388+
${PROJECT_NAME} Threads::Threads ${TBB_IMPORTED_TARGETS} ${OpenMP_CXX_LIBRARIES}
389+
)
390+
set_target_properties(black_scholes PROPERTIES COMPILE_FLAGS ${OpenMP_CXX_FLAGS})
391+
376392
endif()
377393

378394
# -----------------------------------------------------------------------------

benchmark/black_scholes/common.hpp

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
#include <stdlib.h>
2+
#include <math.h>
3+
#include <string>
4+
#include <cassert>
5+
#include <iostream>
6+
#include <chrono>
7+
#include <fstream>
8+
#include <memory>
9+
10+
//Precision to use for calculations
11+
#define FPTYPE float
12+
#define NUM_RUNS 100
13+
14+
15+
typedef struct OptionData_ {
16+
FPTYPE s; // spot price
17+
FPTYPE strike; // strike price
18+
FPTYPE r; // risk-free interest rate
19+
FPTYPE divq; // dividend rate
20+
FPTYPE v; // volatility
21+
FPTYPE t; // time to maturity or option expiration in years
22+
// (1yr = 1.0, 6mos = 0.5, 3mos = 0.25, ..., etc)
23+
char OptionType; // Option type. "P"=PUT, "C"=CALL
24+
FPTYPE divs; // dividend vals (not used in this test)
25+
FPTYPE DGrefval; // DerivaGem Reference Value
26+
} OptionData;
27+
28+
inline OptionData *data;
29+
inline FPTYPE *prices;
30+
inline int numOptions;
31+
32+
inline int* otype;
33+
inline FPTYPE* sptprice;
34+
inline FPTYPE* strike;
35+
inline FPTYPE* rate;
36+
inline FPTYPE* volatility;
37+
inline FPTYPE* otime;
38+
inline int numError {0};
39+
40+
inline FPTYPE* BUFFER {nullptr};
41+
inline int* BUFFER2 {nullptr};
42+
43+
////////////////////////////////////////////////////////////////////////////////
44+
// Cumulative Normal Distribution Function
45+
// See Hull, Section 11.8, P.243-244
46+
////////////////////////////////////////////////////////////////////////////////
47+
#define inv_sqrt_2xPI 0.39894228040143270286
48+
49+
inline FPTYPE CNDF( FPTYPE InputX ) {
50+
int sign;
51+
52+
FPTYPE OutputX;
53+
FPTYPE xInput;
54+
FPTYPE xNPrimeofX;
55+
FPTYPE expValues;
56+
FPTYPE xK2;
57+
FPTYPE xK2_2, xK2_3;
58+
FPTYPE xK2_4, xK2_5;
59+
FPTYPE xLocal, xLocal_1;
60+
FPTYPE xLocal_2, xLocal_3;
61+
62+
// Check for negative value of InputX
63+
if (InputX < 0.0) {
64+
InputX = -InputX;
65+
sign = 1;
66+
}
67+
else {
68+
sign = 0;
69+
}
70+
71+
xInput = InputX;
72+
73+
// Compute NPrimeX term common to both four & six decimal accuracy calcs
74+
expValues = std::exp(-0.5f * InputX * InputX);
75+
xNPrimeofX = expValues;
76+
xNPrimeofX = xNPrimeofX * inv_sqrt_2xPI;
77+
78+
xK2 = 0.2316419 * xInput;
79+
xK2 = 1.0 + xK2;
80+
xK2 = 1.0 / xK2;
81+
xK2_2 = xK2 * xK2;
82+
xK2_3 = xK2_2 * xK2;
83+
xK2_4 = xK2_3 * xK2;
84+
xK2_5 = xK2_4 * xK2;
85+
86+
xLocal_1 = xK2 * 0.319381530;
87+
xLocal_2 = xK2_2 * (-0.356563782);
88+
xLocal_3 = xK2_3 * 1.781477937;
89+
xLocal_2 = xLocal_2 + xLocal_3;
90+
xLocal_3 = xK2_4 * (-1.821255978);
91+
xLocal_2 = xLocal_2 + xLocal_3;
92+
xLocal_3 = xK2_5 * 1.330274429;
93+
xLocal_2 = xLocal_2 + xLocal_3;
94+
95+
xLocal_1 = xLocal_2 + xLocal_1;
96+
xLocal = xLocal_1 * xNPrimeofX;
97+
xLocal = 1.0 - xLocal;
98+
99+
OutputX = xLocal;
100+
101+
if (sign) {
102+
OutputX = 1.0 - OutputX;
103+
}
104+
105+
return OutputX;
106+
}
107+
108+
109+
110+
inline FPTYPE BlkSchlsEqEuroNoDiv(
111+
FPTYPE sptprice, FPTYPE strike, FPTYPE rate,
112+
FPTYPE volatility, FPTYPE time, int otype, float timet) {
113+
114+
FPTYPE OptionPrice;
115+
116+
// local private working variables for the calculation
117+
//FPTYPE xStockPrice; // These two variables are not used
118+
//FPTYPE xStrikePrice;
119+
FPTYPE xRiskFreeRate;
120+
FPTYPE xVolatility;
121+
FPTYPE xTime;
122+
FPTYPE xSqrtTime;
123+
124+
FPTYPE logValues;
125+
FPTYPE xLogTerm;
126+
FPTYPE xD1;
127+
FPTYPE xD2;
128+
FPTYPE xPowerTerm;
129+
FPTYPE xDen;
130+
FPTYPE d1;
131+
FPTYPE d2;
132+
FPTYPE FutureValueX;
133+
FPTYPE NofXd1;
134+
FPTYPE NofXd2;
135+
FPTYPE NegNofXd1;
136+
FPTYPE NegNofXd2;
137+
138+
//xStockPrice = sptprice;
139+
//xStrikePrice = strike;
140+
xRiskFreeRate = rate;
141+
xVolatility = volatility;
142+
143+
xTime = time;
144+
xSqrtTime = std::sqrt(xTime);
145+
146+
logValues = std::log(sptprice/strike);
147+
148+
xLogTerm = logValues;
149+
150+
xPowerTerm = xVolatility * xVolatility;
151+
xPowerTerm = xPowerTerm * 0.5;
152+
153+
xD1 = xRiskFreeRate + xPowerTerm;
154+
xD1 = xD1 * xTime;
155+
xD1 = xD1 + xLogTerm;
156+
157+
xDen = xVolatility * xSqrtTime;
158+
xD1 = xD1 / xDen;
159+
xD2 = xD1 - xDen;
160+
161+
d1 = xD1;
162+
d2 = xD2;
163+
164+
NofXd1 = CNDF( d1 );
165+
NofXd2 = CNDF( d2 );
166+
167+
FutureValueX = strike * ( std::exp( -(rate)*(time) ) );
168+
if (otype == 0) {
169+
OptionPrice = (sptprice * NofXd1) - (FutureValueX * NofXd2);
170+
}
171+
else {
172+
NegNofXd1 = (1.0 - NofXd1);
173+
NegNofXd2 = (1.0 - NofXd2);
174+
OptionPrice = (FutureValueX * NegNofXd2) - (sptprice * NegNofXd1);
175+
}
176+
177+
return OptionPrice;
178+
}
179+
180+
181+
inline void check_error(unsigned i, FPTYPE price) {
182+
FPTYPE priceDelta = data[i].DGrefval - price;
183+
if(std::fabs(priceDelta) >= 1e-4 ){
184+
printf("Error on %d. Computed=%.5f, Ref=%.5f, Delta=%.5f\n",
185+
i, price, data[i].DGrefval, priceDelta);
186+
numError ++;
187+
}
188+
}
189+
190+
191+
// Sequential version
192+
inline void bs_seq(FPTYPE *seq_prices) {
193+
194+
int i, j;
195+
FPTYPE price;
196+
197+
for (j=0; j<NUM_RUNS; j++) {
198+
for (i=0; i<numOptions; i++) {
199+
/* Calling main function to calculate option value based on
200+
* Black & Scholes's equation.
201+
*/
202+
price = BlkSchlsEqEuroNoDiv( sptprice[i], strike[i],
203+
rate[i], volatility[i], otime[i],
204+
otype[i], 0);
205+
seq_prices[i] = price;
206+
207+
#ifdef ERR_CHK
208+
check_error(i, seq_prices[i]);
209+
#endif
210+
}
211+
}
212+
}
213+
214+
215+
// Write prices to output file
216+
inline void dump(const std::string& output_file) {
217+
std::ofstream ofs(output_file);
218+
219+
if(!ofs) {
220+
std::cerr << "ERROR: Unable to open file " << output_file << std::endl;
221+
return ;
222+
}
223+
224+
ofs << numOptions << '\n';
225+
226+
for(auto i=0; i<numOptions; i++) {
227+
ofs << prices[i] << '\n';
228+
}
229+
}
230+
231+
232+
233+
234+
// Read input option data from file
235+
inline bool parse_options(const std::string& option_file) {
236+
FILE* file = fopen(option_file.data(), "r");
237+
if(file == NULL) {
238+
std::cerr << "ERROR: Unable to open file " << option_file << std::endl;
239+
return false;
240+
}
241+
242+
if(fscanf(file, "%i", &numOptions) != 1) {
243+
std::cerr << "ERROR: Unable to read from file " << option_file << std::endl;
244+
fclose(file);
245+
return false;
246+
}
247+
248+
// Allocate spaces for the option data
249+
data = static_cast<OptionData*>(malloc(numOptions*sizeof(OptionData)));
250+
prices = static_cast<FPTYPE*>(malloc(numOptions*sizeof(FPTYPE)));
251+
252+
for (int i = 0; i < numOptions; ++ i) {
253+
int num = fscanf(file, "%f %f %f %f %f %f %c %f %f",
254+
&data[i].s, &data[i].strike, &data[i].r,
255+
&data[i].divq, &data[i].v, &data[i].t,
256+
&data[i].OptionType, &data[i].divs, &data[i].DGrefval);
257+
if(num != 9) {
258+
std::cerr << "ERROR: Unable to read from file " << option_file << std::endl;
259+
fclose(file);
260+
return false;
261+
}
262+
}
263+
264+
const int PAD {256};
265+
const int LINESIZE {64};
266+
267+
BUFFER = static_cast<FPTYPE *>(malloc(5 * numOptions * sizeof(FPTYPE) + PAD));
268+
sptprice = reinterpret_cast<FPTYPE *>(((unsigned long long)BUFFER + PAD) & ~(LINESIZE - 1));
269+
strike = sptprice + numOptions;
270+
rate = strike + numOptions;
271+
volatility = rate + numOptions;
272+
otime = volatility + numOptions;
273+
274+
BUFFER2 = static_cast<int *>(malloc(numOptions * sizeof(FPTYPE) + PAD));
275+
//otype = (int *) (((unsigned long long)BUFFER2 + PAD) & ~(LINESIZE - 1));
276+
otype = reinterpret_cast<int *>(((unsigned long long)BUFFER2 + PAD) & ~(LINESIZE - 1));
277+
278+
for(auto i=0; i<numOptions; i++) {
279+
otype[i] = (data[i].OptionType == 'P') ? 1 : 0;
280+
sptprice[i] = data[i].s;
281+
strike[i] = data[i].strike;
282+
rate[i] = data[i].r;
283+
volatility[i] = data[i].v;
284+
otime[i] = data[i].t;
285+
}
286+
287+
fclose(file);
288+
return true;
289+
//std::cout << "Size of data: " << numOptions * (sizeof(OptionData) + sizeof(int)) << std::endl;
290+
}
291+
292+
std::chrono::microseconds measure_time_taskflow(unsigned);
293+
std::chrono::microseconds measure_time_tbb(unsigned);
294+
std::chrono::microseconds measure_time_omp(unsigned);

0 commit comments

Comments
 (0)