Haskell的forkIO

时间:2019-06-14 17:50:05

标签: haskell concurrency

不同的OS具有不同的并发子系统,Linux中有OS进程,POSIX线程以及当今的“ LWP”线程,Windows具有进程,光纤,线程等。每个进程都由OS调度程序进行调度,并拥有自己的CPU数量。时间。对于Linux“ LWP”来说,这是正确的,因为它们是进程,但共享内存空间,而对于用户空间线程,情况并非如此,因为所有线程共享一个CPU时间量。

Haskell具有forkIO。我在Haskell来源的下一篇评论中找到了

  

Haskell线程的调度是在Haskell内部完成的   运行时系统,并且不使用任何操作系统提供的   线程包。

  

就性能而言,“ forkOS”(又名绑定)线程更多   比“ forkIO”(又名未绑定)线程昂贵,因为“ forkOS”   线程绑定到特定的操作系统线程,而“ forkIO”线程   可以由任何OS线程运行。在“ forkOS”之间进行上下文切换   线程和“ forkIO”线程的价格比两者之间的价格高出许多倍   两个“ forkIO”线程。

强调了用forkIO创建的线程不是由OS调度程序调度的。据我了解,它们可以不受常见限制(当然可以使用-thread选项),但是在这种情况下,我有3个未解决的问题:

  1. 它们(用forkIO创建的“线程”)如何共享那些CPU数量?
  2. 是否可以保证将它们分发到不同的内核,或者由于它们由一个进程表示,所以不能吗?还是这种不确定的行为?
  3. 我是对的,为了避免干扰影响,使用forkOS比使用forkIO更好吗?我的意思是,如果我有2个线程,其中一个服务于HTTP,而另一个则进行大量磁盘I / O操作,那么更好的解决方案是使用forkOS而不是forkIO

2 个答案:

答案 0 :(得分:2)

  1. Haskell线程使用协作多线程。本质上,每次Haskell需要分配内存时,它都会检查是否经过了足够的时间,如果已过去,则会切换到下一个线程。确切的机制要复杂一些(我认为在某些时候它还涉及POSIX信号,例如“警报”),但这应该是主要思想。

  2. 运行时系统使N个Haskell线程在K个OS线程上运行。 K可由用户选择。然后由操作系统决定每个操作系统线程在哪个内核上运行-可能始终是同一内核。

  3. IO繁重的操作应该不是大问题。 Haskell运行时使用无阻塞IO和轮询/选择来在所有线程上多路复用IO。另外,如果您有两个正在运行的Haskell线程,并且至少有两个OS线程专用于运行时,则这些线程应在OS线程上运行,OS应将这些线程分配给两个内核。随时尝试forkIOforkOS进行比较,看看哪种可以为您的情况提供最佳性能,但是forkIO在几乎所有情况下都应该更好。

答案 1 :(得分:0)

在具有4核心CPU的Windows 10上进行的少量测试显示了结果:

module Main where

import Control.Monad
import Control.Concurrent

t1 = forever $ print "1"
t2 = forever $ print "2"

main :: IO ()
main = do
  t1id <- forkOS t1 -- I tried forkOS and forkIO
  t2id <- forkOS t2
  getLine
  putStrLn "Done"

forkIOforkOS都需要ghc-options: -threaded选项才能利用多线程执行。在这两种情况下,我只能看到一个OS进程,但是看到多个线程。在forkIO线程数为4的情况下,而在forkOS线程数为3的情况下,更有趣的是上下文切换的数量,它与上述建议不同:在{{ 1}}(对于不同的线程):480000和48 ...,对于forkIO,它们在同一时间段内分别约为2、3、16。这意味着forkOSforkIO进行了更多的上下文切换(数十万vs数十),并且具有相同价值的汇总执行时间也应该更长,因此{{1} }在Windows上看起来更可取。

正如我在GHC源代码中看到的那样,POSIX线程在后台使用。

编辑。 Linux测试(forkOS):

forkOS:

forkOS

forkIO:

ps -eLf

UID PID PPID LWP C NLWP STIME TTY TIME CMD ... ... ... ... ... ... ... ... ... ... xyz 7432 4865 7432 0 7 10:03 pts/0 00:00:00 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 7432 4865 7448 0 7 10:03 pts/0 00:00:00 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 7432 4865 7449 0 7 10:03 pts/0 00:00:00 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 7432 4865 7450 0 7 10:03 pts/0 00:00:00 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 7432 4865 7451 66 7 10:03 pts/0 00:00:06 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 7432 4865 7452 0 7 10:03 pts/0 00:00:00 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 7432 4865 7453 67 7 10:03 pts/0 00:00:06 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho 情况下的Linux中,我们只有6个LWP,其中之一使用99%的CPU。在UID PID PPID LWP C NLWP STIME TTY TIME CMD ... ... ... ... ... ... ... ... ... ... xyz 8209 4865 8209 0 6 10:08 pts/0 00:00:00 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 8209 4865 8225 0 6 10:08 pts/0 00:00:00 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 8209 4865 8226 0 6 10:08 pts/0 00:00:00 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 8209 4865 8227 0 6 10:08 pts/0 00:00:00 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 8209 4865 8228 99 6 10:08 pts/0 00:00:06 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho xyz 8209 4865 8229 0 6 10:08 pts/0 00:00:00 /home/xyz/prj/thr/.stack-work/install/x86_64-linux-tinfo6/lts-13.25/8.6.5/bin/yohoho 的情况下,我们有7个LWP,其中2个使用了约66%的CPU,从我的角度来看,这看起来更好。因此,似乎forkIO在Linux中也更受欢迎。

相关问题