I have a tool that does a lot of calculations in a loop. It is creating async tasks and then run them in parallel. About 20% of the time, the CPU is maxed out.

At the same time, I have a REST api, implemented with Suave, used to query the data. The issue is that when the CPU is busy, Suave will just not reply at all.

Right now, I have about 10 seconds of every minute where the rest calls will not be processed, while the calculations are done, and afterwards requests are processed normally.

So I am trying to investigate if priorities in thread may be the solution for that.

I'm starting Suave like this:

let listening, server = startWebServerAsync configuration webApplication
server    |> Async.Start
listening |> Async.RunSynchronously

but I was wondering if there is a way to set the priority of the server so that its code is executed if there is a request.

Alternatively, I start all the calculations like this:

snapshots
|> List.map (fun snapshot ->
    async {
        return dosomestuff...
    })
|> Async.Parallel
|> Async.RunSynchronously

is there a way to lower the priority of this execution to give a chance for the web server to reply?

or, should I insert some Thread.Sleep(1) in the computation to give a chance to the context switch?


What I have tried:

  • I've tried to sprinkle the calculations with Thread.Sleep(0) and also Thread.Sleep(1) to see if it helps to do a context switch when there is a Suave request. No effect.

  • I've started the calculations in their own thread and set a low priority, like this:

      let thread = Thread(ThreadStart(processLoop))
      thread.Priority <- ThreadPriority.BelowNormal
      thread.Start()
    

    but this didn't change anything either.


More detail about Suave:

this is an example of an endpoint from Suave.

// get the correlation matrix
let private getCorrelationMatrix () =
    match ReportStore.getReport() with
    | Ok report ->
        {|
            StartTime            = report.StartTime
            EndTime              = report.EndTime
            Interval             = report.Interval
            PublicationTime      = report.PublicationTime
            CorrelationMatrix    = report.CorrelationMatrix
        |}
        |> Json.serialize |> Successful.OK >=> setMimeType "application/json"
    | Result.Error e ->
        ServerErrors.INTERNAL_ERROR e

with ReportStore.getReport() just getting the last data, or error, from a mutable.

The Suave endpoint are very lightweight, they just grab the last data, or the last error, from an array and return it.

It really looks like when all cores are busy with parallel execution, no other threads can preempt that. 10s is very long when you wait for a reply!

0

There are 0 answers