F# Async.Parallel |> Async.RunSynchronously only uses one of the eight CPU core?

我的梦境 提交于 2019-12-24 04:55:08

问题


I created a dotnet core application and run the following code of release build. However, the total CPU usage of the PC is around only 20% and process dotnet run takes only 12% (I have eight logical CPUs and I don't see any one of it use 100% either). Isn't the CPU the bottleneck of the code?

open FSharpx.Control

[<EntryPoint>]
let main argv =
    let ag = new BlockingQueueAgent<int option>(500)

    let enqueue() = async { for i = 0 to 1000 do ag.Add (Some i) }
    async {
        do! [ for i = 0 to 1000 do yield enqueue() ] 
            |> Async.Parallel |> Async.Ignore
        ag.Add None
    } |> Async.Start

    let mutable x = 0

    let rec dequeue() =
        async {
            let! m = ag.AsyncGet()
            match m with
            | Some v ->
                //x <- x ^^^ v
                for z = 0 to 10000 do x <- x + z
                return! dequeue()
            | None -> 
                printfn "Done %d" x
        }

    [ for i = 0 to 100 do yield dequeue() ] 
    |> Async.Parallel |> Async.Ignore |> Async.RunSynchronously
    0

Here is the source code of BlockingQueueAgent: https://github.com/fsprojects/FSharpx.Async/blob/master/src/FSharpx.Async/BlockingQueueAgent.fs

Update: Added more complex code (repaced x <- x ^^^ v). Now it uses a CPU core a lot. Still 13% though. Why it doesn't use multiple core?


回答1:


You're synchronously enqueueing all of your Add operations before you start dequeuing any messages. This means that when the agent is choosing what to do next it will always Add a new item to the queue if it isn't full. When it is full, it will search for the first AsyncGet operation and process that, but then will immediately Add (synchronously) the next item to the queue before allowing another message to be dequeued. This effectively only allows you to dequeue one message at a time because the agent is always switching back and forth between Add and AsyncGet operations.

If you do an AsyncAdd instead of an Add then both enqueuing and dequeueing can happen asynchronously and you get the desired behaviour, i.e.

let enqueue() = async { for i = 0 to 1000 do do! ag.AsyncAdd (Some i) }


来源:https://stackoverflow.com/questions/57455823/f-async-parallel-async-runsynchronously-only-uses-one-of-the-eight-cpu-core

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!