Expected Runtimes
When running under the simclock executor, all callbacks (subscribers or timers) have a simulated execution time of one nanosecond. That is, no matter how much time they take to execute in the real world, in simulation, we pretend they completed in one nanosecond.
It can be very useful, however, to simulate how long the callbacks would take to execute in the real world. For example, if you have a perception callback that takes 200ms to execute, but is listening for messages that arrive every 100ms, this would allow you to properly model what happens when every other message is dropped.
Additionally, setting expected runtimes allows the simclock executor to find additional opportunities for parallelism. The executor will always ensure determinism in how callbacks are fired, but if it knows that a callback takes “200 simulated milliseconds” to run, then it can run other callbacks simultaneously.
All normal rules for callback execution apply; you can not run two or more callbacks from the same stage simultaneously.
Automatic Generation
The easiest way to generate expected runtimes for your pipeline is to run with the --collect-runtimes
command line option:
./my_sample_pipeline --collect-runtimes ./my_runtimes.bin
This will run the pipeline as usual, but will monitor how long every callback takes to execute. It
will automatically make a guess as to the ’expected’ runtime for the callback, and they will be stored
in the path given (in this case, ./my_runtimes.bin
).
The runtimes will of course be heavily dependent on the hardware used to run the pipeline; consider running it on representative hardware. Additionally, even then, they will only represent runtimes of your modules in the circumstances that the pipeline was run under (in other words, it is essentially coming up with a weighted average over the pipeline run, and this may or may not be representative of all situations your pipeline runs in).
This command line option is available if you are using the main_offboard
module; otherwise, you can
gain it by manually adding the CollectRuntimesStage
to your pipeline.
Log Generation
You can also process a data log to generate runtimes based on the information that was logged. This requires you to log the pipeline metrics, and then to run:
./build/generate_runtimes /path/to/log /path/to/output.bin
Otherwise, this is identical to the automatic generation step.
Manual Setting
It’s possible to either just set the expected runtimes manually, or to override whatever the runtimes
files are providing. In either case, you can do so by setting the expected_runtime
field of your
periodic timer configuration or subscriber configuration.
This sets the subscriber for /my/channel_name
to 100ms:
ark::pipeline::SubscriberConfiguration<MyMessage> config;
config.channel_name = "/my/channel_name";
config.maximum_queue_depth = 10;
config.callback = [this](const auto &msg) { this->handle_messaage(msg); };
config.expected_runtime = std::chrono::milliseconds{100};
interface.add_subscriber(config);
And this does the same for a periodic timer:
ark::pipeline::PeriodicTimerConfiguration config;
config.name = "MyTimerName";
config.callback = [this](const auto &now) { this->handle_timer(now); };
config.rate = std::chrono::seconds{1};
config.expected_runtime = std::chrono::milliseconds{100};
interface.add_timer(config);
Note that setting expected runtimes for event-based timers is currently not supported; it is assumed that they are “driving” a simulation pipeline (such as a log player), and need to execute, for practical purposes, instantaneously.
Runtime File Usage
You can make use of the generated runtimes by adding a section to your simclock executor configuration:
package:
configs:
Pipeline:
metadata:
simclock_config:
expected_runtime_config_path: "./my_runtimes.bin"
You can also use runtime files in a “one off” manner by using the --input-runtimes-path
command line
argument. The config file and command line arguments are equivalent.
This will cause the simclock executor to automatically apply all of the runtimes stored in that file to all of the subscriber/timer callbacks currently in your pipeline.
Any subscriber/timer not found in the file will be left with their default values.
Notice
Note that applying new runtimes can dramatically affect the performance of a pipeline; messages that were not dropped before may be dropped now (and vice-versa)!Dynamic Runtimes
You can compute the expected runtime dynamically at runtime by setting the dynamic_expected_runtime
function in the subscriber config. This can be useful for modeling functions whose runtime will
change due to properties of the input data. It can also be used for adding noise to the runtimes.
If the function is set the simclock runner will call it before every callback exectuion with the configured runtime and the next message and returns the new expected runtime.