ocaml-multicore / eio

Effects-based direct-style IO for multicore OCaml
Other
548 stars 66 forks source link

DRAFT: Sync stream select #590

Closed dermesser closed 10 months ago

dermesser commented 1 year ago

This is a work in progress for an implementation of select_of_many with the same type as the one in #585 in the Stream.Locking module. At some point, both implementations would need to be unified so that a mix of locking and sync streams can be selected from.

I added a simple benchmark script which exercises the logic from multiple domains (although just one fiber calling select at the moment). It helped find one bug already. The time-per-item is at about 4-5 µs on my machine; in comparison, the Stream benchmark for sending items on a single stream takes 4.5 µs for the same number of domains -- i.e., it takes about the same time.

Overall I've had the feeling that some approaches I've used might be controversial (eg., a spinning wait on In_transition cells). Also, the code is not in the nicest shape yet, it probably can be decomposed into smaller functions. I'd mainly like to hear some feedback if this is a viable route to go, like in #585. In any case, I hope my dabbling around doesn't cause too much annoyance to you all :-)

dermesser commented 1 year ago

The force-push was made to make this PR independent of #585; they are now both based on main.

talex5 commented 1 year ago

Just a quick note: I wouldn't expect many changes would be needed to sync.ml, as it was designed to support this in the first place. I was expecting it would just need another version of the take/take_suspend functions. At the moment, we have:

let kc v = enqueue (Ok v); true in

This means that when a producer gives us a value, we always resume and accept it. With multiple streams, we'll try to CAS the result and might fail. In that case, it needs to return false.

dermesser commented 1 year ago

What I implemented is, after all, just another version of take and take_suspend, although admittedly with some decoration around it. The logic is essentially the same, amended to support correct cancelling etc.

I'm sure there is a more elegant or succinct way to implement it than the way I have found, though. After all, the simplest way is often the most difficult to find :-)

This means that when a producer gives us a value, we always resume and accept it. With multiple streams, we'll try to CAS the result and might fail. In that case, it needs to return false.

This is what happens in sync.ml:402, or do you mean something else?

dermesser commented 10 months ago

thank you for getting back to me! This PR was (for me) mostly an attempt at getting serious with multicore ocaml, but I didn't really expect it to be merged anyway :) Therefore, thank you for all the helpful feedback and also thank you for providing such a pleasant library to work with!