Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scheduling for IO has fairness and performance problems #377

Open
turion opened this issue Nov 29, 2024 · 0 comments
Open

Scheduling for IO has fairness and performance problems #377

turion opened this issue Nov 29, 2024 · 0 comments

Comments

@turion
Copy link
Owner

turion commented Nov 29, 2024

Since the issue isn't documented well in one place, I'll do that here.

Summary

The problem

The instance MonadSchedule IO works roughly like this:

  1. Create an empty MVar
  2. Launch a new green thread for each given action, putting their results in the MVar
  3. Collect the first result with takeMVar
  4. Return result, together with the continuations which are takeMVar as well

This is a fine implementation, but it doesn't work well in Rhine because there we schedule at each step of the clock, and use the continuations as actions for the next step. So we create a new green thread that does nothing except waiting for a result arrive on the old MVar and pasting it into the new MVar. The problem is exacerbated when one clock ticks much slower than another. In that case, the slower clock builds up a long chain of MVars which it needs wade through before returning the tick, resulting in a huge overhead and noticeable lags.

Solutions

Extend monad-schedule

From the perspective of Rhine, the problem stems from two assumptions in monad-schedule:

  1. The type of continuations (unfinished actions after scheduling) is the same as the type of actions. We cannot distinguish an action that is immediately productive from one that is blocked on an MVar.
  2. A new scheduling context (the MVar) is created each time. It's wasteful to create a new MVar, when there still is an older MVar on which operations are waiting.

We can extend the definition of MonadSchedule to include a type of continuations and type of scheduling contexts, with backwards compatible defaults. This is the most conservative solution, a WIP is in https://github.com/turion/monad-schedule/tree/dev_scheduling_context.

Forbid instance MonadSchedule IO

We could mark this instance deprecated and urge people to use FreeAsyncT. An even more lightweight implementation is possible based on the type Either (MVar a) (IO a), see also https://github.com/turion/monad-schedule/tree/dev_scheduling_context. This solution is the easiest to implement. But it is more restrictive and cumbersome on the user.

Drop monad-schedule dependency in Rhine

We would need to develop a concurrency primitive that is tailored directly to Rhine. The type signature would be something like:

class MonadSchedule m where
  schedule :: [Automaton m a b] -> Automaton m a b

Since I'm not aware of any other major users of monad-schedule, this is probably the cleanest solution, but it has some drawbacks:

  • High development & testing effort
  • It will probably be harder to develop custom instances for users

A WIP is here: #376

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant