为什么 Matlab 比 C++ 快 11 倍
Why is Matlab 11 times faster than C++
我正在比较 Matlab 和 C++ 之间香草看涨期权的蒙特卡罗定价算法的速度。这与为什么 MATLAB 在矩阵乘法中如此之快不同?由于加速不是由于矩阵乘法(只有一个快速完成的点积),而是由于其高效的高斯随机数生成器。
在 Matlab 中,代码已被矢量化,代码如下所示
function [ value ] = OptionMCValue( yearsToExpiry, spot, strike, riskFreeRate, dividendYield, volatility, numPaths )
sd = volatility*sqrt(yearsToExpiry);
sAdjusted = spot * exp( (riskFreeRate - dividendYield - 0.5*volatility*volatility) * yearsToExpiry);
g = randn(1,numPaths);
sT = sAdjusted * exp( g * sd );
values = max(sT-strike,0);`
value = mean(values);
value = value * exp(-riskFreeRate * yearsToExpiry);
end
如果我使用 1000 万条路径运行它,如下所示
strike = 100.0;
yearsToExpiry = 2.16563;
spot = 100.0;
volatility = 0.20;
dividendYield = 0.03;
riskFreeRate = 0.05;
oneMillion = 1000000;
numPaths = 10*oneMillion;
tic
value = OptionMCValue( yearsToExpiry, spot, strike, riskFreeRate, dividendYield, volatility, numPaths );
toc
我得到
Elapsed time is 0.359304 seconds.
12.8311
现在我在VS2013的C++中做同样的事情
我的代码在 OptionMC 类中,如下所示
double OptionMC::value(double yearsToExpiry,
double spot,
double riskFreeRate,
double dividendYield,
double volatility,
unsigned long numPaths )
{
double sd = volatility*sqrt(yearsToExpiry);
double sAdjusted = spot * exp( (riskFreeRate - dividendYield - 0.5*volatility*volatility) * yearsToExpiry);
double value = 0.0;
double g, sT;
for (unsigned long i = 0; i < numPaths; i++)
{
g = GaussianRVByBoxMuller();
sT = sAdjusted * exp(g * sd);
value += Max(sT - m_strike, 0.0);
}
value = value * exp(-riskFreeRate * yearsToExpiry);
value /= (double) numPaths;
return value;
}
BM代码如下
double GaussianRVByBoxMuller()
{
double result;
double x; double y;;
double w;
do
{
x = 2.0*rand() / static_cast<double>(RAND_MAX)-1;
y = 2.0*rand() / static_cast<double>(RAND_MAX)-1;
w = x*x + y*y;
} while (w >= 1.0);
w = sqrt(-2.0 * log(w) / w);
result = x*w;
return result;
}
我已将"优化"选项设置为"在Visual Studio中优化速度"。
对于 10m 路径,需要 4.124 秒。
这比 Matlab 慢 11 倍。
谁能解释一下区别?
编辑:在进一步测试时,减速似乎是对GaussianRVByBoxMuller的调用。Matlab似乎有一个非常有效的实现 - Ziggurat方法。请注意,BM 在这里是次优的,因为它生成 2 个 RV,而我只使用 1。仅解决此问题将提供 2 倍的加速。
就目前而言,您正在生成单线程代码。猜测,Matlab正在使用多线程代码。这允许它以大约 N 的倍数运行得更快,其中 N = CPU 中的内核数。
不过,这个故事还有更多的东西。出现的另一个问题是您正在使用 rand()
,它使用隐藏的全局状态。因此,如果你对代码进行简单的重写以使其成为多线程,那么rand()
内部状态的冲突很可能会阻止你获得太多的速度改进(并且可能很容易运行得更慢 - 也许会慢一点)。
为了解决这个问题,您可以考虑(例如)使用 C++11 中添加的新随机数生成(以及可能的分布)类。有了这些,您可以为每个线程创建一个单独的随机数生成器实例,防止其内部状态发生冲突。
我稍微重写了一下您的代码以使用这些代码,并调用该函数来获得以下内容:
double m_strike = 100.0;
class generator {
std::normal_distribution<double> dis;
std::mt19937_64 gen;
public:
generator(double lower = 0.0, double upper = 1.0)
: gen(std::random_device()()), dis(lower, upper) {}
double operator()() {
return dis(gen);
}
};
double value(double yearsToExpiry,
double spot,
double riskFreeRate,
double dividendYield,
double volatility,
unsigned long numPaths)
{
double sd = volatility*sqrt(yearsToExpiry);
double sAdjusted = spot * exp((riskFreeRate - dividendYield - 0.5*volatility*volatility) * yearsToExpiry);
double value = 0.0;
double g, sT;
generator gen;
// run iterations in parallel, with a private random number generator for each thread:
#pragma omp parallel for reduction(+:value) private(gen)
for (long i = 0; i < numPaths; i++)
{
g = gen(); // GaussianRVByBoxMuller();
sT = sAdjusted * exp(g * sd);
value += std::max(sT - m_strike, 0.0);
}
value = value * exp(-riskFreeRate * yearsToExpiry);
value /= (double)numPaths;
return value;
}
int main() {
std::cout << "value: " << value(2.16563, 100.0, 0.05, 0.03, 0.2, 10'000'000) << "n";
}
我使用以下命令行使用 VC++ 2015 编译了它:
cl -openmp -Qpar -arch:AVX -O2b2 -GL test.cpp
在 AMD A8-7600 上,这在 ~.31 秒内运行。
在英特尔 i7 处理器上,这在 ~.16 秒内运行。
当然,如果你有一个拥有更多内核的CPU,那么你很有可能运行得更快。
就目前而言,我的代码需要VC++ 2015而不是2013,但我怀疑它对性能的影响有多大。这主要只是方便,比如使用 10'000'000
而不是 10000000
(但我不会在这台机器上安装 2013 的副本只是为了弄清楚我需要更改什么以适应它)。
另请注意,在最近的英特尔处理器上,您可能会(或可能不会)通过将arch:AVX
更改为arch:AVX2
来获得一些改进。
快速检查单线程代码表明,您的 Box-Muller 分发代码可能比标准库的正常分发代码快一点,因此切换到线程友好版本可能会获得更快的速度(优化版本也应该大约翻倍)。
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 有可能在Armadillo中复制MATLAB circshift方法吗
- 使用 MATLAB 编码器生成C++代码:编译错误"undefined reference to `rgb2gray_tbb_real64'"
- MATLAB to C++: csvread() not supported by MATLAB Coder
- 加载由 MATLAB Coder 生成的带有函数的 DLL,该函数调用外部函数
- MATLAB:跟踪imufilter对象中的状态变化
- 如何将 c++ 中的客户端 TCP 的替身列表发送到 Matlab 中的 TCP 服务器?
- 将三角函数的正确值与matlab进行比较
- 连接 MATLAB 和 Visual Studios 的问题
- 如何将C++连接到 Matlab
- 如何发送 Mat H=findHomography 返回 Matlab
- 如何在C++中编写 MATLAB fix(X) 函数?
- 提供变量作为 MATLAB 系统命令的输入参数,以便C++可执行文件
- 有没有更好的方法可以使用特征/C++实现 matlab 的逻辑索引?
- 当我运行MEX文件时,MATLAB崩溃
- 从 MATLAB 到 C++:相当于带有选项 'remove' 的 bwmorph
- MATLAB的imfinfo in openCV
- 将C++中的多个参数传递给MatLab共享库函数
- 将 matlab 数组(MAT-文件)转换为C++数组
- 数组数据以错误的方式遍历 Python/Matlab