1

My program needs to constantly perform many repetitive calculations as fast as possible. There are many tasks running parallelly which cause CPU utilisation is at 100%. To let users slow down processing overload(a little under 100% of CPU, depending on hardware), I added

await Task.Delay(TimeSpan.FromMilliseconds(doubleProcessingCycleIntervalMilliseconds));

to heavy processing methods. This works perfect as far as value of doubleProcessingCycleIntervalMilliseconds is at least 1 ms. For users who have high-end computers(calculations speed will take less than one millisecond), I wanted to add same option for delay but instead of milliseconds using ticks. So now code looks:

if (ProcessingCycleIntervalOptionsMilliseconds == true)
{ 
   await Task.Delay(TimeSpan.FromMilliseconds(doubleProcessingCycleIntervalMilliseconds)); 
}
else
{ 
   await Task.Delay(TimeSpan.FromTicks(longProcessingCycleIntervalTicks)); 
}

When walue of longProcessingCycleIntervalTicks is at least 10000 ticks(=1ms) program works perfect. Unfortunately when values go under 1ms(0 for doubleProcessingCycleIntervalMilliseconds which I can understand) or under 10000(i.e. 9999 for longProcessingCycleIntervalTicks) program becomes not responsive. So literally difference of 1 tick below 1ms hangs the program. I don't use MVVM. (Just in case: I checked Stopwatch.IsHighResolution gives true on the development computer)

Is it possible/correct to use

await Task.Delay(TimeSpan.FromTicks(longProcessingCycleIntervalTicks)); 

in .NET 4.5.1 ? If yes, then how to determine when user can use it?

as74
  • 700
  • 4
  • 12
  • 25
  • Can you give some more information on what these tasks are, and how they're being kicked off? If you're running into issues with thread saturation, you could implement a TaskScheduler (http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler(v=vs.110).aspx) that could limit the degrees of parallelism. It's hard to say without understanding more of what it is you're trying to do. – DavidN Feb 16 '14 at 13:46
  • 2
    Windows is not a realtime operating system, and its precision is not that accurate. So, you can not use ticks, you can not even use ticks below ~15 ms. You should change your design. – L.B Feb 16 '14 at 13:58
  • Inside of several async Tasks are methods which are looped/constantly repeated till Task is cancelled. Methods monitor prices and react according to predefined conditions. They should perform as fast as possible(depends on user's hardware) but keeping CPU usage level little below 100%. Parallelism is limited to numer of cores. – as74 Feb 16 '14 at 14:03
  • By looped I hope you don't mean using `while`'s or `for`'s... – Michael Coxon Feb 16 '14 at 14:11
  • Yes methods are in while(!token.IsCancellationRequested) – as74 Feb 16 '14 at 14:15
  • 1
    By using a while or for you are dedicating full CPU time to the loop, You will be blocking any other tasks happening on that core/CPU and will result in really crappy performance. You need a way to manage what is being processed so that the OS can still do all the other stuff it needs to do. By using events the CPU will do a small task (very quickly) then the OS will report back what has happened and then the program can request that another function be processed. This sounds long but all happens in probably a 100000th of a second - it really does not require many clock cycles... – Michael Coxon Feb 16 '14 at 14:35
  • @Michael Coxon "By using a while or for you are dedicating full CPU time to the loop" That's why I used Task.Delay. – as74 Feb 16 '14 at 15:02
  • @L.B Actually when I use 10000ticks which is 1ms, all is ok. – as74 Feb 16 '14 at 15:04
  • @as74 sorry, *You think* you sleep 1 ms, it is actually much more than this, as I said ~15 ms, depending on the system. – L.B Feb 16 '14 at 15:32
  • I really think there is a major bug lurking there. I just played around with some test projects and yes, 9999 (fortunately not 666 :-) ) seems to be a magic number, even if the underlying hardware/OS supports it. Very strange, not really annoying as you shoudn't use the clock that way and typical M$. – Xan-Kun Clark-Davis Nov 15 '15 at 19:10

2 Answers2

3

Your intention is not to keep CPU utilization below 100%. Your intention is to keep the system responsive. Limiting CPU utilization is a misguided goal.

The way you do this is by using low priority threads. Use a custom task scheduler for your CPU bound tasks.

Timing in Windows has limited accuracy. Thread.Sleep cannot work with fractional milliseconds. .NET rounds them away before handing over to Sleep.

Community
  • 1
  • 1
usr
  • 168,620
  • 35
  • 240
  • 369
1

You might be better off looking at the way you are performing the tasks rather than trying to sleep them.

The best way I can think of is by using a task manager to manage each task independently (such as a background worker) and then thread collections of tasks.

This would enable you to manage how many tasks are running instead of trying to 'slow' them down..

i.e

public class Task<returnType>
{
    public delegate returnType funcTask(params object[] args);
    public delegate void returnCallback(returnType ret);

    public funcTask myTask;
    public event returnCallback Callback;

    public Task(funcTask myTask, returnCallback Callback)
    {
        this.myTask = myTask;
        this.Callback = Callback;
    }

    public void DoWork(params object[] args)
    {
        if (this.Callback != null)
        {
            this.Callback(myTask(args));
        }
        else
        {
            throw new Exception("no Callback!");
        }
    }
}

Then you need a manager that has a Queue in it of the tasks you want to complete, call myQueue.Enqueue to queue, myQueue.Dequeue to run the tasks. Basically you can use the already built-in Queue to do this.

You then can create a Queue of task managers full of tasks and have them all run asychronously, and stack nicely on the CPU as they are event driven and the OS and .NET will sort out the rest.

EDIT: To continuously run tasks you will need to create a class that inherits the Queue class, then call an event when something is de-queued. The reasoning behind why I say to use events is that they stack on the CPU.

For a neverending stackable 'Loop' something like this would work...

public class TaskManager<T> : Queue<T>
{

    public delegate void taskDequeued();

    public event taskDequeued OnTaskDequeued;

    public override T Dequeue()
    {
        T ret = base.Dequeue();
        if (OnTaskDequeued != null) OnTaskDequeued();
        return ret;
    }

}

In your function that instantiates the 'loop' you need to do something like...

TaskManager<Task<int>> tasks = new TaskManager<Task<int>>();

Task<int> task = new Task<int>(i => 3 + 4, WriteIntToScreen); // WriteIntToScreen is a fake function to use as the callback

tasks.Enqueue(task);

tasks.OnTaskDequeued += delegate 
{ 
   tasks.Enqueue(task);
   tasks.Dequeue.Invoke();
};

// start the routine with
tasks.Dequeue.Invoke(); // you call do some async threading here with BeginInvoke or something but I am not gonna write all that out as it will be pages...

To cancel you just empty the queue.

Michael Coxon
  • 5,311
  • 1
  • 24
  • 51
  • 2
    Agreed with the fact that if you are adding a delay to your operation, then you are seriously doing something wrong. – Ahmed ilyas Feb 16 '14 at 14:03
  • I can not manage numer of running tasks. If user wants to start using some functions they must be started. If his computer is not efficient enough then he can use delay to still perform what he wants but at the cost of lower speed. – as74 Feb 16 '14 at 14:11
  • Not sure if I fully get what you mean here. Can you please explain what you need to do fully as you did not mention the system would need to scale depending on how the user was using the application. i.e... are they multiple calculations that need to be run depending on what calcs the user starts? – Michael Coxon Feb 16 '14 at 14:12
  • The program constantly monitors market. Let's assume there is 20 instruments. For each of them user wants to start monitor prices plus other factors, calculate them all and react. Calculations can not be driven by events, that's why they are constantly repeated(looped till cancelled/stopped by user). – as74 Feb 16 '14 at 14:27