 |
|
 |
Many thanks for your experience, it helped me...
|
|
|
|
 |
|
 |
I didn't realized timeBeginPeriod(1) impacted the entire system. Very interesting.
Just a few points in regards to using a low sleep that is less than the quantum (10-15 sec).
1) As you know, Windows is not a RTOS (RealTime Operating System). The closest Windows OS to an RTOS is Windows CE. The traditional rule of thumb for a RTOS is to guarantee equal time slicing in a round robin fashion.
2) It is bad practice to use a sleep to "time things". Use sychronization.
3) Tight loops are very heavy on the CPU utilization.
In your code:
while(true)
{
DWORD start=GetTickCount();
for(int i=0;i<100;i++)
Sleep(1);
DWORD end=GetTickCount();
printf("%d ms passed!\n",end-start);
}
This is what is called a "tight loop". It is a CPU hog.
So it wasn't so much that using a timeBeginPeriod(1) will change the resolution of sleep, but rather how much context switching that is going on with that "pseudo-yield" you are trying to achieve.
You have a high context switching here. That is what your QA team saw. This is the type of stuff to look for in your code to run efficiently. You want to minimize the OP codes in such loops. So if you are looking for 100 ms, well, use Sleep(100). That will optimize the "pseudo" yield giving back the CPU to the system with minimum context switching.
Changing the period might improve the speed of your program, but it still doing an awful amount of context switching. It is these high context switching "tight loops" that you need to watch for.
Hector Santos
http://www.santronics.com
|
|
|
|
 |
|
 |
sleep and sychronization isn't a very same thing, that's why sleep exists, isn't it? Usually sychronization is only used in tightly coupled components.
In my example, I didn't mean to loop 100 times to achieve 100ms, I'm emulating a real situatition. In real world, the component may do a lot of work in every loop even go across different levels of different components. That's why I said "The side effect may be accumulated and not to be found as easily as in my trivial program".
|
|
|
|
 |
|
 |
Bill,
You always have a natural time slice. When you use sleep, you change the dynamics of the system. Synchronization is used when you want 100% full control of the chaos of the system. In Windows, a Sleep is often referred to as a "Poor's Man Sychronization" method.
Typically, in a good sound application, you don't need sleeps. Sleep is much more useful when you want to wake up other sleeping threads, which a Sleep(0) will do. Its a form of "yielding" work but a poor way to be sychronize a system. That is often the mistake.
This is why in a RTOS, a Sleep() can be used for sychronization w/ greater success than you can within Windows. This is because in a RTOS() the time slicing or the "Residence Time" is "guaranteed." That is the fundamental concept in a RTOS.
The same is true or "starts out" true for Windows, but with kernel object synchronications or event driven methods, the "residence" time in not guranteed. In other words, Windows will equal time slice equal priority threads, but you don't know what kinds of other pre-emptived operations are taking place.
In any case, the main point in my comment is that the concept of Context Switching is very important when considering efficiency and performance. So in your loop of 100, that is 200 switches - one to pop out of the CPU cache and one to pop back in! If the memory footprint is high, you have a very low performing and inefficient system.
In summary, the tip is:
Avoid using low sleeps in tight, fast loops where the total
OP code residence time is less than the natural CPU quantum
time.
---
Hector
|
|
|
|
 |
|
 |
Hector,
I'm pretty sure you used to work on some embedded system or hand held system
Different domains have different needs, RTOS isn't always the best choice. In a multi-user OS, applications are highly independent. Sychronization is infeasible among them, not to meantion 100% full control of the chaos of the system. It is also not proper to sychronize between different levels (OS never allows applications to sychronize with its kernel objects, right?)
As I always said, there is no absolutely wrong or right thing. Performance does not always mean to save CPU or to save memory usage, it means highest throughput. There could be various bottlenecks in a system. Sometime a logic defect may prevent from achieving a higher capacity, that is exactly what I'm showing here.
-Bill
-- modified at 3:29 Saturday 4th March, 2006
|
|
|
|
 |
