Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are some good use cases for calling 'yield' in a thread?

Many languages that support multi-threading provide an action that allows a thread to offer a context switch to another threads. For example Haskell's yield.

However, the documentation doesn't say what is the actual use case. When it's appropriate to use these yield functions, and when not?

Recently I've seen one such use case in Improving the performance of Warp again where it turns out that when a network server sends a message, it's worth calling yield before trying to receive data again, because it takes the client some time to process the answer and issue another request.

I'd like to see other examples or guidelines when calling yield brings some benefit.

I'm mainly interested in Haskell, but I don't mind learning about other languages or the concept in general.

Note: This has nothing to do with generators or coroutines, such as yield in Python or Ruby.

like image 953
Petr Avatar asked Jul 27 '14 14:07

Petr


2 Answers

GHC's IO manager uses yield to improve performance. The usage can be found on github but I'll paste it here as well.

step :: EventManager -> IO State
step mgr@EventManager{..} = do
  waitForIO
  state <- readIORef emState
  state `seq` return state
  where
    waitForIO = do
      n1 <- I.poll emBackend Nothing (onFdEvent mgr)
      when (n1 <= 0) $ do
        yield
        n2 <- I.poll emBackend Nothing (onFdEvent mgr)
        when (n2 <= 0) $ do
          _ <- I.poll emBackend (Just Forever) (onFdEvent mgr)
          return ()

A helpful comment explains the usage of yield :

If the [first non-blocking] poll fails to find events, we yield, putting the poll loop thread at end of the Haskell run queue. When it comes back around, we do one more non-blocking poll, in case we get lucky and have ready events. If that also returns no events, then we do a blocking poll.

So yield is used to minimize the number of blocking polls the EventManager must perform.

like image 197
cdk Avatar answered Oct 23 '22 03:10

cdk


GHC only suspends threads at specific safe points (in particular when allocating memory). Quoting The Glasgow Haskell Compiler by Simon Marlow and Simon Peyton-Jones:

A context switch only occurs when the thread is at a safe point, where very little additional state needs to be saved. Because we use accurate GC, the stack of the thread can be moved and expanded or shrunk on demand. Contrast these with OS threads, where every context switch must save the entire processor state, and where stacks are immovable so a large chunk of address space has to be reserved up front for each thread.

[...]

Having said that, the implementation does have one problem that users occasionally run into, especially when running benchmarks. We mentioned above that lightweight threads derive some of their efficiency by only context-switching at "safe points", points in the code that the compiler designates as safe, where the internal state of the virtual machine (stack, heap, registers, etc.) is in a tidy state and garbage collection could take place. In GHC, a safe point is whenever memory is allocated, which in almost all Haskell programs happens regularly enough that the program never executes more than a few tens of instructions without hitting a safe point. However, it is possible in highly optimised code to find loops that run for many iterations without allocating memory. This tends to happen often in benchmarks (e.g., functions like factorial and Fibonacci). It occurs less often in real code, although it does happen. The lack of safe points prevents the scheduler from running, which can have detrimental effects. It is possible to solve this problem, but not without impacting the performance of these loops, and often people care about saving every cycle in their inner loops. This may just be a compromise we have to live with.

Therefore it can happen that a program with a tight loop has no such points and never switches threads. Then yield is necessary to let other threads run. See this question and this answer.

like image 20
Petr Avatar answered Oct 23 '22 05:10

Petr