如果我有一个异步值work
,我希望将其复制并并行执行,以便线程执行的硬件功能耗尽,我该怎么做?
例如,对于一个简短的具体示例,请考虑以下愚蠢的程序,该程序搜索小于1001的随机数:
let bound = ref System.Int32.MaxValue
let work =
async {
let rand = new System.Random ()
while !bound > 1000 do
let x = rand.Next ()
if x < !bound then
bound := x
}
[| work; work; work |]
|> Async.Parallel
|> Async.RunSynchronously
(忽略bound
的同步问题。)
在这里,我经营着三名工人;任何非零数字的程序都是正确的;当工人数量正好是可用核心数量时,可能效率更高。如何更改此程序,以便根据可用核心数自动选择工作人员数量?
更新即可。正在使用Async.Parallel
并手动指示正确方式并行化CPU绑定计算的线程数,如上所述?如果没有,那是什么?
答案 0 :(得分:3)
确定Process
的可用内核数量的一种方法是这样的:
let numberOfAvailableCores () : int =
let p = System.Diagnostics.Process.GetCurrentProcess().ProcessorAffinity
let rec countOnes acc = function
| 0un -> acc
| n ->
let i = int (n &&& 1un)
countOnes (acc + i) (n >>> 1)
countOnes 0 (unativeint p)
我发现这比System.Environment.ProcessorCount
更准确,因为AFAIK未能将ProcessorAffinity
考虑进去。
更新:由于Parallel没有公开一个函数来调用一个动作&#34;并行&#34;一个可能的解决方案可能是这样的:
let numberOfAvailableCores () : int =
let p = System.Diagnostics.Process.GetCurrentProcess().ProcessorAffinity
let rec countOnes acc = function
| 0un -> acc
| n ->
let i = int (n &&& 1un)
countOnes (acc + i) (n >>> 1)
countOnes 0 (unativeint p)
let executeInParallel (a : unit->unit) : unit =
let cores = numberOfAvailableCores ()
let actions =
[|
for x in 1..(cores * 2) -> Action a
|]
Parallel.Invoke actions
在尝试估算核心之间是否存在任何争用时的提示,只需在1核心上运行并将结果与&#34; full&#34;核心解决方案如果您有良好的解决方案,那么在启用更多内核时应该会看到线性改进。在1核上运行的一种简单方法是设置ProcessorAffinity标志
let p = System.Diagnostics.Process.GetCurrentProcess ()
p.ProcessorAffinity <- 1n // Makes this process "single-core"
(我正在尽最大努力拒绝回答你没有提出的问题,但我仍感染流感仍然很弱)
PS。 F#Async
在许多方面都很棒,但它们主要用于解决响应性问题,而非 Scalability 问题。这意味着如果您使用大量Async
工作流程,您可能会失去宝贵的时钟周期。你发布的例子不会受到影响。对于CPU绑定问题,我倾向于使用Parallel
,因为它采用自动扩展和工作窃取来利用所有CPU资源,同时具有较低的开销。 Task
或hopac
也是不错的选择。
PS。如果你想自己管理扩展,我相信经验法则是核心数量的两倍。
PS。你说忽略了bound
的同步问题,这是公平的,但我只想指出,如果一个人拥有经常被所有内核访问的共享资源,那么人们可能会看到很多性能提升。