Len*_*naH 7 performance matlab for-loop multidimensional-array irr
我正在尝试在Matlab 2019a中计算具有多个维度的内部收益率。我的公式从理论上讲是有效的(目前暂时忽略“多重收益率”警告),但是问题是,对于更大的矩阵(即noScenarios> 5或更大),代码变得非常慢。有哪些编程替代方法?我也尝试过解决,但我认为也不快。
请注意,由于我没有数学能力,因此像“布伦特方法”这样的简单关键字对我来说是不够的(例如,“计算内部收益率内部收益率的最有效方法是什么?”)。我将不得不知道a)如何在Matlab中实现它,以及b)如果它确实是白痴证明,那么什么都不会出错?谢谢!
clc
clear
close all
noScenarios = 50;
CF = ones(300,noScenarios,noScenarios,noScenarios);
CF = [repmat(-300, 1,noScenarios,noScenarios,noScenarios); CF];
for scenarios1 = 1:noScenarios
for scenarios2 = 1:noScenarios
for scenarios3 = 1:noScenarios
IRR3dimensional(scenarios1,scenarios2,scenarios3) = irr(CF(:,scenarios1,scenarios2,scenarios3));
end
end
end
Run Code Online (Sandbox Code Playgroud)
要计算 IRR,您需要求解多项式方程。必须对每个现金流量向量分别进行此操作。因此,应用于irr多维矩阵并不会缩短执行时间。我怀疑Matlab内部仍然使用循环。
您也许可以通过使用优化选项来获得一些速度,fsolve但大幅提高的可能性很小。想必,Matlab 开发人员已经选择了一种足够好的方法。
因此,唯一的选择就是并行化。如果您有权访问服务器或您的笔记本电脑/台式机具有多个 CPU,则可以通过irr并行运行函数来减少运行时间。(您可能还需要一个并行计算工具箱。)
我稍微修改了您的示例以使用随机现金流值,以便更容易检查。但是,我减少了场景和时间点的数量,以便该timeit函数可以在合理的时间内运行多个模拟。(另外,请记住,执行时间似乎与时间点的数量呈指数关系。)
t = 150;
noScenarios = 10;
noThreads = 4;
CF = rand(t,noScenarios,noScenarios,noScenarios);
CF = [-rand(1,noScenarios,noScenarios,noScenarios); CF];
h1 = @() f1(CF, noScenarios);
fprintf("%0.4f : single thread, loop\n", timeit(h1))
h2 = @() f2(CF, noScenarios);
fprintf("%0.4f : single thread, vectorized\n", timeit(h2))
poolObj = parpool('local', noThreads);
h3 = @() f3(CF, noScenarios);
fprintf("%0.4f : parallelized outer loop\n", timeit(h3))
delete(poolObj);
poolObj = parpool('local', noThreads);
h4 = @() f4(CF, noScenarios);
fprintf("%0.4f : parallelized inner loop\n", timeit(h4))
delete(poolObj);
function res = f1(CF, noScenarios)
res = zeros(noScenarios, noScenarios, noScenarios);
for scenarios1 = 1:noScenarios
for scenarios2 = 1:noScenarios
for scenarios3 = 1:noScenarios
res(scenarios1,scenarios2,scenarios3) = irr(CF(:,scenarios1,scenarios2,scenarios3));
end
end
end
end
function res = f2(CF, noScenarios)
res = reshape(irr(CF), noScenarios, noScenarios, noScenarios);
end
function res = f3(CF, noScenarios)
res = zeros(noScenarios, noScenarios, noScenarios);
parfor scenarios1 = 1:noScenarios
for scenarios2 = 1:noScenarios
for scenarios3 = 1:noScenarios
res(scenarios1,scenarios2,scenarios3) = irr(CF(:,scenarios1,scenarios2,scenarios3));
end
end
end
end
function res = f4(CF, noScenarios)
res = zeros(noScenarios, noScenarios, noScenarios);
for scenarios1 = 1:noScenarios
for scenarios2 = 1:noScenarios
parfor scenarios3 = 1:noScenarios
res(scenarios1,scenarios2,scenarios3) = irr(CF(:,scenarios1,scenarios2,scenarios3));
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
当我在具有 4 个 CPU 和 16 Gb 内存的服务器上运行此代码时,我得到了以下结果。
19.9357 : single thread, loop
20.4318 : single thread, vectorized
...
5.6346 : parallelized outer loop
...
12.4640 : parallelized inner loop
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,向量化版本并irr没有比循环提供任何优势。在这种情况下,速度会稍微慢一些。在我的其他测试中,它偶尔会快一点。
但是,您可以通过将外循环与函数并行化来显着减少运行时间parfor。它比并行化最内层循环更好,因为每个批次都有一定的执行开销。因此,少量较大批次的开销比较大量较小批次的开销要低。
以下是并行化的工作原理。首先,使用以下命令创建本地工作线程池。确保不超过您拥有的 CPU 数量。parpool可以无限期地等待,直到创建所有本地工作线程,并且只有在 CPU 可用的情况下才能创建本地工作线程。
poolObj = parpool('local', noThreads);
Run Code Online (Sandbox Code Playgroud)
池创建可能需要几秒钟的时间。这就是为什么我将它移到我计时的函数之外。对于较大的作业,池创建时间与总执行时间相比微不足道。
在这里,我将池对象保存在变量中,然后将其删除。然而,它是可选的。默认情况下,30 分钟不活动或 Matlab 终止后,该池将被销毁。
之后,您for将要并行化的循环替换为调用parfor,即for scenarios1 = 1:noScenarios变为parfor scenarios1 = 1:noScenarios。默认情况下,parfor将使用所有可用的工作人员,但您也可以指定允许使用的最大工作人员数量parfor (scenarios1 = 1:noScenarios, maxWorkers)。但请注意,不保证执行顺序,即第五次迭代可能会在第三次迭代之前执行。
| 归档时间: |
|
| 查看次数: |
148 次 |
| 最近记录: |