All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opengamma.strata.calc.runner.DefaultCalculationTaskRunner Maven / Gradle / Ivy

/*
 * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.calc.runner;

import static com.opengamma.strata.collect.Guavate.toImmutableList;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.function.Consumer;
import java.util.function.Supplier;

import com.opengamma.strata.basics.CalculationTarget;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.calc.Column;
import com.opengamma.strata.calc.Results;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.Messages;
import com.opengamma.strata.collect.result.Result;
import com.opengamma.strata.data.MarketData;
import com.opengamma.strata.data.scenario.ScenarioArray;
import com.opengamma.strata.data.scenario.ScenarioMarketData;

/**
 * The default calculation task runner.
 * 

* This uses a single instance of {@link ExecutorService}. */ final class DefaultCalculationTaskRunner implements CalculationTaskRunner { /** * Executes the tasks that perform the individual calculations. * This will typically be multi-threaded, but single or direct executors also work. */ private final ExecutorService executor; //------------------------------------------------------------------------- /** * Creates a standard multi-threaded calculation task runner capable of performing calculations. *

* This factory creates an executor basing the number of threads on the number of available processors. * It is recommended to use try-with-resources to manage the runner: *

   *  try (DefaultCalculationTaskRunner runner = DefaultCalculationTaskRunner.ofMultiThreaded()) {
   *    // use the runner
   *  }
   * 
* * @return the calculation task runner */ static DefaultCalculationTaskRunner ofMultiThreaded() { return new DefaultCalculationTaskRunner(createExecutor(Runtime.getRuntime().availableProcessors())); } /** * Creates a calculation task runner capable of performing calculations, specifying the executor. *

* It is the callers responsibility to manage the life-cycle of the executor. * * @param executor the executor to use * @return the calculation task runner */ static DefaultCalculationTaskRunner of(ExecutorService executor) { return new DefaultCalculationTaskRunner(executor); } // create an executor with daemon threads private static ExecutorService createExecutor(int threads) { int effectiveThreads = (threads <= 0 ? Runtime.getRuntime().availableProcessors() : threads); ThreadFactory defaultFactory = Executors.defaultThreadFactory(); ThreadFactory threadFactory = r -> { Thread t = defaultFactory.newThread(r); t.setName("CalculationTaskRunner-" + t.getName()); t.setDaemon(true); return t; }; return Executors.newFixedThreadPool(effectiveThreads, threadFactory); } //------------------------------------------------------------------------- /** * Creates an instance specifying the executor to use. * * @param executor the executor that is used to perform the calculations */ private DefaultCalculationTaskRunner(ExecutorService executor) { this.executor = ArgChecker.notNull(executor, "executor"); } //------------------------------------------------------------------------- @Override public Results calculate( CalculationTasks tasks, MarketData marketData, ReferenceData refData) { // perform the calculations ScenarioMarketData md = ScenarioMarketData.of(1, marketData); Results results = calculateMultiScenario(tasks, md, refData); // unwrap the results // since there is only one scenario it is not desirable to return scenario result containers List> mappedResults = results.getCells().stream() .map(r -> unwrapScenarioResult(r)) .collect(toImmutableList()); return Results.of(results.getColumns(), mappedResults); } //------------------------------------------------------------------------- /** * Unwraps the result from an instance of {@link ScenarioArray} containing a single result. *

* When the user executes a single scenario the functions are invoked with a set of scenario market data * of size 1. This means the functions are simpler and always deal with scenarios. But if the user has * asked for a single set of results they don't want to see a collection of size 1 so the scenario results * need to be unwrapped. *

* If {@code result} is a failure or doesn't contain a {@code ScenarioArray} it is returned. *

* If this method is called with a {@code ScenarioArray} containing more than one value it throws an exception. */ private static Result unwrapScenarioResult(Result result) { if (result.isFailure()) { return result; } Object value = result.getValue(); if (!(value instanceof ScenarioArray)) { return result; } ScenarioArray scenarioResult = (ScenarioArray) value; if (scenarioResult.getScenarioCount() != 1) { throw new IllegalArgumentException(Messages.format( "Expected one result but found {} in {}", scenarioResult.getScenarioCount(), scenarioResult)); } return Result.success(scenarioResult.get(0)); } @Override public void calculateAsync( CalculationTasks tasks, MarketData marketData, ReferenceData refData, CalculationListener listener) { // the listener is decorated to unwrap ScenarioArrays containing a single result ScenarioMarketData md = ScenarioMarketData.of(1, marketData); UnwrappingListener unwrappingListener = new UnwrappingListener(listener); calculateMultiScenarioAsync(tasks, md, refData, unwrappingListener); } //------------------------------------------------------------------------- @Override public Results calculateMultiScenario( CalculationTasks tasks, ScenarioMarketData marketData, ReferenceData refData) { ResultsListener listener = new ResultsListener(); calculateMultiScenarioAsync(tasks, marketData, refData, listener); return listener.result(); } @Override public void calculateMultiScenarioAsync( CalculationTasks tasks, ScenarioMarketData marketData, ReferenceData refData, CalculationListener listener) { List taskList = tasks.getTasks(); // the listener is invoked via this wrapper // the wrapper ensures thread-safety for the listener // it also calls the listener with single CalculationResult cells, not CalculationResults Consumer consumer = new ListenerWrapper(listener, taskList.size(), tasks.getTargets(), tasks.getColumns()); // run each task using the executor taskList.forEach(task -> runTask(task, marketData, refData, consumer)); } // submits a task to the executor to be run private void runTask( CalculationTask task, ScenarioMarketData marketData, ReferenceData refData, Consumer consumer) { // the task is executed, with the result passed to the consumer // the consumer wraps the listener to ensure thread-safety Supplier taskExecutor = () -> task.execute(marketData, refData); CompletableFuture.supplyAsync(taskExecutor, executor).thenAccept(consumer); } //------------------------------------------------------------------------- @Override public void close() { executor.shutdown(); } //------------------------------------------------------------------------- //------------------------------------------------------------------------- /** * Listener that decorates another listener and unwraps {@link ScenarioArray} instances * containing a single value before passing the value to the delegate listener. * This is used by the single scenario async method. */ private static final class UnwrappingListener implements CalculationListener { private final CalculationListener delegate; private UnwrappingListener(CalculationListener delegate) { this.delegate = delegate; } @Override public void calculationsStarted(List targets, List columns) { delegate.calculationsStarted(targets, columns); } @Override public void resultReceived(CalculationTarget target, CalculationResult calculationResult) { Result result = calculationResult.getResult(); Result unwrappedResult = unwrapScenarioResult(result); CalculationResult unwrappedCalculationResult = calculationResult.withResult(unwrappedResult); delegate.resultReceived(target, unwrappedCalculationResult); } @Override public void calculationsComplete() { delegate.calculationsComplete(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy