I am planning to develop a system with tens of thousands of objects in it, which will each have up to 42(but more likely down around 4 or 5) separate actions they will potentially be performing at regular intervals. I also plan to write code that will deactivate the timers until the object comes into use. When idle, the objects will only need 1 timer each, but when active, the other timers will all start at once. At first the number of objects will be small, maybe a few hundred, but I expect it to grow exponentially, and within a few months, start to reach up in the tens of thousands.
So, I am very worried about efficiency of the code I will be writing for the timers and for these objects. There are three levels in which I could write this application on that would all successfully perform the tasks required. Also, I plan to run this system on a Quad Core server, so I would like to make use of multi-threading wherever possible.
To this end, I've decided to use the System.Timers.Timer class which fires a new thread for each elapse event.
These are the 3 levels I am considering:
One single timer operates the entire application, it iterates through each object, checks to see if any other actions need to be fired, and if so, runs them, then moves on to the next.
Multi-tier timer where each object has a master timer that checks all of the functions the object could need to perform, runs any that are ready, and then sets the next timer interval to the next required action time.
Recursive-tier timer where each action in each object has it's own timer that will be triggered, and then set to run the next time it will be available.
The problem with option 1 is that with so many objects and actions, one singular timer elapse in this manner could run for maybe 20+ seconds (while it executed a few million lines of looped code), where this should probably be ticking every 1 second. If the objects aren't kept in synch, the system would likely not work well.
The problem with option 2 is that it would be a little harder to write than option 3, but not by much, it would also mean perhaps 10,000+ maybe timers running on the system (one for each object), creating and destroying threads with each elapse like its nobody's business (which I'm not sure if this is a problem or not). Each timer would have to fire at least once per second in this situation, with perhaps a few hundred lines of code running (up to perhaps a thousand in an extreme case).
The problem with option 3 is the sheer amount of timers that could potentially be introduced into the system. I'm talking about an average of 10,000+ timers with the potential for near 100,000+ timers to be run at the same time. Each elapse event may only have to run 50 or less lines of code though, making them very short. The elapse events would have delays between a hundredth of a second on one extreme, and five minutes on the other, with the average likely being around 1 second.
I am proficient in Visual Basic .NET, and was planning to write it in that, but I could also revert to my high-school days and try to write this in C++ for efficiency if it would make that much of a difference (please let me know if you have any sources on code efficiency between languages). Also toying with the notion of running this on a clustered Linux server instead of my Quad Core Windows server, but I'm not sure if I could get any of my .NET apps to run on a linux cluster like that (would love any info on that as well).
The main question to answer for this topic is:
Do I use option 1, 2, or 3, and why?
~Edit after considering comments~
So the 4th option involving the timer wheel with a spinlock. Here is a job class:
Public Class Job
Private dFireTime As DateTime
Private objF As CrossAppDomainDelegate
Private objParams() As Object
Public Sub New(ByVal Func As CrossAppDomainDelegate, ByVal Params() As Object, ByVal FireTime As DateTime)
objF = Func
dFireTime = FireTime
objParams = Params
End Sub
Public ReadOnly Property FireTime()
Get
Return dFireTime
End Get
End Property
Public ReadOnly Property Func() As CrossAppDomainDelegate
Get
Return objF
End Get
End Property
Public ReadOnly Property Params() As Object()
Get
Return objParams
End Get
End Property
End Class
And then the main loop implementation:
Private Tasks As LinkedList(Of Job)
Private Sub RunTasks()
While True
Dim CurrentTime as DateTime = Datetime.Now
If Not Tasks.Count = 0 AndAlso Tasks(0).FireTime > CurrentTime Then
Dim T As Job = Tasks(0)
Tasks.RemoveFirst()
T.Func.Invoke()
Else
Dim MillisecondDif As Double
MillisecondDif = Tasks(0).FireTime.Subtract(CurrentTime).Milliseconds
If MillisecondDif > 30 Then
Threading.Thread.Sleep(MillisecondDif)
End If
End If
End While
End Sub
Do I have it right?
~Edit 2~
Switched the word "Task" out for "Job" so ppl could stop complaining about it ;)
~Edit 3~
Added variables for tracking time & ensuring spinloops happen when needed
EDIT: I remember interesting interview definetely worth to view: Arun Kishan: Inside Windows 7 - Farewell to the Windows Kernel Dispatcher Lock
As @Steven Sudit stated I warn again: use it only as demo on how timer wheel works and some tasks you have to care about while implement it. Not as reference implementation. In real world you have to write far more complex logic to take into account available resources, scheduling logic and etc.
Here good points stated by Steven Sudit (read post comments for details):
1) Choose right structure to keep your jobs list (it depends as usually):
SortedList<> (or SortedDictionary<>) good on memory consumption and indexing but have to implement synchronized access
ConcurrentQueue<> will help you avoid locking but you have to implement ordering. It also very memory efficient
LinkedList<> is good on insert and retrieve (anyway we need head only) but requires synchronized access (thru it easily implemented via lock-free) and not so memory efficient as it stores two references (prev/next). But it become an issue when you have millions of jobs so all of them take significant amount of memory.
But:
I totally agree with @Steven:
2) To simplify processing logic of simultaneous jobs you can add delegate list (e.g. via ConcurrentQueue to make it lock-free) into original Job class so when you need another job at same time you just add another delegate to start.
@Steven:
3) Start/stoping dispatcher not so straightful as it can be and so can lead to errors. Instead you can wait on an event while using a timeout.
@Steven:
4) About using ticks:
@Steven:
My thoughts:
5) "Managing resources":
@Steven:
I absolutely agree that calling to GetAvailableThreads is naive method to monitor available resources thru CorGetAvailableThreads not so expensive. I want to demontrate there are needs to manage resources and seems to choose bad example.
By any means provided in source code example is must not be treated as right way to monitor available resources. I just want to demonstrate you have to think about it. Thru maybe coded no so good piece of code as example.
6) Using Interlocked.CompareExchange:
@Steven:
You are right I have to point about its usage.
Quick example of usage:
Hope it will be helpful.
@Steven Sudit point me some issues, so here I try to give my vision.
SortedList<> by any means is not obsolete. It still exists in .NET 4.0 and introduced in .NET 2.0 when generics was introduced into language. I can't see any point to remove it from .NET.
But real question here I trying to answer: What data structure can store values in sorted order and will be efficient in storing and indexing. There are two suitable ready to use data structures: SortedDictionary<> and SortedList<>. Here some info about how to choose. I just don't want waste implementation with my own code and hide main algorithm. Here I can implement priority-array or something other but it takes more lines to code. I don't see any reason do not use SortedList<> here...
BTW, I can't understand why you not recommend it? What are reasons?
When @Jrud says he probably will have numerous task to schedule I think it they may have heavy concurrency, so I demonstrate how to solve it. But my point: even if you have low concurrency you stil have chance to get events in same time. Also this is easy possible in multithreaded evironment or when there are many sources want to schedule jobs.
Interlocked functions not so complicated, cheap and since .NET 4.0 inlined so there are no problem to add guard in such situation.
Im not so sure here that you are right. I would recommend to read two nice articles: Part 4: Advanced Threading of Threading in C# by Joseph Albahari and How Do Locks Lock? by Jeff Moser. And of cause Chapter 28 (Primitive Thread Synchronization Constructs) of CLR via C# (3rd edition) by Jeffrey Richter.
Here some qoute:
I would also recommend: Intel® 64 and IA-32 Architectures Software Developer's Manuals if you care about it seriously.
So I don't use VolatileRead/VolatileWrite in my code neither volatile keyword, I don't think Thread.MemoryBarrier will be better here. Maybe you can point me what I miss? Some articles or in-depth discussion?
First of all its just handy method, sometime it is necessary to get all tasks in queue at least for debugging.
But you are not right. As I mentioned in code comments SortedList<> implemented as two arrays you can check this by Reference Source or just by viewing in Reflector. Here some comments from reference source:
I got from .NET 4.0 but it not changed much since 2-3.5
So my code:
involve following:
so consequently we have just flatten read-only list of references to Job's objects. It very fast even you have millions of task. Try to measure yourself.
Any way I added it to show what happens during execution cycle (for debug purposes) but I think it can be useful.
I would recommend to read Patterns of parallel programming by Stephen Toub and Thread-safe Collections in .NET Framework 4 and Their Performance Characteristics, also here many interesting articles.
So I quote:
It don't have any methods to maintain ordered queue. Neither any of new thread-safe collection, they all maintain unordered collection. But reading original @Jrud description I think we have to maintain ordered list of time when task need to be fired. Am I wrong?
Do you know good way to make sleep ThreadPool's thread? How you will implement it?
I think dispatcher goes "sleep" when he does not process any task and schedule job wake-up it. Anyway there are no special processing to put it sleep or wake up so in my thoughts this process equals "sleep".
If you told that I should just reschedule RunJobs via ThreadPool when no jobs available when you are wrong it will eat too many resources and can impact started jobs. Try yourself. Why to do unnecessary job when we can easily avoid it.
You are not right. Either you stick to ticks or you don't care about it entirely. Check DateTime implementation, each access to milliseconds property involve converting internal representaion (in ticks) to ms including division. This can hurt performance on old (Pentium class) compulters (I measure it myself and you can too).
In general I will agree with you. We don't care about representation here because it does not give us noticeable performance boost.
It just my habbit. I process billions of DateTime in recent project so coded accordingly to it. In my project there are noticeable difference between processing by ticks and by other components of DateTime.
I just want to demonstrate you have to care about it. In real world you have to implement far from my straightful logic of scheduling and monitoring resources.
I want to demonstrate timer wheel algorithm and point to some problem that author have to think when implement it.
You are absolutely right I have to warn about it. I thought "quickly ptototype" would be enough. My solution in any means can't be used in production.
None of the above. The standard solution is to keep a list of events, such that each one points to the next one to occur. You then use a single timer and have it wake up only in time for the next event.
edit
Looks like this is called a timer wheel.
edit
As Sentinel pointed out, events should be dispatched to a thread pool. The handler for these events should do a small bit of work as quickly as possible, and without blocking. If it needs to do I/O, it should fire off an async task and terminate. Otherwise, a thread pool would overflow.
The .NET 4.0
Task
class might be helpful here, particularly for its continuation methods.The tradeoff in your three options is between memory and CPU. More timers mean more timer nodes (memory), and aggregating these timers into fewer timers means more CPU, as you check for events that need servicing at run time. The CPU overhead in starting too many timers (and expiring them) is not too great an issue with a decent timer implementation.
SO, in my opinion, if you have a good timer implementation, choose to start as many timers as you need (be as granular as possible). But if any of these timers per object are mutually exclusive, consider reusing a timer node.
This reminds me of the old airline ticketing systems, where you had queues. Ticketing requests were put in different queues depending on what kind of attention they needed.
So maybe you could have the queue of objects requiring frequent attention, and the queue of objects requiring infrequent attention. When necessary, you move them from one to the other.
You could have a timer for the frequent queue, and a timer for the infrequent queue. For the frequent queue, you could split it into multiple queues, one for each thread.
For crunching the frequent queue(s), you should not have more threads than you have cores. If you have two cores, what you want to do is get both of them cranking. Any more threads than that will not make things any faster. In fact, if processing the objects requires disk I/O or getting in line for some other shared hardware, it may not even help to get both cores running.