Чтобы рассчитать IRR, вам нужно решить полиномиальное уравнение.Это должно быть сделано для каждого вектора денежного потока отдельно.Следовательно, применение irr
к многомерной матрице не улучшает время выполнения.Я подозреваю, что Matlab все еще использует цикл для внутреннего использования.
Возможно, вам удастся набрать некоторую скорость, играя с опциями оптимизации fsolve
, но значительное улучшение маловероятно.Предположительно, разработчики Matlab уже выбрали достаточно хороший подход.
Таким образом, ваша единственная альтернатива - распараллеливание.Если у вас есть доступ к серверу или ваш ноутбук / настольный компьютер имеет несколько процессоров, вы можете сократить время выполнения, запустив функции irr
параллельно.(Вам также, вероятно, понадобится Parallel Computing Toolbox.)
Я немного изменил ваш пример, чтобы использовать случайные значения денежных потоков, чтобы упростить проверку.Однако я уменьшил количество сценариев и временных точек, чтобы функция 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
Когда я запускал этот код на сервере с 4 ЦП и 16 ГБ памяти, яполучил следующие результаты.
19.9357 : single thread, loop
20.4318 : single thread, vectorized
...
5.6346 : parallelized outer loop
...
12.4640 : parallelized inner loop
Как видите, векторизованная версия irr
не дает никаких преимуществ по сравнению с циклом.В этом случае это немного медленнее.В других моих тестах это иногда было немного быстрее.
Однако вы можете значительно сократить время выполнения, распараллеливая внешний цикл с функцией parfor
.Это лучше, чем распараллеливание самого внутреннего цикла, поскольку каждый пакет имеет определенные накладные расходы на выполнение.Таким образом, небольшое количество больших пакетов имеет меньшие накладные расходы, чем большое количество меньших пакетов.
Вот как работает распараллеливание.Сначала вы создаете пул локальных рабочих потоков с помощью команды ниже.Убедитесь, что вы не превышаете количество процессоров, которые у вас есть.parpool
может ждать до тех пор, пока все локальные работники не будут созданы, и он может создавать только локального работника, если доступен ЦП.
poolObj = parpool('local', noThreads);
Создание пула может занять несколько секунд.Именно поэтому я переместил это вне функции, которую я рассчитал.Для больших заданий время создания пула незначительно по сравнению с общим временем выполнения.
Здесь я сохраняю объект пула в переменной и впоследствии удаляю его.Тем не менее, это необязательно.По умолчанию пул уничтожается через 30 минут бездействия или после завершения работы Matlab.
После этого вы заменяете цикл for
, который хотите распараллелить, на вызов parfor
, т.е. for scenarios1 = 1:noScenarios
становится parfor scenarios1 = 1:noScenarios
.По умолчанию parfor
будет использовать всех доступных работников, но вы также можете указать максимальное количество работников, которое ему разрешено использовать с parfor (scenarios1 = 1:noScenarios, maxWorkers)
.Обратите внимание, однако, что порядок выполнения не гарантируется, то есть пятая итерация может быть выполнена до третьей итерации.