Hi Vanessa,

On Mar 27, 2024, at 2:57 PM, Vanessa Freudenberg <vanessa@codefrau.net> wrote:


On Wed, Mar 27, 2024 at 2:33 PM Tim Rowledge <tim@rowledge.org> wrote:


> On 2024-03-26, at 10:58 PM, Vanessa Freudenberg <vanessa@codefrau.net> wrote:
>
> we have no actual scheduler that gives a fair share of time to all runnable processes.


I have to admit that I can't even work out what we really mean by "a fair share of time"! Obviously, what we *really* mean in any particular case is that "a fair share" equals "my processes get everything they want and to hell with the rest"

LOL. Well Linux's CFS ("Completely Fair Scheduling") is around 10,000 lines of code ;)

But I'd say the minimum of fairness would be that all runnable processes should make progress all the time. How much time is given to each is debatable, except that higher priority processes should get relatively more time. And "all the time" means that every one of them should be serviced at least once every x milliseconds.
 
We do have a couple of relatively simple things almost ready to provide some improvement;
a) asynchronous file access, which has had a prim plugin for.. a couple of decades? AsynchFile is in the default image but has not users.

Using that is just a lot less convenient I guess, and we'd have to change a lot of code. But we could perhaps implement the current file prims in terms of the async prims, meaning that instead of waiting in the VM we'd wait in the image, so other processes could run in the meantime. That should improve performance dramatically for code that's i/o heavy but distributed across multiple processes (e.g., loading multiple bitmap files for game graphics, or servicing multiple http requests simultaneously).

Leon Matthes, a masters student at HPI (Hi Leon), is making excellent progress finishing the threaded FFI/pythonesque threaded VM prototype I started at Qwaq in 2010. This vm is based on an extremely efficient and simple thread sharing model invented by David Simmons in his QKS Smalltalk vm on the Mac done in the ‘90’s.

This boils down to handing off the vm to other threads by having vm level code call disownVM, which answers a piece of opaque state, and allows other threads to take ownership of the vm, and subsequently calling ownVM: supplying the opaque state, which will resume the blocked thread when its process is of higher priority than the current vm owning thread’s process, or there is no such process.

The implications here are that
- a thread will be made available to run the Smalltalk processes as soon as the vm is disowned (within the period of the heartbeat, ≈ 2ms, but could be 1ms).
- all FFI calls are non-blocking
- the socket layer can be written without a vm thread pool (eg as is done on windows) and there is no longer any need for non-blocking primitives because while the thread issuing the socket call in the vm is blocked, the process on whose behalf the socket primitive was invoked is blocked in the image and visible to the process browser
- existing primitive code that does heavy lifting (eg in Virtend, x264 video encode/decode) is trivially threaded by bracketing the calls to the work routines with disownVM, ownVM:
- the existing scheduler semantics are preserved; the vm arranges to thread switch if a process switch occurs between processes bound to different threads
- the control one has over process/thread affinity is
   = leave it unspecified and a process can run on any thread
   = specify which thread a process must run on (threads are given integer ids, 1, 2, etc)
   = specify which thread a process must /not/ run on, which allows it to run on any thread other than that precluded

I hope we can come together as community this year and take advantage of this functionality which radically simplifies exploiting multiple threads/cores without needing to rewrite the class hierarchy to make it thread safe. We need to rewrite sockets, put some small code changes in Morphic (on the Mac, UI events can only be retrieved on thread 1, so Morphic processes need to be affined to thread 1, and processes making FFI calls can profit from being precluded from running on thread 1, to avoid freezing the ui)

 
b) sockety stuff, where it's almost defined that there will be an appreciable (in cpu terms) time waiting. I *think* the unix plugin does some stuff asynchronously, but not sure about higher level prim calls.
 
Socket code is pretty much async. Chris mentioned waitUntil... which uses semaphores the VM signals when the data is actually available/sent.
The above machinery moves all the signaling up into the image, simplifying the vm code


Even just making that sort of thing trigger a yield event would surely improve matters for server-type usage?

Not when the waiting happens in a primitive, as is the case with file access. We'd really need to use AsynchFile.

Again the above machinery renders AsynchFile obsolete.


Vanessa

best, Eliot
___,,,^..^,,,___