Parallel processing F#Array.Parallel.map不提供并行处理
我必须在F#中模拟一个离散环境,由Python调用,用于强化学习问题。我有一个带有基本类型(主要是float)的函数,可以使数据交换更加顺畅。现在我可以用不同的数据多次运行这个函数,所以并行运行它似乎是个好主意 我有以下代码:Parallel processing F#Array.Parallel.map不提供并行处理,parallel-processing,functional-programming,f#,deadlock,mailboxprocessor,Parallel Processing,Functional Programming,F#,Deadlock,Mailboxprocessor,我必须在F#中模拟一个离散环境,由Python调用,用于强化学习问题。我有一个带有基本类型(主要是float)的函数,可以使数据交换更加顺畅。现在我可以用不同的数据多次运行这个函数,所以并行运行它似乎是个好主意 我有以下代码: type AscentStrategy = |Strategy of seq<float> let simulateAscent env ascentLimiter initState (sequenceOfDepths:seq<float>
type AscentStrategy = |Strategy of seq<float>
let simulateAscent env ascentLimiter initState (sequenceOfDepths:seq<float>) =
//let infinitSeqOfConstantValues = (fun _ -> constantDepth) |> Seq.initInfinite
sequenceOfDepths
|> Seq.scan ( fun ( nextState, rew, isTerminal, _ ) depth -> getNextEnvResponseAndBoundForNextAction(env, nextState , depth , ascentLimiter) ) ( initState, 0.0 , false, 0.0)
|> SeqExtension.takeWhileWithLast (fun (_ , _, isTerminalState, _) -> not isTerminalState)
|> Seq.toArray
and then
let simulateStrategy ({MaxPDCS = maxPDCS ; MaxSimTime = maximumSimulationTime ; PenaltyForExceedingRisk = penaltyForExceedingRisk ;
RewardForDelivering = rewardForDelivering ; PenaltyForExceedingTime = penaltyForExceedingTime ; IntegrationTime = integrationTime
ControlToIntegrationTimeRatio = controlToIntegrationTimeRatio; DescentRate = descentRate; MaximumDepth = maximumDepth ;
BottomTime = bottomTime ; LegDiscreteTime = legDiscreteTime } : SimulationParameters) (Strategy ascentStrategy : AscentStrategy) =
let env, initState , ascentLimiter , _ = getEnvInitStateAndAscentLimiter ( maxPDCS , maximumSimulationTime ,
penaltyForExceedingRisk , rewardForDelivering , penaltyForExceedingTime ,
integrationTime ,
controlToIntegrationTimeRatio,
descentRate ,
maximumDepth ,
bottomTime ,
legDiscreteTime )
ascentStrategy
|> simulateAscent env ascentLimiter initState
let nessosResult = inputsStrategies
|> ParStream.ofArray
|> ParStream.map simulateStrategy
|> ParStream.toArray
我将其与Array.map进行了比较,虽然它速度更快,占用了笔记本电脑70%的CPU,但似乎仍然没有使用全部处理能力。我在一台拥有更多内核(约50个)的机器上运行过它,它几乎不会增加CPU使用率(在50个独立输入的情况下,它的使用率高达总使用率的3/4%)。我想一定是某个地方产生了死锁,但我如何才能检测到并消除它呢
还有,为什么会发生这种情况?在我看来,函数式编程的优点之一就是能够轻松地并行化
PS:SeqExtension.takeWhileWithLast是我在SO上找到的一个函数,由Tomas Petricek在他的一个精彩答案中提供,如果需要,我可以发布它
PPS:env是环境,其类型定义为:
type Environment<'S, 'A ,'I> = |Environment of (State<'S> -> Action<'A> -> EnvironmentOutput<'S ,'I>)
我已经为inputStrategy定义了一个特殊类型(基本是我以前的元组),这样simulateStrategy只接受一个输入。不幸的是,这个问题似乎隐藏在某个地方。我附加了一个CPU使用率图表。对于不同的情况,我在机器上花费的时间为:~8.8秒(连续)~6.2秒(Array.Parallel.map);~6.1秒(Nessos.Streams)我发现在.NET上获得最佳并行性能是必要的。在您的app.config中类似于以下内容:
输入策略从哪里来?它们是如何生成的?@FyodorSoikin它们是用于测试的玩具序列。我生成n个具有常量值的序列。我添加了用于测试代码是否并行运行的玩具代码。ThanksParallel确实使用了多个内核,但隐藏锁(如您似乎正在使用的Seq中的锁)可能会限制它实现的并行性。如果你不能创建一个你在这里共享的最小可复制的程序,那么我建议你试着从简单开始,例如Parallel.for
,观察它实现的并行性,并从那里扩展,直到你明白是什么扼杀了并行性。旁注;我曾经对并行实现性能感到非常兴奋,但我得出的结论是,这很难,而且你可能买不起好的抽象。谢谢。恐怕你是对的。我的代码有点抽象(可能太多了,但我希望在很多方面都可以重用);我不知道这会给并行性带来问题。不过,布莱恩伯恩斯的回答帮助很大。ThanksIt并没有解决整个问题,但确实缩短了时间。在我的笔记本电脑上,它似乎充分利用了所有的4个CPU,在80核的Xeon上,它仍然缺乏完全的并行性,但从时间上看,它从2X(wrt到顺序解决方案)增加到了10X,这已经是一个很好的改进。谢谢
let nessosResult = inputsStrategies
|> ParStream.ofArray
|> ParStream.map simulateStrategy
|> ParStream.toArray