Skip to content

Conversation

@HpLightcorner
Copy link
Contributor

Hey,
And yet another pull request.

Addresses Issue
My application adds a lot of tasks to the scheduler (up to 1000) and needs quite a precise scheduling performance. Each task does not take a lot of time as the code is already optimized, however, I found out that when a lot of tasks need to be executed at the same time (say - 1000 tasks each second), the tick call itself becomes quite slow (especially when iterator debugging is active under MSVC, but also when executing in relase mode). Furthermore, adding 1000 tasks with the same cron-string also takes a lot of time. So I decided to investigate the issue and found the following reasons (adressed in this pull request):

  • std::priority_queue sorts the element each time you call push. In the current implementation sorting is called for each executed task, however this is not necessary at all. Sorting can be done once at the end of tick. I addressed this issue by optimizing how often the queue is sorted
  • The same is true when adding for example 1000 tasks - hence I added a batch-add schedule function where the user can provide a std::map<std::string, std::string>, a std::unordered_map<std::string, std::string> or a std::vector<std::pair<std::string, std::string>> to add_schedule.
  • The calculated chron strings are not cached (CronData::create) - caching them is possible and does improve performance
  • When there is a lot of time between adding tasks and calling tick for the first time, it should be possible to recalculate schedules. Hence I added a function to let the user decide if it is important for him.

To distinguish between the added tasks when batch-adding them, I added a get_name function to the TaskInformation Interface.

I think we can do it the same as last time - let's discuss my approaches and if you see an improvement we can merge? In the end, I was able to get the call to tick from 700 mS for 1000 Tasks to be executed down to 1-10 mS - which is a huge improvement I guess.

@PerMalmberg
Copy link
Owner

Nice stuff. Will have a closer look asap, a bit busy at the moment. Remind me on Thursday if I've not responded by then.

Copy link
Owner

@PerMalmberg PerMalmberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please elaborate on why you need to add multiple schedules that perform the same work instead of adjusting a single schedule? I can't see the benefit even taking multithreading into account since the tick() is still called on a single thread which means that all work is still single-threaded.

@HpLightcorner
Copy link
Contributor Author

Hey,
Sorry for the late response, busy days..... I have the following scenario:
My application is used as a simple "Sampler". Incoming data is hold in a virtual device with several hundred of data-channels and the user is configuring the sampling timepoints using CRON-Strings within a YAML file ("Sample-and-Hold"). So at startup or on change of the YAML file, I read the file and have to add hundreds of channels at once to libcron::Cron. I differentiate the channels by their names (the reason why I added the get_name function to Task). So, to sum up, the application is dynamically adding tasks to libcron - that is the reason why the batch-adding increased my performance: I have hundreds of tasks, each task itself is only taking just a few milliseconds and yes, everything is single threaded and yes, it is the same callback, but based on the task-name different channels get sampled. And I think there are other scenarious where work may differ based on the task name: Let's say we have defined key-words the user can use in a config-file and the schedule performs different tasks based on the key-word which is used as the task-name?

@PerMalmberg
Copy link
Owner

Ok, so I assume that most tasks have > 1s sampling interval?

@HpLightcorner
Copy link
Contributor Author

Actually, there are several channels sampled every second... But also Channels sampled each day at a specific time-point, channels sampled each hour, each minute, every 10 minutes..... That is what makes libcron so powerful in my scenario: giving complete flexibility to customise the data acquisition system by simply changing the CRON string in the config-file.

@PerMalmberg
Copy link
Owner

Ok :) We'll merge this as soon as you're done with the remaining changes.

Copy link
Owner

@PerMalmberg PerMalmberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some additional comments to those already not resolved.

HpLightcorner and others added 3 commits September 24, 2020 23:16
Co-authored-by: Per Malmberg <PerMalmberg@users.noreply.github.com>
@HpLightcorner
Copy link
Contributor Author

I think I am done now :)

@PerMalmberg
Copy link
Owner

I think I am done now :)

Almost :) There's still that break that I'd like to see removed, see earlier comment.

@HpLightcorner
Copy link
Contributor Author

Something like that?

@PerMalmberg
Copy link
Owner

Something like that?

Yes! 👍

@PerMalmberg PerMalmberg merged commit f3fddf5 into PerMalmberg:master Sep 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants