roboticslab-uc3m / yarp-devices

A place for YARP devices
https://robots.uc3m.es/yarp-devices/
9 stars 7 forks source link

Monitor workers could live in a shared thread instance #257

Closed PeterBowman closed 3 years ago

PeterBowman commented 3 years ago

A monitor worker is responsible for periodically checking the boot-up and heartbeat signals broadcast by an iPOS drive. In our code, we conceived said worker as a callback function orquestrated by yarp::os::Timer:

https://github.com/roboticslab-uc3m/yarp-devices/blob/d8f244ce3fd8d909a366c7675919f91bd3879519/libraries/YarpPlugins/TechnosoftIpos/DeviceDriverImpl.cpp#L170

The third parameter to the constructor is true. This means each TechnosoftIpos device instance creates its own thread for this specific purpose. If it's set to false, the callback is called from within a shared, singleton PeriodicThread instance common to all timers configured in this manner.

In other words, if we ever manage to run a whole-body 28-dof launchCanBus, switching from true to false would mean squashing 28 independent threads into only one. This issue is meant for investigating the pros and cons, especially regarding CPU resource usage.

PeterBowman commented 3 years ago

Test code:

#include <algorithm>
#include <memory>
#include <vector>

#include <yarp/os/LogStream.h>
#include <yarp/os/Network.h>
#include <yarp/os/Timer.h>

int main()
{
    yarp::os::Network::initMinimum(yarp::os::YARP_CLOCK_SYSTEM);
    std::vector<std::unique_ptr<yarp::os::Timer>> timers;

    for (auto i = 0; i < 30; i++)
    {
        auto period = 0.01 * (i + 1); // min: 0.01 s, max: 0.3 s

        auto timer = std::make_unique<yarp::os::Timer>(yarp::os::TimerSettings(period), [i](const auto & event)
            {
                volatile long long int result = 1;
                for (int j = 0; j < (i + 1) * 10000; j++) { result += j; }
                yInfo() << "timer" << i + 1 << "reporting count" << event.runCount << "and result" << result;
                return true;
            },
            false); // true: multi-thread, false: mono-thread

        timer->start();
        timers.push_back(std::move(timer));
    }

    yarp::os::SystemClock::delaySystem(20.0);
    std::for_each(timers.cbegin(), timers.cend(), [](const auto & timer) { timer->stop(); });
    yarp::os::Network::finiMinimum();
    return 0;
}

This creates 30 timers with distinct periods and performs some CPU-intensive operation on each step. Results on a 16-core CPU:

There is not much difference as fas as I can tell. I have not tested timer accuracy in the mono-thread scenario, but this is not critical. I'm therefore prone to switch to mono-thread (third parameter equal to false), note we are already using plenty of threads elsewhere.

PeterBowman commented 3 years ago

By the way, YARP 3.3.3 is required for mono-thread because of https://github.com/robotology/yarp/pull/1993. We are safe, though.