I am writing an NLM in which there...
Articles and Tips: qna
01 May 2003
Q.
I am writing an NLM in which there are loops that perform time-consuming operations without calling any blocking APIs. I'll need to call ThreadSwitchWithDelay() at certain points in order to relinquish control properly. The easiest thing to do would be to just call ThreadSwitchWithDelay() in the innermost loop.
However, I was also thinking that if my thread relinquishes control more often than is necessary, this would slow my application and would cause unnecessary context switches. I tried calling ThreadSwitchWithDelay() at 1 millisecond intervals, using GetHighResolutionTimer(). I chose the 1 millisecond interval because the NDK documentation (in ndev_enu.pdf) says:
"In general, the NLM should run for about 1 millisecond (from 3,000 to 10,000 machine instructions) and then relinquish control."
However, the tests I did on my NetWare server (NetWare 6 on a Dell Pentium 4 1.6 GHz) seemed to show that my NLM slows down the server. I think the period of 1 millisecond didn't appear to be proper. What should be the proper period of relinquishing control (or should I just stick to calling ThreadSwitchWithDelay() as often as possible)?
A.
The excerpt you quoted from the NDK documentation is a very dated statement and relates back to NetWare 3 and the days of 20MHz 386 processors. On a 1.6GHz processor, you are talking about at least 1.6 million instructions being executed in 1 millisecond. At the same time, a powerful server could be trying to process thousands of network packets a second or disk I/Os. All in all, 1 millisecond is a long time to block.
When we developed NetWare 5, we looked at the disparity in performance of old processors versus modern ones and changed the underlying yield function in the system scheduler, such that not every yield explicitly coded would actually yield. Thus, you can code them more liberally as you see fit and all you miss will be the function call overhead. You may like to try .1 millisecond switching. This is not a big overhead for the server as the Ring 0 overhead was last documented at around 30 instructions.
You may also consider looking at the LIBC environment, as this can provide a pre-emptive operating environment, removing the issue. Note that in addition to ThreadSwitchWithDelay(), you can also call ThreadSwitch(), ThreadSwitchLowPriority(), or delay() to relinquish processor control.
* Originally published in Novell AppNotes
Disclaimer
The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.