I'm running a python script from C++. I first set up the python script (load module and instantiate class), then call one of the methods of the class N (~1000000) times and measure how much time each method call took using std::chrono::high_resolution_clock
and std::chrono::duration_cast
. However, interestingly, the duration of the method call seems to be either roughly 50 microseconds OR roughly 100 microseconds (seen in Fig 1. below). Moreover, the durations seems to periodic (seen in Fig 2. below, only a part of data pictured).
What could be the cause of this? The method I call from my python code looks like this:
def data_received(self, data, time_us, first_sample_of_experiment):
self.eeg_data_index += 1
c3 = data[4]
others = [data[20], data[22], data[24], data[26]]
filtered = self.average(c3, others)
self.data.append(filtered)
if len(self.data) > 20:
self.data.pop(0)
signal = self.peak_detection.thresholding_algo(c3)
if signal == 0 and not self.peak_over:
self.peak_over = True
peak = signal != 0
if peak and self.peak_over:
self.peak_over = False
self.peak_at = self.eeg_data_index
self.peaks_detected += 1
return [charge_event, charge_event]
Where peak_detection is a peak detection algorithm adapted from here and looks like this:
import numpy as np
class RealtimePeakDetection:
def __init__(self, array, lag, threshold, influence):
self.y = list(array)
self.length = len(self.y)
self.lag = lag
self.threshold = threshold
self.influence = influence
self.signals = [0] * len(self.y)
self.filteredY = np.array(self.y).tolist()
self.avgFilter = [0] * len(self.y)
self.stdFilter = [0] * len(self.y)
self.avgFilter[self.lag - 1] = np.mean(self.y[0:self.lag]).tolist()
self.stdFilter[self.lag - 1] = np.std(self.y[0:self.lag]).tolist()
def thresholding_algo(self, new_value):
i = len(self.y) - 1
self.y.append(new_value)
self.signals += [0]
self.filteredY += [0]
self.avgFilter += [0]
self.stdFilter += [0]
if len(self.y) > self.length:
self.y.pop(0)
if len(self.signals) > self.length:
self.signals.pop(0)
if len(self.filteredY) > self.length:
self.filteredY.pop(0)
if len(self.avgFilter) > self.length:
self.avgFilter.pop(0)
if len(self.stdFilter) > self.length:
self.stdFilter.pop(0)
if abs(self.y[i] - self.avgFilter[i - 1]) > (self.threshold * self.stdFilter[i - 1]):
if self.y[i] > self.avgFilter[i - 1]:
self.signals[i] = 1
else:
self.signals[i] = -1
self.filteredY[i] = self.influence * self.y[i] + (1 - self.influence) * self.filteredY[i - 1]
self.avgFilter[i] = np.mean(self.filteredY[(i - self.lag):i])
self.stdFilter[i] = np.std(self.filteredY[(i - self.lag):i])
else:
self.signals[i] = 0
self.filteredY[i] = self.y[i]
self.avgFilter[i] = np.mean(self.filteredY[(i - self.lag):i])
self.stdFilter[i] = np.std(self.filteredY[(i - self.lag):i])
return self.signals[i]
I wonder what could cause this kind of periodic durations of the method calls? I'm using Linux PREEMPT_RT kernel 5.15.55-rt48 and my C++ program has RTPRIO -98.
My C++ program is a ROS node with the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.8)
project(data_processor)
if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif ()
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(mtms_interfaces REQUIRED)
find_package(fpga_interfaces REQUIRED)
set(MATLAB_FIND_DEBUG true)
# MATLAB
find_package(Matlab)
if (Matlab_FOUND)
# Following 5 lines taken from https://stackoverflow.com/questions/8880802/cmake-linking-shared-library
# Fixes runtime error "error while loading shared libraries: libMatlabDataArray.so: cannot open shared object file: No such file or directory"
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib64")
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib64")
set(LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:${Matlab_ROOT_DIR}/extern/bin/glnxa64:${Matlab_ROOT_DIR}/sys/os/glnxa64)
include_directories(${Matlab_ROOT_DIR}/extern/include/)
link_directories(${Matlab_ROOT_DIR}/extern/bin/glnxa64)
else ()
message(STATUS "MATLAB NOT FOUND")
endif ()
add_executable(
data_processor
src/data_processor.cpp
src/processor.cpp
src/headers/processor.h
src/python_processor.cpp
src/matlab_processor.cpp
src/compiled_matlab_processor.cpp
src/matlab_processor_interface.cpp
src/headers/matlab_processor.h
src/headers/python_processor.h
src/headers/compiled_matlab_processor.h
src/headers/scheduling_utils.h
src/headers/scheduling_utils.cpp
src/headers/matlab_processor_interface.h
src/headers/fpga_event.h
src/headers/data_processor.h
src/headers/matlab_helpers.h
src/matlab_helpers.cpp)
target_include_directories(data_processor
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib>
$<INSTALL_INTERFACE:lib>)
ament_target_dependencies(data_processor rclcpp std_msgs mtms_interfaces fpga_interfaces)
if (Matlab_FOUND)
# MATLAB, Linker to libMatlabEngine in link_directories
target_link_libraries(data_processor MatlabDataArray)
target_link_libraries(data_processor MatlabEngine)
endif ()
# Python
find_package(PythonLibs)
if (PYTHONLIBS_FOUND)
message(STATUS "Python found")
include_directories(${PYTHON_INCLUDE_DIRS})
target_link_libraries(data_processor ${PYTHON_LIBRARIES})
else ()
message(STATUS "Python not found")
endif ()
install(TARGETS
data_processor
DESTINATION lib/${PROJECT_NAME}
)
install(
DIRECTORY launch
DESTINATION share/${PROJECT_NAME}
)
if (BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif ()
ament_package()
I simply use colcon build --packages-select <ros package> --cmake-args -DCMAKE_BUILD_TYPE=Release
to build the project. The compiler used is GNU 9.4.0.