|
 |
During my early chemical engineering Westinghouse Electric days, I spent rich years with working with a suite of systems, including many early Intel x86 systems with RMS and PL/M and other small footprint Unix based RTOS systems mostly for advanced energy process control systems, like Windmills, Nuke Steam Generation plants, Fuel Cells Prototypes, Coal Gasification Prototypes, Robotics Arms, assembly line devices, etc. Process control, simulators and AI (Expert Diagnostic Systems) was my early engineering days expertise.
I doubt it changed much in concept over the years, especially when it comes to Intel systems.
I think we are both in general agreement. I was strictly pointing out how your example or logic close to it, which is common with many programmers, contributes to the efficiency aspects of the system.
I am a strong advocate of "black box" designs. As long as each entity is designed to its isolated best ability and its interface points to the outside world, when you put them together, you usually get a optimal outcome.
Anyway, thanks for your article. It was interesting to me since I didn't realize timeBeginPeriod() impacted the entire system. I was unaware of this.
---
Hector Santos, CTO
http://www.santronics.com
|
|
|
|
 |
|
 |
> It is also not proper to sychronize between different levels (OS never allows applications to sychronize with its kernel objects, right?)
Say what? I think sharing a synchronization object (most of which in Windows are implemented as kernel objects) between kernel code (such as a driver) and user-space is pretty common. I have worked on projects which used events and mutexes, etc., that were shared. It worked very well. I don't see anything wrong with it. What could you have against it?
|
|
|
|
 |
|
 |
Your driver is not OS kernel objects, it's still part of your application. OS has numerous kernel objects for sychronization but they never shared with you, although in theory they can improve your performance. Why, one word: safety.
|
|
|
|
 |
|
 |
One comment about sleep(0). It seems to work on Windows only. I have noticed that on Unix/Linux platforms, sleep(0) doesn't release CPU. Instead, you need to use sleep(n>0) to allow other threads or processes to run on Unix/Linux.
|
|
|
|
 |
|
 |
Sleep(0) is *NOT* the right way to yield the CPU. It will prevent threads with lower priority to be run! You shouldn't actually yield the CPU explicitly, but rather wait on a synchronization object.
|
|
|
|
 |
|
 |
I didn't mean that we should use sleep(0) in multiple threads.
However, sleep(0) can be useful when used in multiple processes. In many cases, you just want every process to get a "fair" chance to run, but they don't have to be synchronized. It is particularly useful when used in a while loop (polling). Without it, other processes hardly get any CPU.
|
|
|
|
 |
|
 |
This is exactly what you're wrong about. Other threads/processes don't get treated fairly, in fact threads with lower priorities never get a chance to be run. So background stuff like the indexing service will never get to do it's work.
|
|
|
|
 |
|
 |
Sleep(n) calls are not acurate in time, but I didn't find it behaves the way you described. I think the priority level and sleep(0) are different things in nature. There is no guanrantee that vey-low-priority threads will get to run, no matter how you try to release CPU. On the other hand, if I want every thread to be treated fairly, I wouldn't assign lower priorities to them, regardless of whether they are background tasks or how they acquire CPU from others.
|
|
|
|
 |
|
 |
Of course there's no guarantee lower priority threads ever get to run. But Sleep(0) will prevent a switch to a thread with lower priority in any case. Which means if it's used to "yield" the CPU frequently, threads with lower priority almost never get a chance to run. If you really want to yield the CPU properly, wait on a synchronization object. This way lower priority threads can get a chance to run (of course not guaranteed).
|
|
|
|
 |
|
 |
As I believed, in programming there is no absolutely right or absolutely wrong thing. The key is that you really understand what you are doing. People should be cautious to lower or to increase priority of a thread. You need understand that you are playing a role which OS usually plays and your application are living with other applications that may or may not created by you.
|
|
|
|
 |