open-simulation-platform / libcosim

OSP C++ co-simulation library
https://open-simulation-platform.github.io/libcosim
Mozilla Public License 2.0
54 stars 10 forks source link

User story: choose the step scheduling type (performance purposes) #743

Open xmirabel opened 1 year ago

xmirabel commented 1 year ago

NEED

It should be possible to choose the step scheduling type, among at least full parallelism (legacy code), and cascading parallelism. The current chosen mode in the OSP is full parallel. This mode matchs with hard real time systems but can be improved in the case of simulation systems. A simple way to improve efficiency is to cascade simulators following their output/input dependencies in order to earn in number of execution steps.

For instance, suppose that, in OspSystemStructure.xml, we defined:

If we define the following order relation: A < B if:

We can deduce from these 2 main informations a STEP PARTIAL ORDER (that is why parallelism will be possible):

Assume that we define a task TSi that:

flowchart TB
    A["Set inputs from OSP to Si"] --> B["Si.do_step()"] --> C["Get outputs from Si to OSP"]

Then for 1 step, we compute everything using cascade and parallelism, i.e. see this UML like activity diagram with forks:

  stateDiagram-v2
    state fork_state1 <<fork>>

      [*] --> fork_state1

      fork_state1 --> TS4
     fork_state1 --> TS1
     fork_state1 --> TS2

      state join_state1 <<join>>
      state join_state2 <<join>>
      TS1--> join_state2
      TS2--> join_state2
      join_state2--> TS3

      TS3-->  join_state1
      TS4-->  join_state1

      join_state1 --> TS5
      TS5--> [*]

Else in full parallelism you need 2 steps so that computation results of S1 and S2 reach S5.

This can be easily achieved using for instance https://taskflow.github.io/taskflow/index.html

... do_step(...){

  tf::Executor executor;
  tf::Taskflow taskflow;

  auto [TS1, TS2, TS3, TS4, TS5] = taskflow.emplace(  // create tasks
    [] () { std::cout << "TS1\n"; set inputs; S1.do_step(); get outputs; },
    [] () { std::cout << "TS2\n"; set inputs; S2.do_step(); get outputs; },
    [] () { std::cout << "TS3\n"; set inputs; S3.do_step(); get outputs; },
    [] () { std::cout << "TS4\n"; set inputs; S4.do_step(); get outputs; },
    [] () { std::cout << "TS5\n"; set inputs; S5.do_step(); get outputs; } 
  );                                  

  // S1 < S3 and S2 < S3  
  TS3.succeed(TS1, TS2);  // TS3 runs after TS1 and TS2

  // S3 < S5 and S4 < S5
  TS5.succeed(TS1, TS4);  // TS5 runs after TS3 and TS4

  executor.run(taskflow).wait(); 

  return ...;
}

DESCRIPTION

As a OSP user
I want to be able to choose the step scheduling type, among at least: - full parallelism (legacy code), and - cascading parallelism, deduced from the OSP OspSystemStructure.xml configuration file.
So that at each step of execution, the applied algorithm is the selected one.

ACCEPTANCE TESTS

Given that an OSP configuration file describe the above example with an increment of 1 from input to output
When the user selects full parallelism scheduling type
Then The results are in consequence (to be calculated by the tester before reading them)
Given that an OSP configuration file describe the above example with an increment of 1 from input to output
When the user selects cascading parallelism scheduling type
Then The results are in consequence (to be calculated by the tester before reading them: different from the above test at each step)

EXAMPLE OF SIGNATURES

// Step scheduling type.
typedef enum
{
    COSIM_STEP_SCHEDULING_TYPE_FULL_PARALLELISM,
    COSIM_STEP_SCHEDULING_TYPE_CASCADING_PARALLELISM,
} cosim_execution_step_scheduling_type;

/**
 *  Get the execution step scheduling type.
 *
 *  \param [in] execution
 *      The concerned execution.
 *
 *  \returns
 *      The execution step scheduling type.
 */
cosim_execution_step_scheduling_type cosim_execution_get_step_scheduling_type(cosim_execution* execution);

/**
 *  Set the execution default continuity of service level.
 *
 *  \param [in] execution
 *      The concerned execution.
 *  \param [in] level
 *      The execution default continuity of service level.
 */
void cosim_execution_set_step_scheduling_type(cosim_execution* execution, cosim_execution_step_scheduling_type type);