Author Topic: The ideal event loop  (Read 3692 times)

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
The ideal event loop
« on: August 14, 2018, 05:05:47 AM »
I want to make a demonstration of the ideal event loop, that I will include in the OS 9.3 SDK.

Problem 1: using as little CPU time as possible when no thread is running. Some programs like MacLoad and Process Spy use very little CPU time. How would they do it?

Problem 2: using as much CPU time as possible when a thread is running, but no more than necessary, for example reading a file and calculating a CRC. Here I do several things at the same time: I process events, I process data, I wait for asynchronous calls to complete and I switch threads.

Offline Naiw

  • Veteran Member
  • ****
  • Posts: 126
  • new to the forums
Re: The ideal event loop
« Reply #1 on: August 14, 2018, 06:06:30 AM »
There is only one ideal event loop, and that's to sleep as much as you possibly can. If every application does that a cooperatively multitasked system feels responsive.

And as for needing a lot of CPU time, the only proper way to do so is by using MPThreads, in which case you can use as much time as you want without Mac OS feeling less responsive and the things you mention such as reading files and calculating a CRC would work just fine- especially if you're on Mac OS 9 or later that can use the file manager without trampolines from an MPTask.

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
Re: The ideal event loop
« Reply #2 on: August 16, 2018, 05:58:36 AM »
If I read asynchronously and it doesn't complete, then the thread blocks and I call Yield in a loop until it completes, but then I use all CPU time. If I call WaitNextEvent with sleep time 0, then I'm close to using all CPU time. If I call WaitNextEvent with sleep time 1, then I wait 167 milliseconds and I can read only 960 K per second.

Suppose that I can do 8000 asynchronous calls per second. I do now 8000 asynchronous reads of 16K without bothering about the result. Then I call WaitNextEvent with sleep time 60. Have you heard anyone try this?

Other tactic: I do 1 asynchronous read of 16K without bothering about the result. Then I call WaitNextEvent with sleep time 6. When it completes the completion routine does the next asynchronous read of 16K without bothering about the result and so on. Have you heard anyone try this?

Offline Daniel

  • Gold Member
  • *****
  • Posts: 300
  • Programmer, Hacker, Thinker
Re: The ideal event loop
« Reply #3 on: August 16, 2018, 08:05:57 AM »
Having the completion routine queue the next read is probably your best bet. You only use CPU time when you are doing useful work.

You could also try having the completion routine store data in a buffer for your thread to process. Every so often, it will wake up and process whatever is available. You could choose a nice long sleep time for that. There's probably a few edge cases that the completion routine can't handle on it's own (it can't allocate memory, for instance). These could be solved by setting a flag somewhere that tells the thread to restart the io read chain.

I haven't really heard of anyone doing that, but I don't know that many programmers.

Offline Naiw

  • Veteran Member
  • ****
  • Posts: 126
  • new to the forums
Re: The ideal event loop
« Reply #4 on: August 17, 2018, 07:53:50 PM »
If I read asynchronously and it doesn't complete, then the thread blocks and I call Yield in a loop until it completes, but then I use all CPU time. If I call WaitNextEvent with sleep time 0, then I'm close to using all CPU time. If I call WaitNextEvent with sleep time 1, then I wait 167 milliseconds and I can read only 960 K per second.

Suppose that I can do 8000 asynchronous calls per second. I do now 8000 asynchronous reads of 16K without bothering about the result. Then I call WaitNextEvent with sleep time 60. Have you heard anyone try this?

Other tactic: I do 1 asynchronous read of 16K without bothering about the result. Then I call WaitNextEvent with sleep time 6. When it completes the completion routine does the next asynchronous read of 16K without bothering about the result and so on. Have you heard anyone try this?

As I said, The problem is not with your application. It's that you use the cooperative multitasking of Mac OS.

If every application called WaitNextEvent with a sleep time of like INT_MAX the system would have been a lot more response, but now unfortunately apps neither sleep as they should (because a lot of applications drive silly things on null events) and this try to regulate the frequency by not sleeping.
I don't remember what scheduling algorithm the process manager uses, but I guess it's sorting based on the sleep time.

The only way you can get somewhat deterministic results no matter what other applications are running is to use MPTasks. The of course these are not immune either, but they are not affecting the user experience in the same extent.

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
Re: The ideal event loop
« Reply #5 on: September 11, 2018, 04:27:18 AM »
Other tactic: I do 1 asynchronous read of 16K without bothering about the result. Then I call WaitNextEvent with sleep time 6. When it completes the completion routine does the next asynchronous read of 16K without bothering about the result and so on. Have you heard anyone try this?
I programmed this as a DFA where I use the completion procedure as transition function. It works, but it's slower than the normal threaded solution (12.5 MB/s instead of 20 MB/s) and you can't debug it. As soon as you do your first PBReadAsync of a block of 16 K a sort of chain reaction starts and your entire file is read without stopping at breakpoints.