0

I'm building a simulator to test student code for a very simple robot. I need to run two functions(to update robot sensors and robot position) on separate threads at regular time intervals. My current implementation is highly processor inefficient because it has a thread dedicated to simply incrementing numbers to keep track of the position in the code. My recent theory is that I may be able to use sleep to give the time delay between updating value of the sensor and robot position. My first question is: is this efficient? Second: Is there any way to do a simple thing but measure clock cycles instead of seconds?

meitme
  • 13
  • 1
  • 3
  • Can you explain in more detail what you are hoping to do? Why do you have different threads for sensors and position? Are the two threads dependent on each other in any way? Unless the test code is doing some really heavy lifting, I doubt that you need to be seriously concerned with optimizing the efficiency of the test code. – Zachary Miller Oct 27 '14 at 18:03

4 Answers4

1

Putting a thread to sleep by waiting on a mutex-like object is generally efficient. A common pattern involves waiting on a mutex with a timeout. When the timeout is reached, the interval is up. When the mutex is releaed, it is the signal for the thread to terminate.

Pseudocode:

void threadMethod() {
  for(;;) {
    bool signalled = this->mutex.wait(1000);
    if(signalled) {
      break; // Signalled, owners wants us to terminate
    }

    // Timeout, meaning our wait time is up
    doPeriodicAction();
  }
}

void start() {
  this->mutex.enter();
  this->thread.start(threadMethod);
}

void stop() {
  this->mutex.leave();
  this->thread.join();
}

On Windows systems, timeouts are generally specified in milliseconds and are accurate to roughly within 16 milliseconds (timeBeginPeriod() may be able to improve this). I do not know of a CPU cycle-triggered synchronization primitive. There are lightweight mutexes called "critical sections" that spin the CPU for a few thousand cycles before delegating to the OS thread scheduler. Within this time they are fairly accurate.

On Linux systems the accuracy may be a bit higher (high frequency timer or tickless kernel) and in addition to mutexes, there are "futexes" (fast mutex) which are similar to Windows' critical sections.


I'm not sure I grasped what you're trying to achieve, but if you want to test student code, you might want to use a virtual clock and control the passing of time yourself. For example by calling a processInputs() and a decideMovements() method that the students have to provide. After each call, 1 time slot is up.

Cygon
  • 9,444
  • 8
  • 42
  • 50
1

This C++11 code uses std::chrono::high_resolution_clock to measure subsecond timing, and std::thread to run three threads. The std::this_thread::sleep_for() function is used to sleep for a specified time.

#include <iostream>
#include <thread>
#include <vector>
#include <chrono>

void seconds()
{
    using namespace std::chrono;

    high_resolution_clock::time_point t1, t2; 
    for (unsigned i=0; i<10; ++i) {
        std::cout << i << "\n";
        t1 = high_resolution_clock::now();
        std::this_thread::sleep_for(std::chrono::seconds(1)); 
        t2 = high_resolution_clock::now();
        duration<double> elapsed = duration_cast<duration<double> >(t2-t1);
        std::cout << "\t( " << elapsed.count() << " seconds )\n";
    }
}
int main()
{
    std::vector<std::thread> t;

    t.push_back(std::thread{[](){ 
        std::this_thread::sleep_for(std::chrono::seconds(3)); 
        std::cout << "awoke after 3\n"; }});
    t.push_back(std::thread{[](){ 
        std::this_thread::sleep_for(std::chrono::seconds(7)); 
        std::cout << "awoke after 7\n"; }});
    t.push_back(std::thread{seconds});

    for (auto &thr : t)
        thr.join();
}

It's hard to know whether this meets your needs because there are a lot of details missing from the question. Under Linux, compile with:

g++ -Wall -Wextra -pedantic -std=c++11 timers.cpp -o timers -lpthread

Output on my machine:

0
    ( 1.00014 seconds)
1
    ( 1.00014 seconds)
2
awoke after 3
    ( 1.00009 seconds)
3
    ( 1.00015 seconds)
