basic refactor
This commit is contained in:
parent
a6df3fcc41
commit
abf3496c78
21 changed files with 13752 additions and 13232 deletions
74
.vscode/settings.json
vendored
Normal file
74
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"cctype": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"csignal": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"cstring": "cpp",
|
||||||
|
"ctime": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"array": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"strstream": "cpp",
|
||||||
|
"bit": "cpp",
|
||||||
|
"*.tcc": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"chrono": "cpp",
|
||||||
|
"compare": "cpp",
|
||||||
|
"complex": "cpp",
|
||||||
|
"concepts": "cpp",
|
||||||
|
"condition_variable": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"list": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
|
"set": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"unordered_set": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"memory_resource": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"optional": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"ratio": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"string_view": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"fstream": "cpp",
|
||||||
|
"future": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iomanip": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"mutex": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"ranges": "cpp",
|
||||||
|
"shared_mutex": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"stop_token": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"thread": "cpp",
|
||||||
|
"cfenv": "cpp",
|
||||||
|
"cinttypes": "cpp",
|
||||||
|
"typeindex": "cpp",
|
||||||
|
"typeinfo": "cpp",
|
||||||
|
"variant": "cpp"
|
||||||
|
}
|
||||||
|
}
|
125
README.md
125
README.md
|
@ -1,3 +1,124 @@
|
||||||
Experiment files for the RTIS ros_rt executor. Clone with submodules on, or add the executor to the src folder.
|
# Dynamic Priority Scheduling for ROS-EDF Executor
|
||||||
|
|
||||||
Run `ros2 run casestudy casestudy_example` then run the `analysis/analysis.ipynb` notebook.
|
This repository contains experiments for the ROS-EDF executor with priority-based and deadline-based scheduling capabilities.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Clone with submodules enabled:
|
||||||
|
```
|
||||||
|
git clone --recurse-submodules https://github.com/user/ROS-Dynamic-Executor-Experiments.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure you have sourced your ROS 2 environment.
|
||||||
|
|
||||||
|
Run the example:
|
||||||
|
```
|
||||||
|
ros2 run casestudy casestudy_example
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run the analysis notebook:
|
||||||
|
```
|
||||||
|
jupyter notebook analysis/analysis.ipynb
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## Dynamically Adjusting Scheduler Approaches
|
||||||
|
|
||||||
|
The PriorityMemoryStrategy supports different scheduling policies for ROS callbacks. To extend it with dynamically adjusting scheduling that reacts to semantic events, there are several viable approaches:
|
||||||
|
|
||||||
|
### Approach A: Custom PriorityExecutableComparator
|
||||||
|
|
||||||
|
Replace the existing comparator with a custom implementation:
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- Direct control over scheduling logic
|
||||||
|
- Clean separation of concerns
|
||||||
|
- Can implement complex scheduling policies
|
||||||
|
- Doesn't require modifying deadline values
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- Requires understanding and maintaining the comparison logic
|
||||||
|
- May need to add new fields to PriorityExecutable to track dynamic priorities
|
||||||
|
- Could become complex if multiple factors affect priority
|
||||||
|
|
||||||
|
### Approach B: Dynamic Deadline Adjustment
|
||||||
|
|
||||||
|
Keep the EDF comparator but adjust deadlines based on priority logic:
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- Works within the existing EDF framework
|
||||||
|
- Conceptually simple - just manipulate deadline values
|
||||||
|
- Doesn't require changing the core sorting mechanism
|
||||||
|
- Easier to debug (you can log deadline changes)
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- Potentially confusing semantics (using deadlines to represent non-deadline priorities)
|
||||||
|
- May interfere with actual deadline-based requirements
|
||||||
|
- Could lead to instability if not carefully managed
|
||||||
|
|
||||||
|
### Approach C: Event-Driven Priority Field
|
||||||
|
|
||||||
|
Add a dynamic boost factor field to PriorityExecutable:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int dynamic_priority_boost = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
Then modify the comparator to consider this value:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if (p1->sched_type == DEADLINE) {
|
||||||
|
// First compare deadline as usual
|
||||||
|
// Then use dynamic_priority_boost as a tiebreaker
|
||||||
|
if (p1_deadline == p2_deadline) {
|
||||||
|
return p1->dynamic_priority_boost > p2->dynamic_priority_boost;
|
||||||
|
}
|
||||||
|
return p1_deadline < p2_deadline;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- Clearer semantics than manipulating deadlines
|
||||||
|
- Keeps deadline scheduling intact for real-time guarantees
|
||||||
|
- Easy to adjust at runtime
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- Requires modifying both PriorityExecutable and comparison logic
|
||||||
|
|
||||||
|
### Approach D: Priority Multiplier System
|
||||||
|
|
||||||
|
Implement a system where chains can have priority multipliers applied:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
float priority_multiplier = 1.0f;
|
||||||
|
```
|
||||||
|
|
||||||
|
And then in the comparator:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// For priority-based scheduling
|
||||||
|
int effective_p1 = p1->priority * p1->priority_multiplier;
|
||||||
|
int effective_p2 = p2->priority * p2->priority_multiplier;
|
||||||
|
return effective_p1 < effective_p2;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- Scales existing priorities rather than replacing them
|
||||||
|
- Preserves relative importance within chains
|
||||||
|
- Intuitive model for temporary priority boosts
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- May need to handle overflow/boundary cases
|
||||||
|
- Requires careful tuning of multiplier ranges
|
||||||
|
|
||||||
|
## Recommendation
|
||||||
|
|
||||||
|
Approach C (Event-Driven Priority Field) offers the best balance of:
|
||||||
|
1. Clean semantics
|
||||||
|
2. Minimal interference with existing scheduling logic
|
||||||
|
3. Clear separation between baseline priorities and dynamic adjustments
|
||||||
|
4. Straightforward implementation
|
||||||
|
|
||||||
|
This approach maintains real-time guarantees while enabling dynamic behaviors based on semantic events.
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
@ -1,95 +1,74 @@
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.8)
|
||||||
project(casestudy)
|
project(casestudy VERSION 0.1.0)
|
||||||
|
|
||||||
# Default to C99
|
# Set C++ standards
|
||||||
if(NOT CMAKE_C_STANDARD)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
endif()
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
# Default to C++14
|
|
||||||
if(NOT CMAKE_CXX_STANDARD)
|
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
# Compiler options
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# find dependencies
|
# Find dependencies
|
||||||
find_package(ament_cmake REQUIRED)
|
find_package(ament_cmake REQUIRED)
|
||||||
# uncomment the following section in order to fill in
|
find_package(rclcpp REQUIRED)
|
||||||
# further dependencies manually.
|
|
||||||
# find_package(<dependency> REQUIRED)
|
|
||||||
find_package(priority_executor REQUIRED)
|
find_package(priority_executor REQUIRED)
|
||||||
find_package(simple_timer REQUIRED)
|
find_package(simple_timer REQUIRED)
|
||||||
find_package(rclcpp REQUIRED)
|
|
||||||
find_package(jsoncpp REQUIRED)
|
find_package(jsoncpp REQUIRED)
|
||||||
find_package(casestudy_tools REQUIRED)
|
find_package(casestudy_tools REQUIRED)
|
||||||
|
|
||||||
# add_library(test_nodes src/test_nodes.cpp)
|
# Define experiment targets
|
||||||
# target_include_directories(test_nodes PUBLIC
|
set(EXPERIMENTS
|
||||||
# $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
|
||||||
# $<INSTALL_INTERFACE:include>
|
|
||||||
# )
|
|
||||||
# ament_target_dependencies(test_nodes
|
|
||||||
# rclcpp
|
|
||||||
# simple_timer
|
|
||||||
# casestudy_tools
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
list(APPEND experiments
|
|
||||||
casestudy_2023customfile_singlethread
|
casestudy_2023customfile_singlethread
|
||||||
casestudy_2024ours_latency
|
casestudy_2024ours_latency
|
||||||
casestudy_2024ours_executor2executor
|
casestudy_2024ours_executor2executor
|
||||||
casestudy_example
|
casestudy_example
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Function to create experiment targets
|
||||||
function(new_experiment experiment_name)
|
function(new_experiment experiment_name)
|
||||||
add_executable(${experiment_name} src/${experiment_name}.cpp)
|
add_executable(${experiment_name} src/${experiment_name}.cpp)
|
||||||
target_include_directories(${experiment_name} PUBLIC
|
target_include_directories(${experiment_name} PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:include>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add dependencies with ament
|
||||||
ament_target_dependencies(${experiment_name}
|
ament_target_dependencies(${experiment_name}
|
||||||
rclcpp
|
|
||||||
priority_executor
|
|
||||||
simple_timer
|
simple_timer
|
||||||
casestudy_tools
|
casestudy_tools
|
||||||
|
rclcpp
|
||||||
|
priority_executor
|
||||||
)
|
)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
foreach(experiment ${experiments})
|
# Create all experiment targets
|
||||||
|
foreach(experiment ${EXPERIMENTS})
|
||||||
new_experiment(${experiment})
|
new_experiment(${experiment})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
target_link_libraries(casestudy_2023customfile_singlethread
|
# Special handling for targets with additional dependencies
|
||||||
jsoncpp
|
target_link_libraries(casestudy_2023customfile_singlethread jsoncpp)
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
install(
|
||||||
|
TARGETS ${EXPERIMENTS}
|
||||||
|
RUNTIME DESTINATION lib/${PROJECT_NAME}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
install(TARGETS
|
|
||||||
${experiments}
|
|
||||||
DESTINATION lib/${PROJECT_NAME}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
install(
|
install(
|
||||||
DIRECTORY include/
|
DIRECTORY include/
|
||||||
DESTINATION include
|
DESTINATION include
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Testing
|
||||||
if(BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
find_package(ament_lint_auto REQUIRED)
|
find_package(ament_lint_auto REQUIRED)
|
||||||
# the following line skips the linter which checks for copyrights
|
|
||||||
# uncomment the line when a copyright and license is not present in all source files
|
|
||||||
#set(ament_cmake_copyright_FOUND TRUE)
|
|
||||||
# the following line skips cpplint (only works in a git repo)
|
|
||||||
# uncomment the line when this package is not in a git repo
|
|
||||||
#set(ament_cmake_cpplint_FOUND TRUE)
|
|
||||||
ament_lint_auto_find_test_dependencies()
|
ament_lint_auto_find_test_dependencies()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Export and package configuration
|
||||||
ament_export_include_directories(include)
|
ament_export_include_directories(include)
|
||||||
ament_package()
|
ament_package()
|
||||||
|
|
3
src/casestudy/include/.gitignore
vendored
Normal file
3
src/casestudy/include/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# We don't have content in this dir yet, but want it in git anyways
|
||||||
|
# So we add a non-empty .gitignore file, explicitly not ignoring itself
|
||||||
|
!.gitignore
|
|
@ -2,19 +2,25 @@
|
||||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
<package format="3">
|
<package format="3">
|
||||||
<name>casestudy</name>
|
<name>casestudy</name>
|
||||||
<version>0.0.0</version>
|
<version>0.1.0</version>
|
||||||
<description>TODO: Package description</description>
|
<description>
|
||||||
<maintainer email="vboxuser@todo.todo">vboxuser</maintainer>
|
ROS 2 case study package for evaluating different executor implementations and timing behaviors
|
||||||
<license>TODO: License declaration</license>
|
</description>
|
||||||
|
<maintainer email="kurt4wilson@gmail.com">kurt</maintainer>
|
||||||
|
<license>Apache License 2.0</license>
|
||||||
|
|
||||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||||
|
|
||||||
<test_depend>ament_lint_auto</test_depend>
|
<!-- Build Dependencies -->
|
||||||
<test_depend>ament_lint_common</test_depend>
|
<depend>rclcpp</depend>
|
||||||
|
|
||||||
<depend>priority_executor</depend>
|
<depend>priority_executor</depend>
|
||||||
<depend>simple_timer</depend>
|
<depend>simple_timer</depend>
|
||||||
<depend>casestudy_tools</depend>
|
<depend>casestudy_tools</depend>
|
||||||
|
<depend>jsoncpp</depend>
|
||||||
|
|
||||||
|
<!-- Test Dependencies -->
|
||||||
|
<test_depend>ament_lint_auto</test_depend>
|
||||||
|
<test_depend>ament_lint_common</test_depend>
|
||||||
|
|
||||||
<export>
|
<export>
|
||||||
<build_type>ament_cmake</build_type>
|
<build_type>ament_cmake</build_type>
|
||||||
|
|
|
@ -2,92 +2,88 @@
|
||||||
#include "priority_executor/priority_executor.hpp"
|
#include "priority_executor/priority_executor.hpp"
|
||||||
#include "priority_executor/priority_memory_strategy.hpp"
|
#include "priority_executor/priority_memory_strategy.hpp"
|
||||||
#include "casestudy_tools/test_nodes.hpp"
|
#include "casestudy_tools/test_nodes.hpp"
|
||||||
#include <string>
|
|
||||||
#include <fstream>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include "std_msgs/msg/string.hpp"
|
#include "std_msgs/msg/string.hpp"
|
||||||
#include "casestudy_tools/primes_workload.hpp"
|
#include "casestudy_tools/primes_workload.hpp"
|
||||||
#include <sys/prctl.h>
|
|
||||||
#include "casestudy_tools/primes_workload.hpp"
|
|
||||||
#include "casestudy_tools/experiment.hpp"
|
#include "casestudy_tools/experiment.hpp"
|
||||||
#include "jsoncpp/json/reader.h"
|
#include "jsoncpp/json/reader.h"
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
|
||||||
// bool should_do_task = true;
|
|
||||||
std::atomic<bool> should_do_task(true);
|
std::atomic<bool> should_do_task(true);
|
||||||
|
|
||||||
void run_one_executor(std::function<void(std::atomic<bool> &)> exec_fun, int idx)
|
void run_one_executor(
|
||||||
{
|
std::function<void(std::atomic<bool>&)> const& exec_fun,
|
||||||
|
int const idx) {
|
||||||
(void)idx;
|
(void)idx;
|
||||||
|
|
||||||
// set this process to highest priority
|
// set this process to highest priority
|
||||||
struct sched_param param;
|
struct sched_param param;
|
||||||
param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
|
param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
|
||||||
int result = sched_setscheduler(0, SCHED_FIFO, ¶m);
|
int const result = sched_setscheduler(0, SCHED_FIFO, ¶m);
|
||||||
if (result != 0)
|
if (result != 0) {
|
||||||
{
|
std::cout << "ros_experiment: sched_setscheduler failed: " << result
|
||||||
std::cout << "ros_experiment: sched_setscheduler failed: " << result << ": " << strerror(errno) << std::endl;
|
<< ": " << strerror(errno) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set process name
|
// set process name
|
||||||
prctl(PR_SET_NAME, "ros_experiment", 0, 0, 0);
|
prctl(PR_SET_NAME, "ros_experiment", 0, 0, 0);
|
||||||
|
|
||||||
exec_fun(should_do_task);
|
exec_fun(should_do_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ros_experiment(int argc, char **arg, std::string file_name, ExperimentConfig config)
|
int ros_experiment(
|
||||||
{
|
int const argc,
|
||||||
|
char** const argv,
|
||||||
|
std::string const& file_name,
|
||||||
|
ExperimentConfig config) {
|
||||||
|
|
||||||
|
rclcpp::init(argc, argv);
|
||||||
|
|
||||||
rclcpp::init(argc, arg);
|
|
||||||
// we have to create a node to process any passed parameters
|
// we have to create a node to process any passed parameters
|
||||||
auto node = rclcpp::Node::make_shared("experiment_node");
|
auto const node = rclcpp::Node::make_shared("experiment_node");
|
||||||
node->declare_parameter("executor_type", "edf");
|
node->declare_parameter("executor_type", "edf");
|
||||||
|
|
||||||
std::string executor_type;
|
std::string executor_type;
|
||||||
node->get_parameter("executor_type", executor_type);
|
node->get_parameter("executor_type", executor_type);
|
||||||
std::cout << "using executor type: " << executor_type << std::endl;
|
std::cout << "using executor type: " << executor_type << std::endl;
|
||||||
|
|
||||||
// TODO: move experiment configuration to main
|
|
||||||
config.executor_type = executor_type;
|
config.executor_type = executor_type;
|
||||||
config.nodes.push_back(node);
|
config.nodes.push_back(node);
|
||||||
|
|
||||||
Experiment experiment(config);
|
Experiment experiment(config);
|
||||||
// std::string result_log = experiment.run(should_do_task);
|
auto const exec_funs = experiment.getRunFunctions();
|
||||||
std::vector<std::function<void(std::atomic<bool> &)>> exec_funs = experiment.getRunFunctions();
|
|
||||||
std::cout << "got " << exec_funs.size() << " executors" << std::endl;
|
std::cout << "got " << exec_funs.size() << " executors" << std::endl;
|
||||||
|
|
||||||
std::vector<std::thread> exec_threads;
|
std::vector<std::thread> exec_threads;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
experiment.resetTimers();
|
experiment.resetTimers();
|
||||||
for (auto exec_fun : exec_funs)
|
|
||||||
{
|
for (auto const& exec_fun : exec_funs) {
|
||||||
exec_threads.push_back(std::thread(run_one_executor, exec_fun, i));
|
exec_threads.emplace_back(run_one_executor, exec_fun, i++);
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &t : exec_threads)
|
for (auto& t : exec_threads) {
|
||||||
{
|
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
experiment.writeLogsToFile(file_name, "2023customfile_singlethread");
|
experiment.writeLogsToFile(file_name, "2023customfile_singlethread");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char* argv[]) {
|
||||||
{
|
std::ifstream input_file("taskset.json");
|
||||||
std::ifstream input_file("taskset.json"); // Replace with actual filename
|
|
||||||
|
|
||||||
if (!input_file.is_open())
|
if (!input_file.is_open()) {
|
||||||
{
|
|
||||||
std::cerr << "Error opening file!" << std::endl;
|
std::cerr << "Error opening file!" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value root;
|
Json::Value root;
|
||||||
Json::Reader reader;
|
Json::Reader reader;
|
||||||
bool parsingSuccessful = reader.parse(input_file, root);
|
bool const parsingSuccessful = reader.parse(input_file, root);
|
||||||
|
|
||||||
if (!parsingSuccessful)
|
if (!parsingSuccessful) {
|
||||||
{
|
|
||||||
std::cerr << "Error parsing file!" << std::endl;
|
std::cerr << "Error parsing file!" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -95,100 +91,74 @@ int main(int argc, char *argv[])
|
||||||
// set ourselves to the second highest priority
|
// set ourselves to the second highest priority
|
||||||
struct sched_param param;
|
struct sched_param param;
|
||||||
param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 2;
|
param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 2;
|
||||||
int result = sched_setscheduler(0, SCHED_FIFO, ¶m);
|
int const result = sched_setscheduler(0, SCHED_FIFO, ¶m);
|
||||||
if (result != 0)
|
if (result != 0) {
|
||||||
{
|
std::cout << "ros_experiment: sched_setscheduler failed: " << result
|
||||||
std::cout << "ros_experiment: sched_setscheduler failed: " << result << ": " << strerror(errno) << std::endl;
|
<< ": " << strerror(errno) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// calibrate the dummy load for the current system
|
// calibrate the dummy load for the current system
|
||||||
calibrate_dummy_load();
|
calibrate_dummy_load();
|
||||||
|
|
||||||
ExperimentConfig config;
|
ExperimentConfig config;
|
||||||
|
|
||||||
// config.chain_lengths = {4, 3, 4};
|
for (uint i = 0; i < root["chain_lengths"].size(); i++) {
|
||||||
for (uint i = 0; i < root["chain_lengths"].size(); i++)
|
|
||||||
{
|
|
||||||
config.chain_lengths.push_back(root["chain_lengths"][i].asUInt());
|
config.chain_lengths.push_back(root["chain_lengths"][i].asUInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
// config.node_ids = {{0, 1, 2, 3}, {4, 5, 6}, {7, 8, 9, 10}};
|
for (uint i = 0; i < root["node_ids"].size(); i++) {
|
||||||
for (uint i = 0; i < root["node_ids"].size(); i++)
|
|
||||||
{
|
|
||||||
std::vector<uint32_t> node_ids_row;
|
std::vector<uint32_t> node_ids_row;
|
||||||
for (uint j = 0; j < root["node_ids"][i].size(); j++)
|
for (uint j = 0; j < root["node_ids"][i].size(); j++) {
|
||||||
{
|
|
||||||
node_ids_row.push_back(root["node_ids"][i][j].asUInt());
|
node_ids_row.push_back(root["node_ids"][i][j].asUInt());
|
||||||
}
|
}
|
||||||
config.node_ids.push_back(node_ids_row);
|
config.node_ids.push_back(std::move(node_ids_row));
|
||||||
}
|
}
|
||||||
// config.node_priorities = {3, 2, 1, 0, 6, 5, 4, 10, 9, 8, 7}; // PICAS-style prioritiesA
|
|
||||||
for (uint i = 0; i < root["node_priorities"].size(); i++)
|
for (uint i = 0; i < root["node_priorities"].size(); i++) {
|
||||||
{
|
|
||||||
config.node_priorities.push_back(root["node_priorities"][i].asUInt());
|
config.node_priorities.push_back(root["node_priorities"][i].asUInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
// disabled for custom
|
|
||||||
config.num_groups = 0;
|
config.num_groups = 0;
|
||||||
// config.group_memberships = {0, 0, 1, 2, 0, 0, 1, 0, 0, 1, 2};
|
|
||||||
|
|
||||||
// config.chain_timer_control = {0, 1, 2};
|
for (uint i = 0; i < root["chain_timer_control"].size(); i++) {
|
||||||
for (uint i = 0; i < root["chain_timer_control"].size(); i++)
|
|
||||||
{
|
|
||||||
config.chain_timer_control.push_back(root["chain_timer_control"][i].asInt());
|
config.chain_timer_control.push_back(root["chain_timer_control"][i].asInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
// config.node_runtimes = {5, 5, 5, 5, // chain 1
|
for (uint i = 0; i < root["node_runtimes"].size(); i++) {
|
||||||
// 10, 20, 5, // chain 2
|
|
||||||
// 10, 10, 15, 20}; // chain 3
|
|
||||||
for (uint i = 0; i < root["node_runtimes"].size(); i++)
|
|
||||||
{
|
|
||||||
config.node_runtimes.push_back(root["node_runtimes"][i].asDouble());
|
config.node_runtimes.push_back(root["node_runtimes"][i].asDouble());
|
||||||
}
|
}
|
||||||
// config.chain_periods = {50, 100, 110};
|
|
||||||
for (uint i = 0; i < root["chain_periods"].size(); i++)
|
for (uint i = 0; i < root["chain_periods"].size(); i++) {
|
||||||
{
|
|
||||||
config.chain_periods.push_back(root["chain_periods"][i].asInt());
|
config.chain_periods.push_back(root["chain_periods"][i].asInt());
|
||||||
}
|
}
|
||||||
// config.node_executor_assignments = {}; // empty - multicore mode
|
|
||||||
for (uint i = 0; i < root["node_executor_assignments"].size(); i++)
|
for (uint i = 0; i < root["node_executor_assignments"].size(); i++) {
|
||||||
{
|
|
||||||
config.node_executor_assignments.push_back(root["node_executor_assignments"][i].asUInt() - 1);
|
config.node_executor_assignments.push_back(root["node_executor_assignments"][i].asUInt() - 1);
|
||||||
}
|
}
|
||||||
// single threaded mode - assign executors to cpu cores
|
|
||||||
for (uint i = 0; i < root["executor_to_cpu_core"].size(); i++)
|
for (uint i = 0; i < root["executor_to_cpu_core"].size(); i++) {
|
||||||
{
|
|
||||||
config.executor_to_cpu_assignments.push_back(root["executor_to_cpu_core"][i].asUInt() - 1);
|
config.executor_to_cpu_assignments.push_back(root["executor_to_cpu_core"][i].asUInt() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
config.parallel_mode = false;
|
config.parallel_mode = false;
|
||||||
config.cores = 4;
|
config.cores = 4;
|
||||||
|
|
||||||
// sanity_check_config(config);
|
int const i = (100 - 35) / 5;
|
||||||
|
int const ros_budget = 35 + i * 5;
|
||||||
|
int const other_task_budget = 65 - i * 5;
|
||||||
|
std::cout << "ROS budget: " << ros_budget
|
||||||
|
<< " Other task budget: " << other_task_budget << std::endl;
|
||||||
|
|
||||||
// from 35/65 to 100/0 in increments of 5
|
|
||||||
// for (int i = 0; i <= (100 - 35) / 5; i++)
|
|
||||||
// {
|
|
||||||
// for now, run with 100% ros
|
|
||||||
int i = (100 - 35) / 5;
|
|
||||||
int ros_budget = 35 + i * 5;
|
|
||||||
int other_task_budget = 65 - i * 5;
|
|
||||||
std::cout << "ROS budget: " << ros_budget << " Other task budget: " << other_task_budget << std::endl;
|
|
||||||
// do other task in a separate thread
|
|
||||||
should_do_task.store(true);
|
should_do_task.store(true);
|
||||||
double other_task_time = 80 * other_task_budget / 100.0;
|
double const other_task_time = 80 * other_task_budget / 100.0;
|
||||||
std::thread other_task(do_other_task, other_task_time, 80, std::ref(should_do_task));
|
std::thread other_task(do_other_task, other_task_time, 80, std::ref(should_do_task));
|
||||||
|
|
||||||
// do ros task
|
std::string const file_name = "ros_" + std::to_string(ros_budget);
|
||||||
|
|
||||||
std::string file_name = "ros_" + std::to_string(ros_budget);
|
|
||||||
// ros_experiment(argc, argv, file_name);
|
|
||||||
std::thread ros_task(ros_experiment, argc, argv, file_name, config);
|
std::thread ros_task(ros_experiment, argc, argv, file_name, config);
|
||||||
std::cout << "tasks started" << std::endl;
|
std::cout << "tasks started" << std::endl;
|
||||||
ros_task.join();
|
ros_task.join();
|
||||||
other_task.join();
|
other_task.join();
|
||||||
std::cout << "tasks joined" << std::endl;
|
std::cout << "tasks joined" << std::endl;
|
||||||
|
|
||||||
// // run once
|
return 0;
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
}
|
}
|
|
@ -7,8 +7,9 @@
|
||||||
// bool should_do_task = true;
|
// bool should_do_task = true;
|
||||||
std::atomic<bool> should_do_task(true);
|
std::atomic<bool> should_do_task(true);
|
||||||
|
|
||||||
void run_one_executor(std::function<void(std::atomic<bool> &)> exec_fun,
|
void run_one_executor(
|
||||||
int idx) {
|
std::function<void(std::atomic<bool>&)> const& exec_fun,
|
||||||
|
int const idx) {
|
||||||
(void)idx;
|
(void)idx;
|
||||||
// set this process to highest priority
|
// set this process to highest priority
|
||||||
// struct sched_param param;
|
// struct sched_param param;
|
||||||
|
@ -27,7 +28,8 @@ void run_one_executor(std::function<void(std::atomic<bool> &)> exec_fun,
|
||||||
rt_attr.sched_policy = SCHED_FIFO;
|
rt_attr.sched_policy = SCHED_FIFO;
|
||||||
rt_attr.sched_runtime = 0;
|
rt_attr.sched_runtime = 0;
|
||||||
rt_attr.sched_period = 0;
|
rt_attr.sched_period = 0;
|
||||||
int result = sched_setattr(0, &rt_attr, 0);
|
|
||||||
|
int const result = sched_setattr(0, &rt_attr, 0);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
std::cout << "executor task: could not set scheduler: " << result << ": "
|
std::cout << "executor task: could not set scheduler: " << result << ": "
|
||||||
<< strerror(errno) << std::endl;
|
<< strerror(errno) << std::endl;
|
||||||
|
@ -35,11 +37,12 @@ void run_one_executor(std::function<void(std::atomic<bool> &)> exec_fun,
|
||||||
|
|
||||||
// set process name
|
// set process name
|
||||||
prctl(PR_SET_NAME, "ros_experiment", 0, 0, 0);
|
prctl(PR_SET_NAME, "ros_experiment", 0, 0, 0);
|
||||||
|
|
||||||
exec_fun(should_do_task);
|
exec_fun(should_do_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ros_experiment(rclcpp::Node::SharedPtr node, std::string file_name,
|
int ros_experiment(
|
||||||
|
rclcpp::Node::SharedPtr const node,
|
||||||
|
std::string const& file_name,
|
||||||
ExperimentConfig config) {
|
ExperimentConfig config) {
|
||||||
|
|
||||||
std::string executor_type;
|
std::string executor_type;
|
||||||
|
@ -56,33 +59,32 @@ int ros_experiment(rclcpp::Node::SharedPtr node, std::string file_name,
|
||||||
|
|
||||||
Experiment experiment(config);
|
Experiment experiment(config);
|
||||||
// std::string result_log = experiment.run(should_do_task);
|
// std::string result_log = experiment.run(should_do_task);
|
||||||
std::vector<std::function<void(std::atomic<bool> &)>> exec_funs =
|
auto const exec_funs = experiment.getRunFunctions();
|
||||||
experiment.getRunFunctions();
|
|
||||||
std::cout << "got " << exec_funs.size() << " executors" << std::endl;
|
std::cout << "got " << exec_funs.size() << " executors" << std::endl;
|
||||||
std::vector<std::thread> exec_threads;
|
std::vector<std::thread> exec_threads;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
experiment.resetTimers();
|
experiment.resetTimers();
|
||||||
for (auto exec_fun : exec_funs) {
|
|
||||||
exec_threads.push_back(std::thread(run_one_executor, exec_fun, i));
|
for (auto const& exec_fun : exec_funs) {
|
||||||
i++;
|
exec_threads.emplace_back(run_one_executor, exec_fun, i++);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &t : exec_threads) {
|
for (auto& t : exec_threads) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string outputname = "2024ours_executor2executor";
|
std::string outputname = "2024ours_executor2executor_" +
|
||||||
outputname +=
|
std::to_string(num_subscribers) + "_" +
|
||||||
"_" + std::to_string(num_subscribers) + "_" + std::to_string(num_chains);
|
std::to_string(num_chains);
|
||||||
experiment.writeLogsToFile(file_name, outputname);
|
experiment.writeLogsToFile(file_name, outputname);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
rclcpp::init(argc, argv);
|
rclcpp::init(argc, argv);
|
||||||
// we have to create a node to process any passed parameters
|
// we have to create a node to process any passed parameters
|
||||||
auto node = rclcpp::Node::make_shared("experiment_node");
|
auto const node = rclcpp::Node::make_shared("experiment_node");
|
||||||
// auto node = rclcpp::Node::make_shared("experiment_node",
|
// auto node = rclcpp::Node::make_shared("experiment_node",
|
||||||
// rclcpp::NodeOptions().use_intra_process_comms(true));
|
// rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||||
node->declare_parameter("executor_type", "edf");
|
node->declare_parameter("executor_type", "edf");
|
||||||
|
@ -106,7 +108,7 @@ int main(int argc, char *argv[]) {
|
||||||
rt_attr.sched_policy = SCHED_FIFO;
|
rt_attr.sched_policy = SCHED_FIFO;
|
||||||
// rt_attr.sched_priority = sched_get_priority_max(SCHED_FIFO) - 2;
|
// rt_attr.sched_priority = sched_get_priority_max(SCHED_FIFO) - 2;
|
||||||
rt_attr.sched_priority = 99;
|
rt_attr.sched_priority = 99;
|
||||||
int result = sched_setattr(0, &rt_attr, 0);
|
int const result = sched_setattr(0, &rt_attr, 0);
|
||||||
|
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
std::cout << "ros_experiment: sched_setscheduler failed: " << result << ": "
|
std::cout << "ros_experiment: sched_setscheduler failed: " << result << ": "
|
||||||
|
@ -141,12 +143,10 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
for (int i = 0; i < num_s + 1; i++) {
|
for (int i = 0; i < num_s + 1; i++) {
|
||||||
config.node_priorities.push_back(i);
|
config.node_priorities.push_back(i);
|
||||||
|
config.node_runtimes.push_back(runtime);
|
||||||
}
|
}
|
||||||
config.chain_lengths.push_back(num_s+1);
|
config.chain_lengths.push_back(num_s+1);
|
||||||
config.chain_periods.push_back(10);
|
config.chain_periods.push_back(10);
|
||||||
for (int i = 0; i < num_s + 1; i++) {
|
|
||||||
config.node_runtimes.push_back(runtime);
|
|
||||||
}
|
|
||||||
config.chain_timer_control.push_back(c);
|
config.chain_timer_control.push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,16 +174,16 @@ int main(int argc, char *argv[]) {
|
||||||
// for (int i = 0; i <= (100 - 35) / 5; i++)
|
// for (int i = 0; i <= (100 - 35) / 5; i++)
|
||||||
// {
|
// {
|
||||||
// for now, run with 100% ros
|
// for now, run with 100% ros
|
||||||
int i = (100 - 35) / 5;
|
int const i = (100 - 35) / 5;
|
||||||
int ros_budget = 35 + i * 5;
|
int const ros_budget = 35 + i * 5;
|
||||||
int other_task_budget = 65 - i * 5;
|
int const other_task_budget = 65 - i * 5;
|
||||||
std::cout << "ROS budget: " << ros_budget
|
std::cout << "ROS budget: " << ros_budget
|
||||||
<< " Other task budget: " << other_task_budget << std::endl;
|
<< " Other task budget: " << other_task_budget << std::endl;
|
||||||
// do other task in a separate thread
|
// do other task in a separate thread
|
||||||
should_do_task.store(true);
|
should_do_task.store(true);
|
||||||
|
|
||||||
// do ros task
|
// do ros task
|
||||||
std::string file_name = "ros_" + std::to_string(ros_budget);
|
std::string const file_name = "ros_" + std::to_string(ros_budget);
|
||||||
// ros_experiment(argc, argv, file_name);
|
// ros_experiment(argc, argv, file_name);
|
||||||
std::thread ros_task(ros_experiment, node, file_name, config);
|
std::thread ros_task(ros_experiment, node, file_name, config);
|
||||||
std::cout << "tasks started" << std::endl;
|
std::cout << "tasks started" << std::endl;
|
||||||
|
@ -193,4 +193,5 @@ int main(int argc, char *argv[]) {
|
||||||
// // run once
|
// // run once
|
||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
|
return 0;
|
||||||
}
|
}
|
|
@ -7,9 +7,8 @@
|
||||||
// bool should_do_task = true;
|
// bool should_do_task = true;
|
||||||
std::atomic<bool> should_do_task(true);
|
std::atomic<bool> should_do_task(true);
|
||||||
|
|
||||||
|
void run_one_executor(std::function<void(std::atomic<bool>&)> const& exec_fun,
|
||||||
void run_one_executor(std::function<void(std::atomic<bool> &)> exec_fun,
|
int const idx) {
|
||||||
int idx) {
|
|
||||||
(void)idx;
|
(void)idx;
|
||||||
// set this process to highest priority
|
// set this process to highest priority
|
||||||
// struct sched_param param;
|
// struct sched_param param;
|
||||||
|
@ -28,7 +27,7 @@ void run_one_executor(std::function<void(std::atomic<bool> &)> exec_fun,
|
||||||
rt_attr.sched_policy = SCHED_FIFO;
|
rt_attr.sched_policy = SCHED_FIFO;
|
||||||
rt_attr.sched_runtime = 0;
|
rt_attr.sched_runtime = 0;
|
||||||
rt_attr.sched_period = 0;
|
rt_attr.sched_period = 0;
|
||||||
int result = sched_setattr(0, &rt_attr, 0);
|
int const result = sched_setattr(0, &rt_attr, 0);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
std::cout << "executor task: could not set scheduler: " << result << ": "
|
std::cout << "executor task: could not set scheduler: " << result << ": "
|
||||||
<< strerror(errno) << std::endl;
|
<< strerror(errno) << std::endl;
|
||||||
|
@ -40,9 +39,8 @@ void run_one_executor(std::function<void(std::atomic<bool> &)> exec_fun,
|
||||||
exec_fun(should_do_task);
|
exec_fun(should_do_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ros_experiment(rclcpp::Node::SharedPtr node, std::string file_name,
|
int ros_experiment(rclcpp::Node::SharedPtr const& node, std::string const& file_name,
|
||||||
ExperimentConfig config) {
|
ExperimentConfig config) {
|
||||||
|
|
||||||
std::string executor_type;
|
std::string executor_type;
|
||||||
node->get_parameter("executor_type", executor_type);
|
node->get_parameter("executor_type", executor_type);
|
||||||
int num_subscribers;
|
int num_subscribers;
|
||||||
|
@ -57,33 +55,32 @@ int ros_experiment(rclcpp::Node::SharedPtr node, std::string file_name,
|
||||||
|
|
||||||
Experiment experiment(config);
|
Experiment experiment(config);
|
||||||
// std::string result_log = experiment.run(should_do_task);
|
// std::string result_log = experiment.run(should_do_task);
|
||||||
std::vector<std::function<void(std::atomic<bool> &)>> exec_funs =
|
std::vector<std::function<void(std::atomic<bool>&)>> exec_funs =
|
||||||
experiment.getRunFunctions();
|
experiment.getRunFunctions();
|
||||||
std::cout << "got " << exec_funs.size() << " executors" << std::endl;
|
std::cout << "got " << exec_funs.size() << " executors" << std::endl;
|
||||||
std::vector<std::thread> exec_threads;
|
std::vector<std::thread> exec_threads;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
experiment.resetTimers();
|
experiment.resetTimers();
|
||||||
for (auto exec_fun : exec_funs) {
|
for (auto const& exec_fun : exec_funs) {
|
||||||
exec_threads.push_back(std::thread(run_one_executor, exec_fun, i));
|
exec_threads.push_back(std::thread(run_one_executor, exec_fun, i));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &t : exec_threads) {
|
for (auto& t : exec_threads) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string outputname = "2024ours_latency";
|
std::string const outputname = "2024ours_latency_" +
|
||||||
outputname +=
|
std::to_string(num_subscribers) + "_" + std::to_string(num_chains);
|
||||||
"_" + std::to_string(num_subscribers) + "_" + std::to_string(num_chains);
|
|
||||||
experiment.writeLogsToFile(file_name, outputname);
|
experiment.writeLogsToFile(file_name, outputname);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
rclcpp::init(argc, argv);
|
rclcpp::init(argc, argv);
|
||||||
// we have to create a node to process any passed parameters
|
// we have to create a node to process any passed parameters
|
||||||
auto node = rclcpp::Node::make_shared("experiment_node");
|
auto const node = rclcpp::Node::make_shared("experiment_node");
|
||||||
// auto node = rclcpp::Node::make_shared("experiment_node",
|
// auto node = rclcpp::Node::make_shared("experiment_node",
|
||||||
// rclcpp::NodeOptions().use_intra_process_comms(true));
|
// rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||||
node->declare_parameter("executor_type", "edf");
|
node->declare_parameter("executor_type", "edf");
|
||||||
|
@ -107,7 +104,7 @@ int main(int argc, char *argv[]) {
|
||||||
rt_attr.sched_policy = SCHED_FIFO;
|
rt_attr.sched_policy = SCHED_FIFO;
|
||||||
// rt_attr.sched_priority = sched_get_priority_max(SCHED_FIFO) - 2;
|
// rt_attr.sched_priority = sched_get_priority_max(SCHED_FIFO) - 2;
|
||||||
rt_attr.sched_priority = 99;
|
rt_attr.sched_priority = 99;
|
||||||
int result = sched_setattr(0, &rt_attr, 0);
|
int const result = sched_setattr(0, &rt_attr, 0);
|
||||||
|
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
std::cout << "ros_experiment: sched_setscheduler failed: " << result << ": "
|
std::cout << "ros_experiment: sched_setscheduler failed: " << result << ": "
|
||||||
|
@ -134,7 +131,7 @@ int main(int argc, char *argv[]) {
|
||||||
for (int i = 0; i < num_s + 1; i++) {
|
for (int i = 0; i < num_s + 1; i++) {
|
||||||
config.node_priorities.push_back(i);
|
config.node_priorities.push_back(i);
|
||||||
}
|
}
|
||||||
config.chain_lengths.push_back(num_s+1);
|
config.chain_lengths.push_back(num_s + 1);
|
||||||
config.chain_periods.push_back(10);
|
config.chain_periods.push_back(10);
|
||||||
for (int i = 0; i < num_s + 1; i++) {
|
for (int i = 0; i < num_s + 1; i++) {
|
||||||
config.node_runtimes.push_back(runtime);
|
config.node_runtimes.push_back(runtime);
|
||||||
|
@ -150,7 +147,6 @@ int main(int argc, char *argv[]) {
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
config.num_groups = 0;
|
config.num_groups = 0;
|
||||||
config.group_memberships = {}; // no groups
|
config.group_memberships = {}; // no groups
|
||||||
|
|
||||||
|
@ -166,16 +162,16 @@ int main(int argc, char *argv[]) {
|
||||||
// for (int i = 0; i <= (100 - 35) / 5; i++)
|
// for (int i = 0; i <= (100 - 35) / 5; i++)
|
||||||
// {
|
// {
|
||||||
// for now, run with 100% ros
|
// for now, run with 100% ros
|
||||||
int i = (100 - 35) / 5;
|
int const i = (100 - 35) / 5;
|
||||||
int ros_budget = 35 + i * 5;
|
int const ros_budget = 35 + i * 5;
|
||||||
int other_task_budget = 65 - i * 5;
|
int const other_task_budget = 65 - i * 5;
|
||||||
std::cout << "ROS budget: " << ros_budget
|
std::cout << "ROS budget: " << ros_budget
|
||||||
<< " Other task budget: " << other_task_budget << std::endl;
|
<< " Other task budget: " << other_task_budget << std::endl;
|
||||||
// do other task in a separate thread
|
// do other task in a separate thread
|
||||||
should_do_task.store(true);
|
should_do_task.store(true);
|
||||||
|
|
||||||
// do ros task
|
// do ros task
|
||||||
std::string file_name = "ros_" + std::to_string(ros_budget);
|
std::string const file_name = "ros_" + std::to_string(ros_budget);
|
||||||
// ros_experiment(argc, argv, file_name);
|
// ros_experiment(argc, argv, file_name);
|
||||||
std::thread ros_task(ros_experiment, node, file_name, config);
|
std::thread ros_task(ros_experiment, node, file_name, config);
|
||||||
std::cout << "tasks started" << std::endl;
|
std::cout << "tasks started" << std::endl;
|
||||||
|
@ -185,4 +181,5 @@ int main(int argc, char *argv[]) {
|
||||||
// // run once
|
// // run once
|
||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
|
return 0;
|
||||||
}
|
}
|
|
@ -3,13 +3,10 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
|
|
||||||
#include <rclcpp/rclcpp.hpp>
|
#include <rclcpp/rclcpp.hpp>
|
||||||
#include <std_msgs/msg/string.hpp>
|
#include <std_msgs/msg/string.hpp>
|
||||||
|
|
||||||
#include "casestudy_tools/test_nodes.hpp"
|
#include "casestudy_tools/test_nodes.hpp"
|
||||||
#include "casestudy_tools/experiment.hpp"
|
#include "casestudy_tools/experiment.hpp"
|
||||||
#include "casestudy_tools/primes_workload.hpp"
|
#include "casestudy_tools/primes_workload.hpp"
|
||||||
|
@ -19,8 +16,9 @@
|
||||||
// bool should_do_task = true;
|
// bool should_do_task = true;
|
||||||
std::atomic<bool> should_do_task(true);
|
std::atomic<bool> should_do_task(true);
|
||||||
|
|
||||||
void run_one_executor(std::function<void(std::atomic<bool> &)> exec_fun, int idx)
|
void run_one_executor(
|
||||||
{
|
std::function<void(std::atomic<bool>&)> const& exec_fun,
|
||||||
|
int const idx) {
|
||||||
(void)idx;
|
(void)idx;
|
||||||
// cpu_set_t cpuset;
|
// cpu_set_t cpuset;
|
||||||
// CPU_ZERO(&cpuset);
|
// CPU_ZERO(&cpuset);
|
||||||
|
@ -38,32 +36,37 @@ void run_one_executor(std::function<void(std::atomic<bool> &)> exec_fun, int idx
|
||||||
// set this process to second highest priority
|
// set this process to second highest priority
|
||||||
struct sched_param param;
|
struct sched_param param;
|
||||||
param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 2;
|
param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 2;
|
||||||
int result = sched_setscheduler(0, SCHED_FIFO, ¶m);
|
int const result = sched_setscheduler(0, SCHED_FIFO, ¶m);
|
||||||
if (result != 0)
|
|
||||||
{
|
if (result != 0) {
|
||||||
std::cerr << "ros_experiment: sched_setscheduler failed: " << result << ": " << strerror(errno) << std::endl;
|
std::cerr << "ros_experiment: sched_setscheduler failed: " << result
|
||||||
std::cerr << "This operation requires root privileges. Please run the program with sufficient permissions." << std::endl;
|
<< ": " << strerror(errno) << std::endl;
|
||||||
|
std::cerr << "This operation requires root privileges. Please run the program with sufficient permissions."
|
||||||
|
<< std::endl;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the process name to "ros_experiment" for easier identification during debugging or system monitoring.
|
// Set the process name to "ros_experiment" for easier identification during debugging or system monitoring.
|
||||||
prctl(PR_SET_NAME, "ros_experiment", 0, 0, 0);
|
prctl(PR_SET_NAME, "ros_experiment", 0, 0, 0);
|
||||||
|
|
||||||
exec_fun(should_do_task);
|
exec_fun(should_do_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ros_experiment(int argc, char **arg, std::string file_name, ExperimentConfig config)
|
int ros_experiment(
|
||||||
{
|
int const argc,
|
||||||
|
char** const argv,
|
||||||
|
std::string const& file_name,
|
||||||
|
ExperimentConfig config) {
|
||||||
|
|
||||||
|
rclcpp::init(argc, argv);
|
||||||
|
|
||||||
rclcpp::init(argc, arg);
|
|
||||||
// we have to create a node to process any passed parameters
|
// we have to create a node to process any passed parameters
|
||||||
auto node = rclcpp::Node::make_shared("experiment_parameters");
|
auto const node = rclcpp::Node::make_shared("experiment_parameters");
|
||||||
node->declare_parameter("executor_type", "edf");
|
node->declare_parameter("executor_type", "edf");
|
||||||
|
|
||||||
std::string executor_type;
|
std::string executor_type;
|
||||||
node->get_parameter("executor_type", executor_type);
|
node->get_parameter("executor_type", executor_type);
|
||||||
std::cout << "using executor type: " << executor_type << std::endl;
|
std::cout << "using executor type: " << executor_type << std::endl;
|
||||||
|
|
||||||
// TODO: move experiment configuration to main
|
|
||||||
config.executor_type = executor_type;
|
config.executor_type = executor_type;
|
||||||
// Enable parallel mode for the experiment configuration.
|
// Enable parallel mode for the experiment configuration.
|
||||||
// When set to true, this allows multiple executors to run concurrently,
|
// When set to true, this allows multiple executors to run concurrently,
|
||||||
|
@ -73,29 +76,28 @@ int ros_experiment(int argc, char **arg, std::string file_name, ExperimentConfig
|
||||||
|
|
||||||
Experiment experiment(config);
|
Experiment experiment(config);
|
||||||
// std::string result_log = experiment.run(should_do_task);
|
// std::string result_log = experiment.run(should_do_task);
|
||||||
std::vector<std::function<void(std::atomic<bool> &)> > exec_funs = experiment.getRunFunctions();
|
auto const exec_funs = experiment.getRunFunctions();
|
||||||
std::cout << "got " << exec_funs.size() << " executors" << std::endl;
|
std::cout << "got " << exec_funs.size() << " executors" << std::endl;
|
||||||
|
|
||||||
std::vector<std::thread> exec_threads;
|
std::vector<std::thread> exec_threads;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
experiment.resetTimers();
|
experiment.resetTimers();
|
||||||
for (auto exec_fun : exec_funs)
|
|
||||||
{
|
for (auto const& exec_fun : exec_funs) {
|
||||||
exec_threads.push_back(std::thread(run_one_executor, exec_fun, i));
|
exec_threads.emplace_back(run_one_executor, exec_fun, i++);
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &t : exec_threads)
|
for (auto& t : exec_threads) {
|
||||||
{
|
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
std::string outputname = "casestudy_example";
|
|
||||||
|
std::string const outputname = "casestudy_example";
|
||||||
experiment.writeLogsToFile(file_name, outputname);
|
experiment.writeLogsToFile(file_name, outputname);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char* argv[]) {
|
||||||
{
|
|
||||||
// calibrate the dummy load for the current system
|
// calibrate the dummy load for the current system
|
||||||
calibrate_dummy_load();
|
calibrate_dummy_load();
|
||||||
|
|
||||||
|
@ -115,20 +117,18 @@ int main(int argc, char *argv[])
|
||||||
sanity_check_config(config);
|
sanity_check_config(config);
|
||||||
|
|
||||||
std::thread ros_task([&]() {
|
std::thread ros_task([&]() {
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
ros_experiment(argc, argv, "cs_example", config);
|
ros_experiment(argc, argv, "cs_example", config);
|
||||||
}
|
} catch (std::exception const& e) {
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
std::cerr << "Exception in ros_experiment: " << e.what() << std::endl;
|
std::cerr << "Exception in ros_experiment: " << e.what() << std::endl;
|
||||||
}
|
} catch (...) {
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
std::cerr << "Unknown exception in ros_experiment." << std::endl;
|
std::cerr << "Unknown exception in ros_experiment." << std::endl;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
std::cout << "tasks started" << std::endl;
|
std::cout << "tasks started" << std::endl;
|
||||||
ros_task.join();
|
ros_task.join();
|
||||||
std::cout << "tasks joined" << std::endl;
|
std::cout << "tasks joined" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
|
@ -1,38 +1,26 @@
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.8)
|
||||||
project(casestudy_tools)
|
project(casestudy_tools VERSION 0.1.0)
|
||||||
|
|
||||||
# Default to C99
|
# Set C++ standards
|
||||||
if(NOT CMAKE_C_STANDARD)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
endif()
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
# Default to C++14
|
|
||||||
if(NOT CMAKE_CXX_STANDARD)
|
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
# Compiler options
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# find dependencies
|
# Find dependencies
|
||||||
find_package(ament_cmake REQUIRED)
|
find_package(ament_cmake REQUIRED)
|
||||||
find_package(rclcpp REQUIRED)
|
find_package(rclcpp REQUIRED)
|
||||||
find_package(priority_executor REQUIRED)
|
find_package(priority_executor REQUIRED)
|
||||||
find_package(simple_timer REQUIRED)
|
find_package(simple_timer REQUIRED)
|
||||||
|
|
||||||
if(BUILD_TESTING)
|
# Library targets
|
||||||
find_package(ament_lint_auto REQUIRED)
|
add_library(primes_workload
|
||||||
# the following line skips the linter which checks for copyrights
|
src/primes_workload.cpp
|
||||||
# uncomment the line when a copyright and license is not present in all source files
|
)
|
||||||
#set(ament_cmake_copyright_FOUND TRUE)
|
|
||||||
# the following line skips cpplint (only works in a git repo)
|
|
||||||
# uncomment the line when this package is not in a git repo
|
|
||||||
#set(ament_cmake_cpplint_FOUND TRUE)
|
|
||||||
ament_lint_auto_find_test_dependencies()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(primes_workload src/primes_workload.cpp)
|
|
||||||
target_include_directories(primes_workload PUBLIC
|
target_include_directories(primes_workload PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:include>
|
||||||
|
@ -41,7 +29,6 @@ ament_target_dependencies(primes_workload
|
||||||
simple_timer
|
simple_timer
|
||||||
rclcpp
|
rclcpp
|
||||||
priority_executor
|
priority_executor
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(test_nodes src/test_nodes.cpp)
|
add_library(test_nodes src/test_nodes.cpp)
|
||||||
|
@ -54,8 +41,9 @@ ament_target_dependencies(test_nodes
|
||||||
rclcpp
|
rclcpp
|
||||||
simple_timer
|
simple_timer
|
||||||
)
|
)
|
||||||
|
add_library(experiment_host
|
||||||
add_library(experiment_host src/experiment.cpp)
|
src/experiment.cpp
|
||||||
|
)
|
||||||
target_include_directories(experiment_host PUBLIC
|
target_include_directories(experiment_host PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:include>
|
||||||
|
@ -70,7 +58,13 @@ ament_target_dependencies(experiment_host
|
||||||
priority_executor
|
priority_executor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
find_package(ament_lint_auto REQUIRED)
|
||||||
|
ament_lint_auto_find_test_dependencies()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Installation
|
||||||
install(
|
install(
|
||||||
DIRECTORY include/
|
DIRECTORY include/
|
||||||
DESTINATION include
|
DESTINATION include
|
||||||
|
@ -85,6 +79,7 @@ install(
|
||||||
INCLUDES DESTINATION include
|
INCLUDES DESTINATION include
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Export and package configuration
|
||||||
ament_export_include_directories(include)
|
ament_export_include_directories(include)
|
||||||
# ament_export_libraries(primes_workload test_nodes experiment_host)
|
# ament_export_libraries(primes_workload test_nodes experiment_host)
|
||||||
ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)
|
ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)
|
||||||
|
|
|
@ -8,9 +8,7 @@
|
||||||
#include "casestudy_tools/test_nodes.hpp"
|
#include "casestudy_tools/test_nodes.hpp"
|
||||||
#include "simple_timer/rt-sched.hpp"
|
#include "simple_timer/rt-sched.hpp"
|
||||||
|
|
||||||
|
struct ExperimentConfig {
|
||||||
struct ExperimentConfig
|
|
||||||
{
|
|
||||||
std::vector<uint32_t> chain_lengths;
|
std::vector<uint32_t> chain_lengths;
|
||||||
// TODO: rename to callback_ids
|
// TODO: rename to callback_ids
|
||||||
std::vector<std::vector<uint32_t>> node_ids;
|
std::vector<std::vector<uint32_t>> node_ids;
|
||||||
|
@ -38,103 +36,101 @@ struct ExperimentConfig
|
||||||
bool parallel_mode = false;
|
bool parallel_mode = false;
|
||||||
size_t cores = 2;
|
size_t cores = 2;
|
||||||
|
|
||||||
// rclcpp::Node::SharedPtr node;
|
// nodes to host the callbacks. In parallel (one executor using multiple threads) mode,
|
||||||
// nodes to host the callbacks. In parallel (one executor using multiple threads) mode, there is one node and one executor. The node is provided by the experiment provider
|
// there is one node and one executor. The node is provided by the experiment provider.
|
||||||
// in singlethread (each executor uses one thread), there is one node per executor. Additional nodes may be created by the experiment host
|
// In singlethread (each executor uses one thread), there is one node per executor.
|
||||||
|
// Additional nodes may be created by the experiment host.
|
||||||
std::vector<rclcpp::Node::SharedPtr> nodes;
|
std::vector<rclcpp::Node::SharedPtr> nodes;
|
||||||
std::vector<std::string> special_cases = {};
|
std::vector<std::string> special_cases;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct experiment_executor
|
struct experiment_executor {
|
||||||
{
|
std::shared_ptr<priority_executor::TimedExecutor> executor;
|
||||||
std::shared_ptr<timed_executor::TimedExecutor> executor;
|
std::shared_ptr<priority_executor::PriorityMemoryStrategy<>> strat;
|
||||||
std::shared_ptr<PriorityMemoryStrategy<>> strat;
|
|
||||||
|
|
||||||
std::shared_ptr<rclcpp::Executor> default_executor;
|
std::shared_ptr<rclcpp::Executor> default_executor;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Experiment
|
class Experiment {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
Experiment(ExperimentConfig config);
|
explicit Experiment(ExperimentConfig config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Run the experiment
|
* @brief Run the experiment in the current thread
|
||||||
* runs in the current thread
|
* @param should_do_task a flag that can be set to false to stop the experiment.
|
||||||
* @param should_do_task a flag that can be set to false to stop the experiment. The flag will be set to false when the experiment is complete.
|
* The flag will be set to false when the experiment is complete.
|
||||||
|
* @return std::string containing experiment results
|
||||||
*/
|
*/
|
||||||
std::string run(std::atomic<bool> &should_do_task);
|
std::string run(std::atomic<bool>& should_do_task);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the functions required to run the experiment
|
* @brief Get the functions required to run the experiment
|
||||||
* returns a vector of functions. Each function will run a different executor. It's up to the caller to run these functions in different threads.
|
* @return vector of functions. Each function will run a different executor.
|
||||||
|
* It's up to the caller to run these functions in different threads.
|
||||||
* The caller may set the thread properties (e.g. priority) before running the function.
|
* The caller may set the thread properties (e.g. priority) before running the function.
|
||||||
* Each function accepts a flag that can be set to false to stop the experiment. The flag will be set to false when the experiment is complete.
|
* Each function accepts a flag that can be set to false to stop the experiment.
|
||||||
|
* The flag will be set to false when the experiment is complete.
|
||||||
*/
|
*/
|
||||||
std::vector<std::function<void(std::atomic<bool> &)>> getRunFunctions();
|
std::vector<std::function<void(std::atomic<bool>&)>> getRunFunctions();
|
||||||
|
|
||||||
static size_t numExecutorsRequired(const ExperimentConfig &config);
|
static size_t numExecutorsRequired(ExperimentConfig const &config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the executor (and strategy) for a given executor index (0 by default)
|
* @brief Get the executor (and strategy) for a given executor index
|
||||||
|
* @param executor_idx The index of the executor (defaults to 0)
|
||||||
|
* @return experiment_executor
|
||||||
*/
|
*/
|
||||||
experiment_executor getExecutor(int executor_idx = 0);
|
experiment_executor getExecutor(int executor_idx = 0);
|
||||||
|
|
||||||
node_time_logger getInternalLogger();
|
node_time_logger getInternalLogger();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* collect logs from the executors. Also add any logs from passed in loggers
|
* @brief Collect logs from the executors and optionally add logs from passed in loggers
|
||||||
* @param optional array of loggers to add to the logs
|
* @param loggers Optional array of loggers to add to the logs
|
||||||
|
* @return std::string containing the combined logs
|
||||||
*/
|
*/
|
||||||
std::string buildLogs(std::vector<node_time_logger> loggers = {});
|
std::string buildLogs(std::vector<node_time_logger> loggers = {});
|
||||||
|
|
||||||
void resetTimers();
|
void resetTimers();
|
||||||
|
void writeLogsToFile(std::string const &filename, std::string const &experiment_name);
|
||||||
void writeLogsToFile(std::string filename, std::string experiment_name);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExperimentConfig config;
|
ExperimentConfig config;
|
||||||
std::vector<experiment_executor> executors;
|
std::vector<experiment_executor> executors;
|
||||||
|
|
||||||
// values to keep track of runtime values
|
// Chain-indexed vector of deques of deadlines
|
||||||
|
std::vector<std::deque<uint64_t>*> chain_deadlines_deque;
|
||||||
// chain-indexed vector of deques of deadlines
|
// Maps node_id to node
|
||||||
std::vector<std::deque<uint64_t> *> chain_deadlines_deque;
|
|
||||||
// maps node_id to node
|
|
||||||
std::map<uint32_t, std::shared_ptr<CaseStudyNode>> nodes;
|
std::map<uint32_t, std::shared_ptr<CaseStudyNode>> nodes;
|
||||||
// maps chain_id to timer
|
// Maps chain_id to timer
|
||||||
std::map<uint32_t, std::shared_ptr<rclcpp::TimerBase>> chain_timer_handles;
|
std::map<uint32_t, std::shared_ptr<rclcpp::TimerBase>> chain_timer_handles;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create the executors using the type and amount set in the config
|
* @brief Create the executors using the type and amount set in the config
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
void createExecutors();
|
void createExecutors();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a single executor using the type set in the config
|
* @brief Create a single executor using the type set in the config
|
||||||
*
|
* @param executor_num The executor number to create
|
||||||
* @return experiment_executor
|
* @return experiment_executor
|
||||||
*/
|
*/
|
||||||
experiment_executor createSingleExecutor(uint executor_num = 0);
|
experiment_executor createSingleExecutor(uint executor_num = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the executor for a given node id
|
* @brief Get the executor for a given node id
|
||||||
* @param node_id if the node_executor_assignments is empty, then this parameter is ignored, and the first executor is returned
|
* @param node_id If node_executor_assignments is empty, this parameter is ignored
|
||||||
|
* and the first executor is returned
|
||||||
|
* @return experiment_executor
|
||||||
*/
|
*/
|
||||||
experiment_executor getExecutorForNode(int node_id=0);
|
experiment_executor getExecutorForNode(int node_id = 0);
|
||||||
|
|
||||||
void createNodesAndAssignProperties();
|
void createNodesAndAssignProperties();
|
||||||
|
|
||||||
|
|
||||||
void setInitialDeadlines();
|
void setInitialDeadlines();
|
||||||
|
|
||||||
|
|
||||||
node_time_logger _logger;
|
node_time_logger _logger;
|
||||||
};
|
};
|
||||||
|
|
||||||
int do_other_task(double work_time, uint period, std::atomic<bool> &should_do_task);
|
int do_other_task(double work_time, uint period, std::atomic<bool>& should_do_task);
|
||||||
void sanity_check_config(ExperimentConfig config);
|
void sanity_check_config(ExperimentConfig const &config);
|
||||||
|
|
||||||
#endif // RTIS_EXPERIMENT_HOST
|
#endif // RTIS_EXPERIMENT_HOST
|
|
@ -22,7 +22,7 @@ namespace DummyLoadCalibration
|
||||||
{
|
{
|
||||||
inline std::atomic<int> calibration{1};
|
inline std::atomic<int> calibration{1};
|
||||||
|
|
||||||
inline void setCalibration(int calib);
|
inline void setCalibration(int const calib);
|
||||||
|
|
||||||
inline int getCalibration();
|
inline int getCalibration();
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,6 @@ void calibrate_dummy_load();
|
||||||
* This function performs a busy wait for a specified duration.
|
* This function performs a busy wait for a specified duration.
|
||||||
* It is used to calibrate the dummy load for the current system.
|
* It is used to calibrate the dummy load for the current system.
|
||||||
*/
|
*/
|
||||||
void dummy_load(int load_ms);
|
void dummy_load(int const load_ms);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -1,21 +1,21 @@
|
||||||
#ifndef RTIS_TEST_NODES
|
#ifndef RTIS_TEST_NODES
|
||||||
#define RTIS_TEST_NODES
|
#define RTIS_TEST_NODES
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "rclcpp/rclcpp.hpp"
|
#include "rclcpp/rclcpp.hpp"
|
||||||
#include "std_msgs/msg/string.hpp"
|
#include "std_msgs/msg/string.hpp"
|
||||||
#include "std_msgs/msg/int32.hpp"
|
#include "std_msgs/msg/int32.hpp"
|
||||||
#include "simple_timer/rt-sched.hpp"
|
#include "simple_timer/rt-sched.hpp"
|
||||||
|
|
||||||
class CaseStudyNode
|
class CaseStudyNode {
|
||||||
{
|
public:
|
||||||
public: node_time_logger logger;
|
node_time_logger logger;
|
||||||
// inherit constructor
|
// inherit constructor
|
||||||
CaseStudyNode(std::string publish_topic, rclcpp::Node::SharedPtr node)
|
CaseStudyNode(std::string const &publish_topic, rclcpp::Node::SharedPtr const &node) {
|
||||||
{
|
|
||||||
this->name = publish_topic;
|
this->name = publish_topic;
|
||||||
this->node = node;
|
this->node = node;
|
||||||
// seed random number generator
|
// seed random number generator
|
||||||
srand(time(NULL));
|
srand(time(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool use_random_runtime = false;
|
bool use_random_runtime = false;
|
||||||
|
@ -25,33 +25,38 @@ class CaseStudyNode
|
||||||
std::string get_name();
|
std::string get_name();
|
||||||
rclcpp::CallbackGroup::SharedPtr callback_group_;
|
rclcpp::CallbackGroup::SharedPtr callback_group_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
rclcpp::Node::SharedPtr node;
|
rclcpp::Node::SharedPtr node;
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define COUNT_MAX 500
|
#define COUNT_MAX 500
|
||||||
|
|
||||||
class PublisherNode : public CaseStudyNode{
|
class PublisherNode : public CaseStudyNode {
|
||||||
public:
|
public:
|
||||||
PublisherNode(std::string publish_topic, double runtime, int period, int chain, rclcpp::Node::SharedPtr node, rclcpp::CallbackGroup::SharedPtr callback_group = nullptr);
|
PublisherNode(std::string const &publish_topic, double runtime, int period, int chain,
|
||||||
|
rclcpp::Node::SharedPtr const &node,
|
||||||
|
rclcpp::CallbackGroup::SharedPtr const &callback_group = nullptr);
|
||||||
rclcpp::TimerBase::SharedPtr timer_;
|
rclcpp::TimerBase::SharedPtr timer_;
|
||||||
int count_max_ = COUNT_MAX;
|
int count_max_ = COUNT_MAX;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
rclcpp::Publisher<std_msgs::msg::Int32>::SharedPtr pub_;
|
rclcpp::Publisher<std_msgs::msg::Int32>::SharedPtr pub_;
|
||||||
double runtime_;
|
double runtime_;
|
||||||
int period_;
|
int period_;
|
||||||
int chain_;
|
int chain_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WorkerNode : public CaseStudyNode{
|
class WorkerNode : public CaseStudyNode {
|
||||||
public:
|
public:
|
||||||
WorkerNode(std::string subscribe_topic, std::string publish_topic, double runtime, int period, int chain, rclcpp::Node::SharedPtr node, rclcpp::CallbackGroup::SharedPtr callback_group = nullptr);
|
WorkerNode(std::string const &subscribe_topic, std::string const &publish_topic,
|
||||||
|
double runtime, int period, int chain,
|
||||||
|
rclcpp::Node::SharedPtr const &node,
|
||||||
|
rclcpp::CallbackGroup::SharedPtr const &callback_group = nullptr);
|
||||||
rclcpp::Subscription<std_msgs::msg::Int32>::SharedPtr sub_;
|
rclcpp::Subscription<std_msgs::msg::Int32>::SharedPtr sub_;
|
||||||
int count_max_ = COUNT_MAX;
|
int count_max_ = COUNT_MAX;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double runtime_;
|
double runtime_;
|
||||||
int period_;
|
int period_;
|
||||||
int chain_;
|
int chain_;
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
<package format="3">
|
<package format="3">
|
||||||
<name>casestudy_tools</name>
|
<name>casestudy_tools</name>
|
||||||
<version>0.0.0</version>
|
<version>0.1.0</version>
|
||||||
<description>TODO: Package description</description>
|
<description>
|
||||||
<maintainer email="kurt4wilson@gmail.com">vboxuser</maintainer>
|
ROS 2 package providing utility tools and common functionality for dynamic executor case studies
|
||||||
<license>TODO: License declaration</license>
|
</description>
|
||||||
|
<maintainer email="kurt4wilson@gmail.com">kurt</maintainer>
|
||||||
|
<license>Apache License 2.0</license>
|
||||||
|
|
||||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,12 @@
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
Experiment::Experiment(ExperimentConfig config) : config(config) {
|
Experiment::Experiment(ExperimentConfig config)
|
||||||
|
: config(std::move(config)) {
|
||||||
_logger = create_logger();
|
_logger = create_logger();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sanity_check_config(ExperimentConfig config) {
|
void sanity_check_config(ExperimentConfig const &config) {
|
||||||
// make sure chain_lengths and node_ids are the same size
|
// make sure chain_lengths and node_ids are the same size
|
||||||
if (config.chain_lengths.size() != config.node_ids.size()) {
|
if (config.chain_lengths.size() != config.node_ids.size()) {
|
||||||
std::cout << "ros_experiment: chain_lengths.size()= "
|
std::cout << "ros_experiment: chain_lengths.size()= "
|
||||||
|
@ -25,6 +26,7 @@ void sanity_check_config(ExperimentConfig config) {
|
||||||
<< " != node_ids.size()= " << config.node_ids.size() << std::endl;
|
<< " != node_ids.size()= " << config.node_ids.size() << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure each chain_lengths is the same size as the corresponding node_ids
|
// make sure each chain_lengths is the same size as the corresponding node_ids
|
||||||
for (uint32_t i = 0; i < config.chain_lengths.size(); i++) {
|
for (uint32_t i = 0; i < config.chain_lengths.size(); i++) {
|
||||||
if (config.chain_lengths[i] != config.node_ids[i].size()) {
|
if (config.chain_lengths[i] != config.node_ids[i].size()) {
|
||||||
|
@ -34,6 +36,7 @@ void sanity_check_config(ExperimentConfig config) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure chain_timer_control is the same size as chain_lengths
|
// make sure chain_timer_control is the same size as chain_lengths
|
||||||
if (config.chain_timer_control.size() != config.chain_lengths.size()) {
|
if (config.chain_timer_control.size() != config.chain_lengths.size()) {
|
||||||
std::cout << "ros_experiment: chain_timer_control.size()= "
|
std::cout << "ros_experiment: chain_timer_control.size()= "
|
||||||
|
@ -42,12 +45,14 @@ void sanity_check_config(ExperimentConfig config) {
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
std::set<int> all_node_ids = std::set<int>();
|
|
||||||
for (uint32_t i = 0; i < config.node_ids.size(); i++) {
|
std::set<int> all_node_ids;
|
||||||
for (uint32_t j = 0; j < config.node_ids[i].size(); j++) {
|
for (auto const &chain : config.node_ids) {
|
||||||
all_node_ids.insert(config.node_ids[i][j]);
|
for (auto const &node_id : chain) {
|
||||||
|
all_node_ids.insert(node_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure we have the right number of node_priorities and node_runtimes
|
// make sure we have the right number of node_priorities and node_runtimes
|
||||||
if (all_node_ids.size() != config.node_priorities.size()) {
|
if (all_node_ids.size() != config.node_priorities.size()) {
|
||||||
std::cout << "ros_experiment: all_node_ids.size()= " << all_node_ids.size()
|
std::cout << "ros_experiment: all_node_ids.size()= " << all_node_ids.size()
|
||||||
|
@ -63,50 +68,46 @@ void sanity_check_config(ExperimentConfig config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Experiment::run(std::atomic<bool> &should_do_task) {
|
std::string Experiment::run(std::atomic<bool>& should_do_task) {
|
||||||
// TODO: split into setup and run, so that run can be re-used in
|
// TODO: split into setup and run, so that run can be re-used in
|
||||||
// Experiment::getRunFunctions
|
// Experiment::getRunFunctions
|
||||||
if (config.node_executor_assignments.size() != 0 ||
|
if (!config.node_executor_assignments.empty()
|
||||||
numExecutorsRequired(config) != 1) {
|
|| numExecutorsRequired(config) != 1) {
|
||||||
std::cerr
|
std::cerr << "called Experiment::run with non-empty node_executor_assignments"
|
||||||
<< "called Experiment::run with node_executor_assignments.size() != 0"
|
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
createExecutors();
|
createExecutors();
|
||||||
|
|
||||||
createNodesAndAssignProperties();
|
createNodesAndAssignProperties();
|
||||||
|
|
||||||
setInitialDeadlines();
|
setInitialDeadlines();
|
||||||
// reset all timers
|
|
||||||
resetTimers();
|
resetTimers();
|
||||||
// in this function (run), there is only one executor, so retrieve it now
|
|
||||||
|
// In this function (run), there is only one executor, so retrieve it now
|
||||||
experiment_executor executor = getExecutorForNode();
|
experiment_executor executor = getExecutorForNode();
|
||||||
if (config.executor_type == "edf" || config.executor_type == "picas") {
|
if (config.executor_type == "edf" || config.executor_type == "picas") {
|
||||||
executor.executor->spin();
|
executor.executor->spin();
|
||||||
} else if (config.executor_type == "default") {
|
} else if (config.executor_type == "default") {
|
||||||
executor.default_executor->spin();
|
executor.default_executor->spin();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "spin done" << std::endl;
|
std::cout << "spin done" << std::endl;
|
||||||
should_do_task.store(false);
|
should_do_task.store(false);
|
||||||
|
|
||||||
return buildLogs();
|
return buildLogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::function<void(std::atomic<bool> &)>>
|
std::vector<std::function<void(std::atomic<bool>&)>>
|
||||||
Experiment::getRunFunctions() {
|
Experiment::getRunFunctions() {
|
||||||
// do pre-run setup
|
// Do pre-run setup
|
||||||
createExecutors();
|
createExecutors();
|
||||||
createNodesAndAssignProperties();
|
createNodesAndAssignProperties();
|
||||||
|
|
||||||
// create a function for each executor
|
// Create a function for each executor
|
||||||
std::vector<std::function<void(std::atomic<bool> &)>> run_functions;
|
std::vector<std::function<void(std::atomic<bool>&)>> run_functions;
|
||||||
for (size_t i = 0; i < numExecutorsRequired(config); i++) {
|
for (size_t i = 0; i < numExecutorsRequired(config); i++) {
|
||||||
// get the executor for this function
|
experiment_executor const executor = executors[i];
|
||||||
experiment_executor executor = executors[i];
|
run_functions.emplace_back(
|
||||||
run_functions.push_back([this,
|
[this, executor](std::atomic<bool>& should_do_task) {
|
||||||
executor](std::atomic<bool> &should_do_task) {
|
|
||||||
if (config.executor_type == "edf" || config.executor_type == "picas") {
|
if (config.executor_type == "edf" || config.executor_type == "picas") {
|
||||||
executor.executor->spin();
|
executor.executor->spin();
|
||||||
} else if (config.executor_type == "default") {
|
} else if (config.executor_type == "default") {
|
||||||
|
@ -117,92 +118,90 @@ Experiment::getRunFunctions() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// do these last, since they're time-sensitive
|
// Do these last since they're time-sensitive
|
||||||
setInitialDeadlines();
|
setInitialDeadlines();
|
||||||
// resetTimers();
|
|
||||||
|
|
||||||
return run_functions;
|
return run_functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Experiment::numExecutorsRequired(const ExperimentConfig &config) {
|
size_t Experiment::numExecutorsRequired(ExperimentConfig const &config) {
|
||||||
// get the number of unique values in the node_executor_assignments
|
if (config.node_executor_assignments.empty()) {
|
||||||
if (config.node_executor_assignments.size() == 0) {
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<int> unique_executors;
|
std::set<int> unique_executors;
|
||||||
for (auto &assignment : config.node_executor_assignments) {
|
for (auto const &assignment : config.node_executor_assignments) {
|
||||||
unique_executors.insert(assignment);
|
unique_executors.insert(assignment);
|
||||||
}
|
}
|
||||||
return unique_executors.size();
|
return unique_executors.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
experiment_executor Experiment::getExecutor(int executor_idx) {
|
experiment_executor Experiment::getExecutor(int const executor_idx) {
|
||||||
return executors[executor_idx];
|
return executors[executor_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Experiment::createExecutors() {
|
void Experiment::createExecutors() {
|
||||||
if (config.node_executor_assignments.size() == 0) {
|
if (config.node_executor_assignments.empty()) {
|
||||||
// create a single executor
|
// Create a single executor
|
||||||
executors.push_back(createSingleExecutor(0));
|
executors.push_back(createSingleExecutor(0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check configuration validity
|
||||||
// depending on config.executor_to_cpu_assignments and config.parallel_mode,
|
// depending on config.executor_to_cpu_assignments and config.parallel_mode,
|
||||||
// we may need to create the host nodes
|
// we may need to create the host nodes
|
||||||
if (config.executor_to_cpu_assignments.size() != 0 && config.parallel_mode) {
|
if (!config.executor_to_cpu_assignments.empty() && config.parallel_mode) {
|
||||||
std::cerr << "executor_to_cpu_assignments not supported for parallel mode"
|
std::cerr << "executor_to_cpu_assignments not supported for parallel mode"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.executor_to_cpu_assignments.size() != 0 &&
|
if (!config.executor_to_cpu_assignments.empty()
|
||||||
config.executor_to_cpu_assignments.size() !=
|
&& config.executor_to_cpu_assignments.size() != numExecutorsRequired(config)) {
|
||||||
numExecutorsRequired(config)) {
|
std::cerr << "executor_to_cpu_assignments.size() != numExecutorsRequired(config)"
|
||||||
std::cerr
|
|
||||||
<< "executor_to_cpu_assignments.size() != numExecutorsRequired(config)"
|
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the required host nodes - re-use any existing nodes
|
// Create the required host nodes - re-use any existing nodes
|
||||||
for (uint i = config.nodes.size();
|
for (uint i = config.nodes.size();
|
||||||
i < config.executor_to_cpu_assignments.size(); i++) {
|
i < config.executor_to_cpu_assignments.size(); i++) {
|
||||||
// create a node for each executor
|
// create a node for each executor
|
||||||
rclcpp::Node::SharedPtr node =
|
auto const node = std::make_shared<rclcpp::Node>(
|
||||||
std::make_shared<rclcpp::Node>("node_" + std::to_string(i));
|
"node_" + std::to_string(i));
|
||||||
config.nodes.push_back(node);
|
config.nodes.push_back(node);
|
||||||
}
|
}
|
||||||
std::cout << "created " << config.nodes.size() << " nodes" << std::endl;
|
std::cout << "created " << config.nodes.size() << " nodes" << std::endl;
|
||||||
|
|
||||||
int num_executors = numExecutorsRequired(config);
|
int const num_executors = numExecutorsRequired(config);
|
||||||
std::cout << "creating " << num_executors << " executors" << std::endl;
|
std::cout << "creating " << num_executors << " executors" << std::endl;
|
||||||
for (int i = 0; i < num_executors; i++) {
|
for (int i = 0; i < num_executors; i++) {
|
||||||
executors.push_back(createSingleExecutor(i));
|
executors.push_back(createSingleExecutor(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
experiment_executor Experiment::createSingleExecutor(uint executor_num) {
|
experiment_executor Experiment::createSingleExecutor(uint const executor_num) {
|
||||||
experiment_executor executor = {nullptr, nullptr, nullptr};
|
experiment_executor executor = {nullptr, nullptr, nullptr};
|
||||||
|
|
||||||
if (config.executor_type == "edf" || config.executor_type == "picas") {
|
if (config.executor_type == "edf" || config.executor_type == "picas") {
|
||||||
executor.strat = std::make_shared<PriorityMemoryStrategy<>>();
|
executor.strat = std::make_shared<priority_executor::PriorityMemoryStrategy<>>();
|
||||||
rclcpp::ExecutorOptions options;
|
rclcpp::ExecutorOptions options;
|
||||||
options.memory_strategy = executor.strat;
|
options.memory_strategy = executor.strat;
|
||||||
|
|
||||||
if (config.parallel_mode) {
|
if (config.parallel_mode) {
|
||||||
executor.executor =
|
executor.executor = std::make_shared<priority_executor::MultithreadTimedExecutor>(
|
||||||
std::make_shared<timed_executor::MultithreadTimedExecutor>(
|
|
||||||
options, "priority_executor", config.cores);
|
options, "priority_executor", config.cores);
|
||||||
} else {
|
} else {
|
||||||
executor.executor =
|
executor.executor = std::make_shared<priority_executor::TimedExecutor>(options);
|
||||||
std::make_shared<timed_executor::TimedExecutor>(options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
executor.executor->prio_memory_strategy_ = executor.strat;
|
executor.executor->prio_memory_strategy_ = executor.strat;
|
||||||
if (config.node_executor_assignments.size() == 0) {
|
|
||||||
// add all nodes to the executor
|
if (config.node_executor_assignments.empty()) {
|
||||||
for (auto &node : config.nodes) {
|
// Add all nodes to the executor
|
||||||
|
for (auto const &node : config.nodes) {
|
||||||
executor.executor->add_node(node);
|
executor.executor->add_node(node);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// add only this executor's nodes
|
// Add only this executor's nodes
|
||||||
executor.executor->add_node(config.nodes[executor_num]);
|
executor.executor->add_node(config.nodes[executor_num]);
|
||||||
}
|
}
|
||||||
} else if (config.executor_type == "default") {
|
} else if (config.executor_type == "default") {
|
||||||
|
@ -210,42 +209,40 @@ experiment_executor Experiment::createSingleExecutor(uint executor_num) {
|
||||||
// executor.default_executor =
|
// executor.default_executor =
|
||||||
// std::make_shared<ROSDefaultMultithreadedExecutor>(rclcpp::ExecutorOptions(),
|
// std::make_shared<ROSDefaultMultithreadedExecutor>(rclcpp::ExecutorOptions(),
|
||||||
// config.cores);
|
// config.cores);
|
||||||
executor.default_executor =
|
executor.default_executor = std::make_shared<rclcpp::executors::MultiThreadedExecutor>(
|
||||||
std::make_shared<rclcpp::executors::MultiThreadedExecutor>(
|
|
||||||
rclcpp::ExecutorOptions(), config.cores);
|
rclcpp::ExecutorOptions(), config.cores);
|
||||||
} else {
|
} else {
|
||||||
executor.default_executor = std::make_shared<ROSDefaultExecutor>();
|
executor.default_executor = std::make_shared<priority_executor::ROSDefaultExecutor>();
|
||||||
}
|
}
|
||||||
if (config.node_executor_assignments.size() == 0) {
|
|
||||||
// add all nodes to the executor
|
if (config.node_executor_assignments.empty()) {
|
||||||
for (auto &node : config.nodes) {
|
// Add all nodes to the executor
|
||||||
|
for (auto const &node : config.nodes) {
|
||||||
executor.default_executor->add_node(node);
|
executor.default_executor->add_node(node);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// add only this executor's nodes
|
// Add only this executor's nodes
|
||||||
executor.default_executor->add_node(config.nodes[executor_num]);
|
executor.default_executor->add_node(config.nodes[executor_num]);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
// RCLCPP_ERROR(node->get_logger(), "Unknown executor type: %s",
|
// RCLCPP_ERROR(node->get_logger(), "Unknown executor type: %s",
|
||||||
// executor_type.c_str());
|
// executor_type.c_str());
|
||||||
return executor;
|
return executor;
|
||||||
}
|
|
||||||
return executor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
experiment_executor Experiment::getExecutorForNode(int node_id) {
|
experiment_executor Experiment::getExecutorForNode(int const node_id) {
|
||||||
if (config.node_executor_assignments.size() == 0) {
|
if (config.node_executor_assignments.empty()) {
|
||||||
return executors[0];
|
return executors[0];
|
||||||
} else {
|
}
|
||||||
int executor_idx = config.node_executor_assignments[node_id];
|
|
||||||
|
int const executor_idx = config.node_executor_assignments[node_id];
|
||||||
std::cout << "node " << node_id << " assigned to executor " << executor_idx
|
std::cout << "node " << node_id << " assigned to executor " << executor_idx
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
return executors[executor_idx];
|
return executors[executor_idx];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Experiment::createNodesAndAssignProperties() {
|
void Experiment::createNodesAndAssignProperties() {
|
||||||
|
|
||||||
// BEGIN SPECIAL CASES
|
// BEGIN SPECIAL CASES
|
||||||
std::vector<std::vector<std::shared_ptr<CaseStudyNode>>> all_nodes;
|
std::vector<std::vector<std::shared_ptr<CaseStudyNode>>> all_nodes;
|
||||||
rclcpp::CallbackGroup::SharedPtr cb_group0;
|
rclcpp::CallbackGroup::SharedPtr cb_group0;
|
||||||
|
@ -272,7 +269,7 @@ void Experiment::createNodesAndAssignProperties() {
|
||||||
|
|
||||||
std::vector<rclcpp::CallbackGroup::SharedPtr> cb_groups;
|
std::vector<rclcpp::CallbackGroup::SharedPtr> cb_groups;
|
||||||
if (config.num_groups != 0 &&
|
if (config.num_groups != 0 &&
|
||||||
config.executor_to_cpu_assignments.size() != 0) {
|
!config.executor_to_cpu_assignments.empty()) {
|
||||||
std::cout << "WARNING: num_groups and executor_to_cpu_assignments are both "
|
std::cout << "WARNING: num_groups and executor_to_cpu_assignments are both "
|
||||||
"set. This is not supported. Ignoring num_groups."
|
"set. This is not supported. Ignoring num_groups."
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
@ -290,7 +287,7 @@ void Experiment::createNodesAndAssignProperties() {
|
||||||
bool first_node = true;
|
bool first_node = true;
|
||||||
for (uint32_t node_chain_idx = 0;
|
for (uint32_t node_chain_idx = 0;
|
||||||
node_chain_idx < config.node_ids[chain_id].size(); node_chain_idx++) {
|
node_chain_idx < config.node_ids[chain_id].size(); node_chain_idx++) {
|
||||||
uint32_t node_id = config.node_ids[chain_id][node_chain_idx];
|
uint32_t const node_id = config.node_ids[chain_id][node_chain_idx];
|
||||||
// has this node been created yet?
|
// has this node been created yet?
|
||||||
if (nodes.find(node_id) != nodes.end()) {
|
if (nodes.find(node_id) != nodes.end()) {
|
||||||
// if it has, then this node exists in another chain
|
// if it has, then this node exists in another chain
|
||||||
|
@ -299,7 +296,7 @@ void Experiment::createNodesAndAssignProperties() {
|
||||||
}
|
}
|
||||||
experiment_executor executor = getExecutorForNode(node_id);
|
experiment_executor executor = getExecutorForNode(node_id);
|
||||||
std::shared_ptr<const void> handle;
|
std::shared_ptr<const void> handle;
|
||||||
ExecutableType type;
|
priority_executor::ExecutableType type;
|
||||||
// is this the first node in the chain?
|
// is this the first node in the chain?
|
||||||
if (node_chain_idx == 0) {
|
if (node_chain_idx == 0) {
|
||||||
// create a timer node
|
// create a timer node
|
||||||
|
@ -313,7 +310,7 @@ void Experiment::createNodesAndAssignProperties() {
|
||||||
cb_group = cb_groups[config.group_memberships[node_id] - 1];
|
cb_group = cb_groups[config.group_memberships[node_id] - 1];
|
||||||
}
|
}
|
||||||
std::shared_ptr<PublisherNode> node;
|
std::shared_ptr<PublisherNode> node;
|
||||||
if (config.node_executor_assignments.size() == 0) {
|
if (config.node_executor_assignments.empty()) {
|
||||||
node = std::make_shared<PublisherNode>(
|
node = std::make_shared<PublisherNode>(
|
||||||
"node_" + std::to_string(node_id), config.node_runtimes[node_id],
|
"node_" + std::to_string(node_id), config.node_runtimes[node_id],
|
||||||
config.chain_periods[chain_id], chain_id, config.nodes[0],
|
config.chain_periods[chain_id], chain_id, config.nodes[0],
|
||||||
|
@ -327,7 +324,7 @@ void Experiment::createNodesAndAssignProperties() {
|
||||||
}
|
}
|
||||||
handle = node->timer_->get_timer_handle();
|
handle = node->timer_->get_timer_handle();
|
||||||
nodes[node_id] = node;
|
nodes[node_id] = node;
|
||||||
type = ExecutableType::TIMER;
|
type = priority_executor::ExecutableType::TIMER;
|
||||||
// this_chain_timer_handle = node->timer_;
|
// this_chain_timer_handle = node->timer_;
|
||||||
std::cout << "chain timer handle set: " << chain_id << std::endl;
|
std::cout << "chain timer handle set: " << chain_id << std::endl;
|
||||||
chain_timer_handles[chain_id] = node->timer_;
|
chain_timer_handles[chain_id] = node->timer_;
|
||||||
|
@ -335,7 +332,7 @@ void Experiment::createNodesAndAssignProperties() {
|
||||||
all_nodes[chain_id].push_back(node);
|
all_nodes[chain_id].push_back(node);
|
||||||
} else {
|
} else {
|
||||||
// create a subscriber node
|
// create a subscriber node
|
||||||
int subscription_id = config.node_ids[chain_id][node_chain_idx - 1];
|
int const subscription_id = config.node_ids[chain_id][node_chain_idx - 1];
|
||||||
rclcpp::CallbackGroup::SharedPtr cb_group;
|
rclcpp::CallbackGroup::SharedPtr cb_group;
|
||||||
if (jiang2022_cb && ((chain_id == 1 && node_chain_idx == 1) ||
|
if (jiang2022_cb && ((chain_id == 1 && node_chain_idx == 1) ||
|
||||||
(chain_id == 2 && node_chain_idx == 1))) {
|
(chain_id == 2 && node_chain_idx == 1))) {
|
||||||
|
@ -352,13 +349,13 @@ void Experiment::createNodesAndAssignProperties() {
|
||||||
|
|
||||||
std::shared_ptr<WorkerNode> node;
|
std::shared_ptr<WorkerNode> node;
|
||||||
if (ours2024_latency) {
|
if (ours2024_latency) {
|
||||||
uint32_t first_node_id = config.node_ids[chain_id][0];
|
uint32_t const first_node_id = config.node_ids[chain_id][0];
|
||||||
node = std::make_shared<WorkerNode>(
|
node = std::make_shared<WorkerNode>(
|
||||||
"node_" + std::to_string(first_node_id),
|
"node_" + std::to_string(first_node_id),
|
||||||
"node_" + std::to_string(node_id), config.node_runtimes[node_id],
|
"node_" + std::to_string(node_id), config.node_runtimes[node_id],
|
||||||
config.chain_periods[chain_id], chain_id, config.nodes[0],
|
config.chain_periods[chain_id], chain_id, config.nodes[0],
|
||||||
cb_group);
|
cb_group);
|
||||||
} else if (config.node_executor_assignments.size() == 0) {
|
} else if (config.node_executor_assignments.empty()) {
|
||||||
node = std::make_shared<WorkerNode>(
|
node = std::make_shared<WorkerNode>(
|
||||||
"node_" + std::to_string(subscription_id),
|
"node_" + std::to_string(subscription_id),
|
||||||
"node_" + std::to_string(node_id), config.node_runtimes[node_id],
|
"node_" + std::to_string(node_id), config.node_runtimes[node_id],
|
||||||
|
@ -374,12 +371,12 @@ void Experiment::createNodesAndAssignProperties() {
|
||||||
}
|
}
|
||||||
handle = node->sub_->get_subscription_handle();
|
handle = node->sub_->get_subscription_handle();
|
||||||
nodes[node_id] = node;
|
nodes[node_id] = node;
|
||||||
type = ExecutableType::SUBSCRIPTION;
|
type = priority_executor::ExecutableType::SUBSCRIPTION;
|
||||||
|
|
||||||
all_nodes[chain_id].push_back(node);
|
all_nodes[chain_id].push_back(node);
|
||||||
}
|
}
|
||||||
if (config.executor_type == "edf") {
|
if (config.executor_type == "edf") {
|
||||||
std::string cb_name = "node_" + std::to_string(node_id) + "_cb";
|
std::string const cb_name = "node_" + std::to_string(node_id) + "_cb";
|
||||||
executor.strat->set_executable_deadline(
|
executor.strat->set_executable_deadline(
|
||||||
handle, config.chain_periods[chain_id], type, chain_id, cb_name);
|
handle, config.chain_periods[chain_id], type, chain_id, cb_name);
|
||||||
// executor.executor->add_node(nodes[node_id]);
|
// executor.executor->add_node(nodes[node_id]);
|
||||||
|
@ -406,7 +403,7 @@ void Experiment::createNodesAndAssignProperties() {
|
||||||
// executor.executor->add_node(nodes[node_id]);
|
// executor.executor->add_node(nodes[node_id]);
|
||||||
executor.strat->set_executable_priority(
|
executor.strat->set_executable_priority(
|
||||||
handle, config.node_priorities[node_id], type,
|
handle, config.node_priorities[node_id], type,
|
||||||
ExecutableScheduleType::CHAIN_AWARE_PRIORITY, chain_id);
|
priority_executor::ExecutableScheduleType::CHAIN_AWARE_PRIORITY, chain_id);
|
||||||
std::cout << "node " << node_id << " has priority "
|
std::cout << "node " << node_id << " has priority "
|
||||||
<< config.node_priorities[node_id] << std::endl;
|
<< config.node_priorities[node_id] << std::endl;
|
||||||
} else if (config.executor_type == "default") {
|
} else if (config.executor_type == "default") {
|
||||||
|
@ -422,29 +419,28 @@ std::string Experiment::buildLogs(std::vector<node_time_logger> node_logs) {
|
||||||
|
|
||||||
output_file << "[" << std::endl;
|
output_file << "[" << std::endl;
|
||||||
// for each node
|
// for each node
|
||||||
for (auto node : nodes) {
|
for (auto const &node : nodes) {
|
||||||
// print the node's logs
|
// print the node's logs
|
||||||
node_time_logger logger = node.second->logger;
|
node_time_logger const logger = node.second->logger;
|
||||||
for (auto &log : *(logger.recorded_times)) {
|
for (auto const &log : *(logger.recorded_times)) {
|
||||||
output_file << "{\"entry\": " << log.first
|
output_file << "{\"entry\": " << log.first
|
||||||
<< ", \"time\": " << log.second << "}," << std::endl;
|
<< ", \"time\": " << log.second << "}," << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.executor_type == "edf" || config.executor_type == "picas") {
|
if (config.executor_type == "edf" || config.executor_type == "picas") {
|
||||||
for (auto &executor : executors) {
|
for (auto const &executor : executors) {
|
||||||
node_time_logger strat_log = executor.strat->logger_;
|
node_time_logger const strat_log = executor.strat->logger_;
|
||||||
for (auto &log : *(strat_log.recorded_times)) {
|
for (auto const &log : *(strat_log.recorded_times)) {
|
||||||
// std::cout << log.first << " " << log.second << std::endl;
|
// std::cout << log.first << " " << log.second << std::endl;
|
||||||
output_file << "{\"entry\": " << log.first
|
output_file << "{\"entry\": " << log.first
|
||||||
<< ", \"time\": " << log.second << "}," << std::endl;
|
<< ", \"time\": " << log.second << "}," << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// executor may be a subclass of RTISTimed, so we can get its logger
|
// executor may be a subclass of RTISTimed, so we can get its logger
|
||||||
if (dynamic_cast<RTISTimed *>(executor.executor.get())) {
|
if (auto const *timed_executor = dynamic_cast<priority_executor::RTISTimed*>(executor.executor.get())) {
|
||||||
node_time_logger executor_log =
|
node_time_logger const executor_log = timed_executor->logger_;
|
||||||
dynamic_cast<RTISTimed *>(executor.executor.get())->logger_;
|
for (auto const &log : *(executor_log.recorded_times)) {
|
||||||
for (auto &log : *(executor_log.recorded_times)) {
|
|
||||||
output_file << "{\"entry\": " << log.first
|
output_file << "{\"entry\": " << log.first
|
||||||
<< ", \"time\": " << log.second << "}," << std::endl;
|
<< ", \"time\": " << log.second << "}," << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -454,14 +450,13 @@ std::string Experiment::buildLogs(std::vector<node_time_logger> node_logs) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// a default executor
|
// a default executor
|
||||||
for (auto &executor : executors) {
|
for (auto const &executor : executors) {
|
||||||
// experimental: executor.default_executor's type is rclcpp::Executor, but
|
// experimental: executor.default_executor's type is rclcpp::Executor, but
|
||||||
// here it's guaranteed to inherit from RTISTimed we can get a logger from
|
// here it's guaranteed to inherit from RTISTimed we can get a logger from
|
||||||
// it. for safety, we check the type first
|
// it. for safety, we check the type first
|
||||||
if (dynamic_cast<RTISTimed *>(executor.default_executor.get())) {
|
if (auto const* timed_executor = dynamic_cast<priority_executor::RTISTimed*>(executor.default_executor.get())) {
|
||||||
node_time_logger default_log =
|
node_time_logger const default_log = timed_executor->logger_;
|
||||||
dynamic_cast<RTISTimed *>(executor.default_executor.get())->logger_;
|
for (auto const &log : *(default_log.recorded_times)) {
|
||||||
for (auto &log : *(default_log.recorded_times)) {
|
|
||||||
output_file << "{\"entry\": " << log.first
|
output_file << "{\"entry\": " << log.first
|
||||||
<< ", \"time\": " << log.second << "}," << std::endl;
|
<< ", \"time\": " << log.second << "}," << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -473,14 +468,14 @@ std::string Experiment::buildLogs(std::vector<node_time_logger> node_logs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add internal logs
|
// add internal logs
|
||||||
for (auto &log_entry : *(_logger.recorded_times)) {
|
for (auto const &log_entry : *(_logger.recorded_times)) {
|
||||||
output_file << "{\"entry\": " << log_entry.first
|
output_file << "{\"entry\": " << log_entry.first
|
||||||
<< ", \"time\": " << log_entry.second << "}," << std::endl;
|
<< ", \"time\": " << log_entry.second << "}," << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add logs from argument
|
// add logs from argument
|
||||||
for (auto &log : node_logs) {
|
for (auto const &log : node_logs) {
|
||||||
for (auto &log_entry : *(log.recorded_times)) {
|
for (auto const &log_entry : *(log.recorded_times)) {
|
||||||
output_file << "{\"entry\": " << log_entry.first
|
output_file << "{\"entry\": " << log_entry.first
|
||||||
<< ", \"time\": " << log_entry.second << "}," << std::endl;
|
<< ", \"time\": " << log_entry.second << "}," << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -495,7 +490,7 @@ std::string Experiment::buildLogs(std::vector<node_time_logger> node_logs) {
|
||||||
void Experiment::setInitialDeadlines() {
|
void Experiment::setInitialDeadlines() {
|
||||||
timespec current_time;
|
timespec current_time;
|
||||||
clock_gettime(CLOCK_MONOTONIC_RAW, ¤t_time);
|
clock_gettime(CLOCK_MONOTONIC_RAW, ¤t_time);
|
||||||
uint64_t millis =
|
uint64_t const millis =
|
||||||
(current_time.tv_sec * 1000UL) + (current_time.tv_nsec / 1000000);
|
(current_time.tv_sec * 1000UL) + (current_time.tv_nsec / 1000000);
|
||||||
// we need a logger to record the first deadline, which is generated here
|
// we need a logger to record the first deadline, which is generated here
|
||||||
// TODO: generate the first deadline in the executor (or node) itself
|
// TODO: generate the first deadline in the executor (or node) itself
|
||||||
|
@ -503,7 +498,7 @@ void Experiment::setInitialDeadlines() {
|
||||||
for (uint32_t chain_index = 0; chain_index < config.chain_lengths.size();
|
for (uint32_t chain_index = 0; chain_index < config.chain_lengths.size();
|
||||||
chain_index++) {
|
chain_index++) {
|
||||||
size_t executor_idx;
|
size_t executor_idx;
|
||||||
if (config.node_executor_assignments.size() == 0) {
|
if (config.node_executor_assignments.empty()) {
|
||||||
executor_idx = 0;
|
executor_idx = 0;
|
||||||
} else {
|
} else {
|
||||||
executor_idx = config.node_executor_assignments[chain_index];
|
executor_idx = config.node_executor_assignments[chain_index];
|
||||||
|
@ -525,7 +520,7 @@ void Experiment::setInitialDeadlines() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Experiment::resetTimers() {
|
void Experiment::resetTimers() {
|
||||||
for (auto timer : chain_timer_handles) {
|
for (auto const &timer : chain_timer_handles) {
|
||||||
std::cout << "resetting timer " << timer.first << std::endl;
|
std::cout << "resetting timer " << timer.first << std::endl;
|
||||||
if (timer.second == nullptr) {
|
if (timer.second == nullptr) {
|
||||||
std::cout << "timer is null" << std::endl;
|
std::cout << "timer is null" << std::endl;
|
||||||
|
@ -542,14 +537,14 @@ void Experiment::resetTimers() {
|
||||||
// std::this_thread::sleep_for(std::chrono::seconds(1));
|
// std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Experiment::writeLogsToFile(std::string file_name,
|
void Experiment::writeLogsToFile(std::string const &file_name,
|
||||||
std::string experiment_name) {
|
std::string const &experiment_name) {
|
||||||
std::ofstream output_file;
|
std::ofstream output_file;
|
||||||
std::string name = experiment_name;
|
std::string const name = experiment_name;
|
||||||
// make sure results directory exists. we don't have access to std::filesystem
|
// make sure results directory exists. we don't have access to std::filesystem
|
||||||
// here :(
|
// here :(
|
||||||
std::string command = "mkdir -p results/" + name;
|
std::string const command = "mkdir -p results/" + name;
|
||||||
int result = system(command.c_str());
|
int const result = system(command.c_str());
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
std::cout << "could not create results directory: " << result << ": "
|
std::cout << "could not create results directory: " << result << ": "
|
||||||
<< strerror(errno) << std::endl;
|
<< strerror(errno) << std::endl;
|
||||||
|
@ -563,8 +558,8 @@ void Experiment::writeLogsToFile(std::string file_name,
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
int do_other_task(double work_time, uint period,
|
int do_other_task(double const work_time, uint const period,
|
||||||
std::atomic<bool> &should_do_task) {
|
std::atomic<bool>& should_do_task) {
|
||||||
if (work_time == 0) {
|
if (work_time == 0) {
|
||||||
std::cout << "other task: work time is 0, exiting" << std::endl;
|
std::cout << "other task: work time is 0, exiting" << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -573,7 +568,7 @@ int do_other_task(double work_time, uint period,
|
||||||
cpu_set_t cpuset;
|
cpu_set_t cpuset;
|
||||||
CPU_ZERO(&cpuset);
|
CPU_ZERO(&cpuset);
|
||||||
CPU_SET(0, &cpuset);
|
CPU_SET(0, &cpuset);
|
||||||
int result = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
|
int const result = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
std::cout << "other task: could not set affinity: " << result << ": "
|
std::cout << "other task: could not set affinity: " << result << ": "
|
||||||
<< strerror(errno) << std::endl;
|
<< strerror(errno) << std::endl;
|
||||||
|
@ -593,7 +588,7 @@ int do_other_task(double work_time, uint period,
|
||||||
do {
|
do {
|
||||||
timespec current_time;
|
timespec current_time;
|
||||||
clock_gettime(CLOCK_MONOTONIC_RAW, ¤t_time);
|
clock_gettime(CLOCK_MONOTONIC_RAW, ¤t_time);
|
||||||
uint64_t millis_start = (current_time.tv_sec * (uint64_t)1000) +
|
uint64_t const millis_start = (current_time.tv_sec * (uint64_t)1000) +
|
||||||
(current_time.tv_nsec / 1000000);
|
(current_time.tv_nsec / 1000000);
|
||||||
|
|
||||||
// do work
|
// do work
|
||||||
|
@ -602,9 +597,9 @@ int do_other_task(double work_time, uint period,
|
||||||
|
|
||||||
// sleep until next period
|
// sleep until next period
|
||||||
clock_gettime(CLOCK_MONOTONIC_RAW, ¤t_time);
|
clock_gettime(CLOCK_MONOTONIC_RAW, ¤t_time);
|
||||||
uint64_t millis_end = (current_time.tv_sec * (uint64_t)1000) +
|
uint64_t const millis_end = (current_time.tv_sec * (uint64_t)1000) +
|
||||||
(current_time.tv_nsec / 1000000);
|
(current_time.tv_nsec / 1000000);
|
||||||
uint64_t sleep_time = period - (millis_end - millis_start);
|
uint64_t const sleep_time = period - (millis_end - millis_start);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
|
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
|
||||||
} while (should_do_task);
|
} while (should_do_task);
|
||||||
std::cout << "other task done" << std::endl;
|
std::cout << "other task done" << std::endl;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "casestudy_tools/primes_workload.hpp"
|
#include "casestudy_tools/primes_workload.hpp"
|
||||||
|
|
||||||
|
|
||||||
void DummyLoadCalibration::setCalibration(int calib) {
|
void DummyLoadCalibration::setCalibration(int const calib) {
|
||||||
calibration.store(calib, std::memory_order_relaxed);
|
calibration.store(calib, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,12 +20,12 @@ void calibrate_dummy_load()
|
||||||
for (int iteration = 0; iteration < MAX_ITERATIONS; ++iteration)
|
for (int iteration = 0; iteration < MAX_ITERATIONS; ++iteration)
|
||||||
{
|
{
|
||||||
// Measure the time taken for the dummy load
|
// Measure the time taken for the dummy load
|
||||||
auto start_time = std::chrono::high_resolution_clock::now();
|
auto const start_time = std::chrono::high_resolution_clock::now();
|
||||||
dummy_load(TARGET_DURATION_MS); // intended workload
|
dummy_load(TARGET_DURATION_MS); // intended workload
|
||||||
auto end_time = std::chrono::high_resolution_clock::now();
|
auto const end_time = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
// Calculate duration in microseconds
|
// Calculate duration in microseconds
|
||||||
auto duration_us =
|
auto const duration_us =
|
||||||
std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
|
std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
|
||||||
|
|
||||||
std::cout << "[Calibration] Iteration: " << iteration
|
std::cout << "[Calibration] Iteration: " << iteration
|
||||||
|
@ -51,12 +51,12 @@ void calibrate_dummy_load()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dummy_load(int load_ms)
|
void dummy_load(int const load_ms)
|
||||||
{
|
{
|
||||||
const int calib_factor = DummyLoadCalibration::getCalibration();
|
int const calib_factor = DummyLoadCalibration::getCalibration();
|
||||||
|
|
||||||
// calibration factor corresponds to 100ms load, scale it
|
// calibration factor corresponds to 100ms load, scale it
|
||||||
const int scaled_factor = calib_factor * load_ms / 100;
|
int const scaled_factor = calib_factor * load_ms / 100;
|
||||||
|
|
||||||
volatile int dummy_var = 0;
|
volatile int dummy_var = 0;
|
||||||
constexpr int DUMMY_LOAD_ITER = 100'000;
|
constexpr int DUMMY_LOAD_ITER = 100'000;
|
||||||
|
|
|
@ -6,71 +6,68 @@
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <rclcpp/logger.hpp>
|
#include <rclcpp/logger.hpp>
|
||||||
|
|
||||||
std::string CaseStudyNode::get_name() { return name; }
|
std::string CaseStudyNode::get_name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
#define BUFFER_LENGTH 0
|
#define BUFFER_LENGTH 0
|
||||||
|
|
||||||
rclcpp::QoS get_qos()
|
rclcpp::QoS get_qos() {
|
||||||
{
|
if (BUFFER_LENGTH == 0) {
|
||||||
if (BUFFER_LENGTH == 0)
|
|
||||||
{
|
|
||||||
return rclcpp::QoS(rclcpp::KeepAll());
|
return rclcpp::QoS(rclcpp::KeepAll());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return rclcpp::QoS(rclcpp::KeepLast(BUFFER_LENGTH));
|
return rclcpp::QoS(rclcpp::KeepLast(BUFFER_LENGTH));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void timespec_diff(timespec *start, timespec *end, timespec *result)
|
void timespec_diff(timespec const* start, timespec const* end, timespec* result) {
|
||||||
{
|
if ((end->tv_nsec - start->tv_nsec) < 0) {
|
||||||
if ((end->tv_nsec - start->tv_nsec) < 0)
|
|
||||||
{
|
|
||||||
result->tv_sec = end->tv_sec - start->tv_sec - 1;
|
result->tv_sec = end->tv_sec - start->tv_sec - 1;
|
||||||
result->tv_nsec = 1000000000 + end->tv_nsec - start->tv_nsec;
|
result->tv_nsec = 1000000000 + end->tv_nsec - start->tv_nsec;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
result->tv_sec = end->tv_sec - start->tv_sec;
|
result->tv_sec = end->tv_sec - start->tv_sec;
|
||||||
result->tv_nsec = end->tv_nsec - start->tv_nsec;
|
result->tv_nsec = end->tv_nsec - start->tv_nsec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PublisherNode::PublisherNode(std::string publish_topic, double runtime,
|
PublisherNode::PublisherNode(
|
||||||
int period, int chain,
|
std::string const &publish_topic,
|
||||||
rclcpp::Node::SharedPtr node,
|
double const runtime,
|
||||||
rclcpp::CallbackGroup::SharedPtr callback_group)
|
int const period,
|
||||||
: CaseStudyNode(publish_topic, node), runtime_(runtime), period_(period),
|
int const chain,
|
||||||
chain_(chain)
|
rclcpp::Node::SharedPtr const &node,
|
||||||
{
|
rclcpp::CallbackGroup::SharedPtr const &callback_group)
|
||||||
|
: CaseStudyNode(publish_topic, node)
|
||||||
|
, runtime_(runtime)
|
||||||
|
, period_(period)
|
||||||
|
, chain_(chain) {
|
||||||
|
|
||||||
logger = create_logger();
|
logger = create_logger();
|
||||||
pub_ = node->create_publisher<std_msgs::msg::Int32>(publish_topic, get_qos());
|
pub_ = node->create_publisher<std_msgs::msg::Int32>(publish_topic, get_qos());
|
||||||
auto timer_callback = [this]() -> void
|
|
||||||
{
|
auto const timer_callback = [this]() -> void {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << "{\"operation\": \"start_work\", \"chain\": " << chain_
|
ss << "{\"operation\": \"start_work\", \"chain\": " << chain_
|
||||||
<< ", \"node\": \"" << get_name() << "\", \"count\": " << count_max_;
|
<< ", \"node\": \"" << get_name() << "\", \"count\": " << count_max_;
|
||||||
std::chrono::nanoseconds time_until_trigger = timer_->time_until_trigger();
|
|
||||||
std::chrono::microseconds time_until_trigger_us =
|
std::chrono::nanoseconds const time_until_trigger = timer_->time_until_trigger();
|
||||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
std::chrono::microseconds const time_until_trigger_us =
|
||||||
time_until_trigger);
|
std::chrono::duration_cast<std::chrono::microseconds>(time_until_trigger);
|
||||||
|
|
||||||
ss << ", \"next_release_us\": " << time_until_trigger_us.count() << "}";
|
ss << ", \"next_release_us\": " << time_until_trigger_us.count() << "}";
|
||||||
log_entry(logger, ss.str());
|
log_entry(logger, ss.str());
|
||||||
// nth_prime_silly(runtime_);
|
|
||||||
double used_runtime = runtime_;
|
double used_runtime = runtime_;
|
||||||
if (use_random_runtime)
|
if (use_random_runtime) {
|
||||||
{
|
if (rand() % 100 < runtime_over_chance * 100) {
|
||||||
if (rand() % 100 < runtime_over_chance * 100)
|
|
||||||
{
|
|
||||||
used_runtime *= over_runtime_scale;
|
used_runtime *= over_runtime_scale;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
used_runtime *= normal_runtime_scale;
|
used_runtime *= normal_runtime_scale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RCLCPP_INFO(rclcpp::get_logger(get_name()), "running %s with runtime %f",
|
// RCLCPP_INFO(rclcpp::get_logger(get_name()), "running %s with runtime %f",
|
||||||
// get_name().c_str(), used_runtime);
|
// get_name().c_str(), used_runtime);
|
||||||
|
|
||||||
struct timespec start_time;
|
struct timespec start_time;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||||
|
|
||||||
|
@ -81,14 +78,15 @@ PublisherNode::PublisherNode(std::string publish_topic, double runtime,
|
||||||
pub_->publish(msg);
|
pub_->publish(msg);
|
||||||
// TEST: can we take more than one msg from DDS?
|
// TEST: can we take more than one msg from DDS?
|
||||||
// pub_->publish(msg);
|
// pub_->publish(msg);
|
||||||
|
|
||||||
ss.str("");
|
ss.str("");
|
||||||
ss << "{\"operation\": \"end_work\", \"chain\": " << chain_
|
ss << "{\"operation\": \"end_work\", \"chain\": " << chain_
|
||||||
<< ", \"node\": \"" << get_name() << "\", \"count\": " << count_max_ << ", \"target_runtime\": " << runtime_
|
<< ", \"node\": \"" << get_name() << "\", \"count\": " << count_max_
|
||||||
<< "}";
|
<< ", \"target_runtime\": " << runtime_ << "}";
|
||||||
log_entry(logger, ss.str());
|
log_entry(logger, ss.str());
|
||||||
|
|
||||||
count_max_--;
|
count_max_--;
|
||||||
if (count_max_ == 0)
|
if (count_max_ == 0) {
|
||||||
{
|
|
||||||
rclcpp::shutdown();
|
rclcpp::shutdown();
|
||||||
}
|
}
|
||||||
/* struct timespec end_time;
|
/* struct timespec end_time;
|
||||||
|
@ -100,29 +98,36 @@ PublisherNode::PublisherNode(std::string publish_topic, double runtime,
|
||||||
RCLCPP_INFO(rclcpp::get_logger(get_name()), "done running %s, took %lu ms",
|
RCLCPP_INFO(rclcpp::get_logger(get_name()), "done running %s, took %lu ms",
|
||||||
get_name().c_str(), diff_ms); */
|
get_name().c_str(), diff_ms); */
|
||||||
};
|
};
|
||||||
if (callback_group != nullptr)
|
|
||||||
{
|
if (callback_group != nullptr) {
|
||||||
callback_group_ = callback_group;
|
callback_group_ = callback_group;
|
||||||
|
} else {
|
||||||
|
callback_group_ = node->create_callback_group(
|
||||||
|
rclcpp::CallbackGroupType::Reentrant);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
timer_ = node->create_wall_timer(
|
||||||
callback_group_ =
|
std::chrono::milliseconds(period),
|
||||||
node->create_callback_group(rclcpp::CallbackGroupType::Reentrant);
|
timer_callback,
|
||||||
}
|
callback_group_);
|
||||||
timer_ = node->create_wall_timer(std::chrono::milliseconds(period),
|
|
||||||
timer_callback, callback_group_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkerNode::WorkerNode(std::string subscribe_topic, std::string publish_topic,
|
WorkerNode::WorkerNode(
|
||||||
double runtime, int period, int chain,
|
std::string const &subscribe_topic,
|
||||||
rclcpp::Node::SharedPtr node,
|
std::string const &publish_topic,
|
||||||
rclcpp::CallbackGroup::SharedPtr callback_group)
|
double const runtime,
|
||||||
: CaseStudyNode(publish_topic, node), runtime_(runtime), period_(period),
|
int const period,
|
||||||
chain_(chain)
|
int const chain,
|
||||||
{
|
rclcpp::Node::SharedPtr const &node,
|
||||||
|
rclcpp::CallbackGroup::SharedPtr const &callback_group)
|
||||||
|
: CaseStudyNode(publish_topic, node)
|
||||||
|
, runtime_(runtime)
|
||||||
|
, period_(period)
|
||||||
|
, chain_(chain) {
|
||||||
|
|
||||||
logger = create_logger();
|
logger = create_logger();
|
||||||
auto callback = [this](const std_msgs::msg::Int32::SharedPtr msg) -> void
|
|
||||||
{
|
auto const callback = [this](std_msgs::msg::Int32::SharedPtr const msg) -> void {
|
||||||
// prevent unused variable warning
|
// prevent unused variable warning
|
||||||
(void)msg;
|
(void)msg;
|
||||||
|
|
||||||
|
@ -133,42 +138,42 @@ WorkerNode::WorkerNode(std::string subscribe_topic, std::string publish_topic,
|
||||||
log_entry(logger, ss.str());
|
log_entry(logger, ss.str());
|
||||||
// RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
|
// RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
|
||||||
// nth_prime_silly(runtime_);
|
// nth_prime_silly(runtime_);
|
||||||
|
|
||||||
double used_runtime = runtime_;
|
double used_runtime = runtime_;
|
||||||
if (use_random_runtime)
|
if (use_random_runtime) {
|
||||||
{
|
if (rand() % 100 < runtime_over_chance * 100) {
|
||||||
if (rand() % 100 < runtime_over_chance * 100)
|
|
||||||
{
|
|
||||||
used_runtime *= over_runtime_scale;
|
used_runtime *= over_runtime_scale;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
used_runtime *= normal_runtime_scale;
|
used_runtime *= normal_runtime_scale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dummy_load(used_runtime);
|
dummy_load(used_runtime);
|
||||||
// RCLCPP_INFO(this->get_logger(), "Result: %d", result);
|
// RCLCPP_INFO(this->get_logger(), "Result: %d", result);
|
||||||
auto new_msg = std_msgs::msg::Int32();
|
auto new_msg = std_msgs::msg::Int32();
|
||||||
new_msg.data = msg->data;
|
new_msg.data = msg->data;
|
||||||
pub_->publish(new_msg);
|
pub_->publish(new_msg);
|
||||||
|
|
||||||
ss.str("");
|
ss.str("");
|
||||||
ss << "{\"operation\": \"end_work\", \"chain\": " << chain_
|
ss << "{\"operation\": \"end_work\", \"chain\": " << chain_
|
||||||
<< ", \"node\": \"" << get_name() << "\", \"count\": " << msg->data
|
<< ", \"node\": \"" << get_name() << "\", \"count\": " << msg->data
|
||||||
<< "}";
|
<< "}";
|
||||||
log_entry(logger, ss.str());
|
log_entry(logger, ss.str());
|
||||||
};
|
};
|
||||||
|
|
||||||
rclcpp::SubscriptionOptions sub_options;
|
rclcpp::SubscriptionOptions sub_options;
|
||||||
if (callback_group)
|
if (callback_group) {
|
||||||
{
|
|
||||||
callback_group_ = callback_group;
|
callback_group_ = callback_group;
|
||||||
}
|
} else {
|
||||||
else
|
callback_group_ = node->create_callback_group(
|
||||||
{
|
rclcpp::CallbackGroupType::Reentrant);
|
||||||
callback_group_ =
|
|
||||||
node->create_callback_group(rclcpp::CallbackGroupType::Reentrant);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub_options.callback_group = callback_group_;
|
sub_options.callback_group = callback_group_;
|
||||||
sub_ = node->create_subscription<std_msgs::msg::Int32>(
|
sub_ = node->create_subscription<std_msgs::msg::Int32>(
|
||||||
subscribe_topic, get_qos(), callback, sub_options);
|
subscribe_topic,
|
||||||
|
get_qos(),
|
||||||
|
callback,
|
||||||
|
sub_options);
|
||||||
pub_ = node->create_publisher<std_msgs::msg::Int32>(publish_topic, get_qos());
|
pub_ = node->create_publisher<std_msgs::msg::Int32>(publish_topic, get_qos());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a2741afc9a204758ec047cb25c6a85c92b3298a6
|
Subproject commit 91c2a42d6bc0130cec32447373a0fec0def34a26
|
Loading…
Add table
Add a link
Reference in a new issue