7

I am developing an application which analyses real-time financial data. Currently my main computational cycle has the following design:

long cycle_counter=0;
while (process_data)
{
  (analyse data, issue instruction - 5000 lines of straightforwasrd code with computations)
  cycle_counter++;
  Thread.Sleep(5);
}

When I run this application on my notebook (one Core i5) processor, the cycle runs 200-205 times per second - a sort of as expected (if you don't bother about why it runs more than 200 times a second).

But when I deploy the application on "real" workstation, which has 2 6-core Xeon processors and 24 GB of fast RAM, and which loads Win7 in about 3 seconds, the application runs the cycle about 67 times per second.

My questions are:

  • why is this happening?

  • how can I influence the number of runs per second in this situation?

  • are there any better solutions for running the cycle 200-1000 times per second? I am now thinking about just removing Thread.Sleep() (the way I use it here is criticised a lot). With 12 cores I have no problems using one core just for this cycle. But there my be some downside to such solution?

Thank you for your ideas.

Muhammad Reda
  • 26,379
  • 14
  • 93
  • 105
  • If that system has a lot of other processes executing at the same time then it's not a surprise that your thread gets less processor time. The Thread.Sleep method will cause a thread to give up the processor and not be scheduled again for at least the amount of time specified. It may not be scheduled for more than that time though. One option would be to set the Priority of your Thread to possibly get it more processor time. – jmcilhinney Jan 26 '14 at 14:30
  • Try to check the xeon machine's power settings, we had a problem where a really powerful machine ran an application slowly because it was on some power-saving settings. Check both BIOS and windows power settings. – David S. Jan 26 '14 at 14:32
  • In general, if you are using Thread.Sleep with an argument other than 0 in production code, you're doing something wrong. – Eric Lippert Jan 26 '14 at 16:01
  • @Eric: I would say: Keep in mind that threads are a limited resource, that creating them is costly, and that every additional thread makes debugging harder. Or even hell. Only use them to solve your performance problems when you have *really* tracked down the hot spots of your application. By profiling. Not by supposing. Apart from that: go on and use them! Responsibly. – hagello Jan 26 '14 at 16:51
  • 1
    64 times per second is normal. Start up Chrome to get to 200 times per second. – Hans Passant Jan 26 '14 at 18:49
  • The Xeon workstation has no other consuming applications - a bare Win7 plus my application installed. CPU usage is tiny. Changing Priority had no effect (surprise?). Power saving modes everywhere were changed to Max Performance. – user3237591 Jan 26 '14 at 19:29
  • @HansPassant Indeed, when I started Chrome, I immediately got 200 times per second. That's amazing. I guess this is happening because Chrome changes the system resolution. – user3237591 Feb 12 '14 at 00:26
  • It is because Chrome is a bad citizen that abuses the operating system. Jeff Atwood [has blogged about it](http://www.codinghorror.com/blog/2013/10/why-does-windows-have-terrible-battery-life.html) without otherwise having any idea why. – Hans Passant Feb 12 '14 at 00:31

6 Answers6

6

The approach you're taking is simply fundamentally broken. Polling strategies are in general a bad way to go, and any time you do a Sleep for a reason other than "I want to give the rest of my timeslice back to the operating system", you're probably doing something wrong.

A better way to approach the problem is:

  • Make a threadsafe queue of unprocessed work
  • Make one thread that puts new work in the queue
  • Make n threads that take work out of the queue and do the work. n should be the number of CPUs you have minus one. If you have more than n threads then at least two threads are trading off CPU time, which is making them both slower!
  • The worker threads do nothing but sit in a loop taking work out of the queue and doing the work.
  • If the queue is empty then the "take work out" blocks.
  • When new work arrives, one of the blocked threads is reactivated.

How to build a queue with these properties is a famous problem called The Producer/Consumer Problem. There are lots of articles on how to do it any many implementations of blocking producer-consumer queues. I recommend finding an existing debugged one rather than trying to write your own; getting it right can be tricky.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I'm afraid my situation is almost the opposite. I have several threads which supply new data (unprocessed work) - these are threads from third party APIs which supply streaming financial data. So I cannot change this. One thread is quite enough to process the computations - I don't need N threads to do this. Plus, the computations produce a "snapshot" of current prices/volumes - such snapshot is better done by one thread. What I need here is simply predictable polling intervals. Thank you for your idea, though. – user3237591 Jan 26 '14 at 20:09
  • @user3237591, do it the other way around then. Let the third party threads put the work items into the queue and have your thread process them. Look at the .Net BlockingCollection class which makes these scenarios really simple. – adrianm Jan 26 '14 at 20:47
  • 1
    Right, the number of threads that are dequeing the work is irrelevant. The point is that the working thread blocks until it is woken up, rather than sleeping, waking up, asking for work, sleeping,... – Eric Lippert Jan 26 '14 at 20:52
  • +1 for BlockingCollection. Write code so that all the streaming APIs put data in a BlockingCollection. BlockingCollection will synchronize data input from multiple threads. Your computation code should use GetConsumingEnumerable to read work items from this BlockingCollection and produce relevant snapshot. Have a look at this: http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/03/c.net-little-wonders-concurrentbag-and-blockingcollection.aspx – SolutionYogi Jan 27 '14 at 04:24
3

The resolution of Sleep is dictated by the current timer tick interval and is usually either 10 or 15 milliseconds depending on the edition of Windows. This can be changed, however, by issuing a timeBeginPeriod command. See this answer.

Community
  • 1
  • 1
  • But both machines have the same Win7 installed. So why such difference? Tweaking Windows through timeBeginPeriod seems way too complicated, but I may try it just to figure out if this helps. – user3237591 Jan 26 '14 at 19:36
  • It's possible that something else in your process - a hook DLL, say - is already setting the timer interval on one system but not on the other. – 500 - Internal Server Error Jan 26 '14 at 20:44
3

Windows is not a RTOS (Real Time Operating System), so you cannot precisely determine when your thread will resume. Thread.Sleep(5) really means "wake me up no sooner then 5ms". The actual sleep time is determined by the specific hardware and mostly by the system load. You can try to workaround the system load issue by running your application on a higher priority.

BTW, System.Threading.Timer is a better approach (above comments still apply though).

Wagner DosAnjos
  • 6,304
  • 1
  • 15
  • 29
  • Good ideas. I will try both playing with application priority and using Threading.Timer (which is likely to suffer from resolution issues, if there are any, as well). – user3237591 Jan 26 '14 at 20:17
1

Check your timer's actual frequency: many hardware timers have actual resolution

  65536 ticks per hour = 65536 / 3600 = 18.204 ticks per second 

So called "18.2" constant, that's why the actual timer's resolution is 1/18.2 = 55 ms; in the case of Sleep(5) it means that is could be either Sleep(0) or Sleep(55) depending on round up.

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • Only it's the "18.2 ms constant", you have something inverted. – H H Jan 26 '14 at 14:31
  • It seems like there is no way to make my cycle run more than 65 times per second and not fully use one of processor cores. If I use Sleep(0) or no Sleep() at all, I get 450000 runs per second. If I use anything less than Sleep (10) I get 65 times per second. – user3237591 Feb 10 '14 at 23:09
1

Not sure it is the best approach but another approach.
Try BlockingCollection and all you do in the producer is add and sleep.
The consumer then has the option to work full time if needed.
This still does not explain why the higher powered PC ran less cycles.

paparazzo
  • 44,497
  • 23
  • 105
  • 176
1

Is it OK for you to run your loop 200 times per second on average?

var delay = TimeSpan.FromMillseconds(5);
while (process_data) {
    Console.WriteLine("do work");
    var now = DateTime.Now;
    if (now < nextDue) 
        System.Threading.Thread.Sleep(nextDue - now);
    nextDue = nextDue.Add(delay);
}

Using this technique, your loop will execute somewhat stumbling, but it should be OK on average, as the code depends neither on the resolution of Sleep nor on the resolution of DateTime.Now.

You might even combine this approach with a Timer.

hagello
  • 2,843
  • 2
  • 27
  • 37