4
    ( 1.00011 seconds)
5
    ( 1.00013 seconds)
6
awoke after 7
    ( 1.0001 seconds)
7
    ( 1.00015 seconds)
8
    ( 1.00014 seconds)
9
    ( 1.00013 seconds)

Other C++11 standard features that may be of interest include timed_mutex and promise/future.

Edward
  • 6,964
  • 2
  • 29
  • 55
0

Yes your theory is correct. You can use sleep to put some delay between execution of a function by thread. Efficiency depends on how wide you can choose that delay to get desired result. You have to explain details of your implementation. For e.g we don't know whether two threads are dependent ( in that case you have to take care of synchronization which would blow up some cycles ).

ravi
  • 10,994
  • 1
  • 18
  • 36
  • You can use `sleep()`, yes. But probably in most cases that's a pretty bad idea (you might ending up busy waiting). The more appropriate approach is to use timed semaphores/condition variables. – πάντα ῥεῖ Oct 27 '14 at 18:22
0

Here's the one way to do it. I'm using C++11, thread, atomics and high precision clock. The scheduler will callback a function that takes dt seconds which is time elapsed since last call. The loop can be stopped by calling stop() method of if callback function returns false.

Scheduler code

#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
#include <system_error>

class ScheduledExecutor {
public:
    ScheduledExecutor()
    {}
    ScheduledExecutor(const std::function<bool(double)>& callback, double period)
    {
        initialize(callback, period);
    }
    void initialize(const std::function<bool(double)>& callback, double period)
    {
        callback_ = callback;
        period_ = period;
        keep_running_ = false;
    }

    void start()
    {
        keep_running_ = true;
        sleep_time_sum_ = 0;
        period_count_ = 0;
        th_ = std::thread(&ScheduledExecutor::executorLoop, this);
    }

    void stop()
    {
        keep_running_ = false;
        try {
            th_.join();
        }
        catch(const std::system_error& /* e */)
        { }
    }

    double getSleepTimeAvg()
    {
        //TODO: make this function thread safe by using atomic types
        //right now this is not implemented for performance and that
        //return of this function is purely informational/debugging purposes
        return sleep_time_sum_ / period_count_;
    }

    unsigned long getPeriodCount()
    {
        return period_count_;
    }

private:
    typedef std::chrono::high_resolution_clock clock;
    template <typename T>
    using duration = std::chrono::duration<T>;

    void executorLoop()
    {
        clock::time_point call_end = clock::now();
        while (keep_running_) {
            clock::time_point call_start = clock::now();
            duration<double> since_last_call = call_start - call_end;

            if (period_count_ > 0 && !callback_(since_last_call.count()))
                break;

            call_end = clock::now();

            duration<double> call_duration = call_end - call_start;
            double sleep_for = period_ - call_duration.count();
            sleep_time_sum_ += sleep_for;
            ++period_count_;
            if (sleep_for > MinSleepTime)
                std::this_thread::sleep_for(std::chrono::duration<double>(sleep_for));
        }
    }

private:
    double period_;
    std::thread th_;
    std::function<bool(double)> callback_;
    std::atomic_bool keep_running_;

    static constexpr double MinSleepTime = 1E-9;

    double sleep_time_sum_;
    unsigned long period_count_;
};

Example usage

bool worldUpdator(World& w, double dt)
{
    w.update(dt);
    return true;
}

void main() {
    //create world for your simulator
    World w(...);

    //start scheduler loop for every 2ms calls
    ScheduledExecutor exec;
    exec.initialize(
        std::bind(worldUpdator, std::ref(w), std::placeholders::_1), 
            2E-3);
    exec.start();

    //main thread just checks on the results every now and then
    while (true) {
        if (exec.getPeriodCount() % 10000 == 0) {
            std::cout << exec.getSleepTimeAvg() << std::endl;
        }
    }
}

There are also other, related questions on SO.

Community
  • 1
  • 1
Shital Shah
  • 63,284
  • 17
  • 238
  • 185