Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 为什么基于haskell枚举器的IO如此频繁地调用sigprocmask?_Performance_Haskell_Ghc - Fatal编程技术网

Performance 为什么基于haskell枚举器的IO如此频繁地调用sigprocmask?

Performance 为什么基于haskell枚举器的IO如此频繁地调用sigprocmask?,performance,haskell,ghc,Performance,Haskell,Ghc,修订摘要 好的,看起来系统调用肯定与GC有关,而根本的问题是GC发生得太频繁了。这似乎与splitWhen和pack的使用有关,这是我通过分析得出的最好结论 将每个块从惰性文本转换为严格文本,并将它们全部连接起来,因为它建立了块的缓冲区。那肯定会分配很多 包,因为它正在从一种类型转换到另一种类型,必须分配,这在我的内部循环中,所以这也是有意义的 原版 在基于haskell枚举器的IO中,我偶然发现了一些令人惊讶的系统调用活动。希望有人能解释一下 我一直在玩弄haskell版本的快速perl脚本,

修订摘要

好的,看起来系统调用肯定与GC有关,而根本的问题是GC发生得太频繁了。这似乎与splitWhen和pack的使用有关,这是我通过分析得出的最好结论

将每个块从惰性文本转换为严格文本,并将它们全部连接起来,因为它建立了块的缓冲区。那肯定会分配很多

包,因为它正在从一种类型转换到另一种类型,必须分配,这在我的内部循环中,所以这也是有意义的

原版

在基于haskell枚举器的IO中,我偶然发现了一些令人惊讶的系统调用活动。希望有人能解释一下

我一直在玩弄haskell版本的快速perl脚本,我曾经编写了几个月,现在断断续续。脚本从每行读取一些json,然后打印出一个特定字段(如果存在)

这是perl版本,以及我如何运行它

cat ~/sample_input | perl -lpe '($_) = grep(/type/, split(/,/))' > /dev/null
这里是haskell版本(它的调用方式与perl版本类似)


如果在这些sigsetmask之间读取的数据量很大,我首先猜测运行时正在gc运行之前执行sigsetmask,因此,gc不会因堆处于不一致状态而中断。

多于一条注释,少于一个答案:如果您通过GHC源代码grep,您将看到
posix/TTY.c
(TERMIOS code)和
sm/gc.c
(通过
{,un}blockUserSignals
)最有可能的候选对象。您可以使用调试符号编译GHC,或者只抛出一些伪(唯一)系统调用,以确保您可以区分这两个系统调用配置文件来找到答案。另一个廉价的测试是移除任何终端交互,如果掩蔽行为消失,那么这将是支持GC的温和证据(none的答案)


编辑:我应该承认一些库代码也可以调用
sigprocmask
,我忽略了这一可能性较小的源代码,但实际上这可能是问题所在

通过评论将此提升到最高级别:


FWIW,我正在经历运行时(我们也在IRC中讨论这一点),sigprocmask只有两种用途:GC和tty驱动程序。后者不太可能,我建议进行分析以验证它是否执行了大量GC,并尝试找出原因


结果(来自IRC)它为0.5MB的数据分配了90MB的空间,垃圾收集器确实被触发了很多次。现在,我们来看看枚举器为什么要做这么多额外的分配。

您是否关心分配或对sigprocmask调用的开销

如果是前者,并且您想要使用
枚举器
包,那么这个小小的更改将帮助4k测试集减少50%:8MB的分配减少到4MB,gen0 GC从15减少到6

splitOn :: EI.Enumeratee T.Text T.Text IO b
splitOn = EL.concatMap (T.split fastSplit)

fastSplit :: Char -> Bool
fastSplit ','  = True
fastSplit '\n' = True
fastSplit _    = False
之前(来自
+RTS-sstderr-RTS
的统计数据):

堆中分配的8212680字节 GC期间复制的696184字节 148656字节的最大驻留空间(1个示例) 30664字节最大斜率 2 MB总内存在使用中(0 MB因碎片而丢失) Tot时间(已用)平均暂停最大暂停 Gen 0 15 Cels,0个PAR 0.00 s 0.00 s 0.000 01S 0.000 05S Gen 1 1 Cels,0 PAR 0.00 s 0.00 s 0.00 10S 0.00 10S 之后:

3,838,048 bytes allocated in the heap 689,592 bytes copied during GC 148,368 bytes maximum residency (1 sample(s)) 27,040 bytes maximum slop 2 MB total memory in use (0 MB lost due to fragmentation) Tot time (elapsed) Avg pause Max pause Gen 0 6 colls, 0 par 0.00s 0.00s 0.0001s 0.0003s Gen 1 1 colls, 0 par 0.00s 0.00s 0.0006s 0.0006s 堆中分配的3838048字节 GC期间复制的689592字节 148368字节的最大驻留空间(1个示例) 27040字节最大斜率 2 MB总内存在使用中(0 MB因碎片而丢失) Tot时间(已用)平均暂停最大暂停 Gen 0 6 Cels,0个PAR 0.00 s 0.00 s 0.000 01S 0.000 03S Gen 1 1 Cels,0面值0.00 s 0.00 s 0.000 06S 0.000 06S 这是一个相当合理的改进,但肯定还有一些不尽如人意的地方。我并没有过多地使用枚举器,而是尝试在导管中重写它——0.4.1只是为了好玩。它应该相当于

import Data.Conduit as C
import qualified Data.Conduit.Binary as Cb
import qualified Data.Conduit.List as Cl
import qualified Data.Conduit.Text as Ct
import qualified Data.Text as T
import qualified Data.Text.IO as TI
import Control.Monad.Trans (MonadIO, liftIO)
import System.Environment
import System.IO (stdin)

grabField :: Monad m => String -> Conduit T.Text m T.Text
grabField = Cl.filter . T.isInfixOf . T.pack

printField :: MonadIO m => T.Text -> m ()
printField field = liftIO $ do
  TI.putStrLn field
  putStr "\n\n"

fastSplit :: Char -> Bool
fastSplit ','  = True
fastSplit '\n' = True
fastSplit _    = False

main :: IO ()
main = do
  field:_ <- getArgs
  runResourceT $ Cb.sourceHandle stdin
              $$ Ct.decode Ct.utf8
              =$ Cl.concatMap (T.split fastSplit)
              =$ grabField field
              =$ Cl.mapM_ printField
导入数据。导管为C
将限定的Data.conductor.Binary作为Cb导入
导入符合条件的数据.conductor.List作为Cl
将限定的Data.conductor.Text作为Ct导入
导入符合条件的数据。文本为T
将限定的Data.Text.IO导入为TI
进口管制.Monad.Trans(MonadIO,liftIO)
导入系统。环境
导入System.IO(标准输入法)
grabField::Monad m=>String->导管T.Text m T.Text
grabbield=Cl.filter。T.isInfixOf。T.pack
printField::MonadIO m=>T.Text->m()
printField=liftIO$do
TI.putStrLn字段
putStr“\n\n”
fastSplit::Char->Bool
fastSplit','=真
fastSplit'\n'=真
fastSplit=错误
main::IO()
main=do

字段:\它不是一次读取很多数据。我想是4k还是8k?嗯,我想每个256k的分配都有一个“小”gc。也许中间结构实际上分配了这么多。另一个想法是sigsetmask可能会保护线程开关(GHC轻量级线程)的寄存器保存序列。这在Perl中是不会发生的,它使用Posix线程(如果它使用线程的话),afaik。不过我只是在猜测。我对GHC runtime.FWIW知之甚少,我正在经历运行时(我们也在IRC中讨论这个问题),而
sigprocmask
只有两种用途:GC和tty驱动程序。后者不太可能,我建议评测以验证它是否在执行大量GC,并尝试找出原因。Perl实际上不执行线程(有
ithreads
,但它非常可怕;相当于每个线程都有自己的独立Perl解释器),并且没有基于事件循环的输入管理器(因此,除非您使用的是POE或提供自己事件管理器的GUI库,否则不要选择
。@geekosaur:I'm c
8,212,680 bytes allocated in the heap
         696,184 bytes copied during GC
         148,656 bytes maximum residency (1 sample(s))
          30,664 bytes maximum slop
               2 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0        15 colls,     0 par    0.00s    0.00s     0.0001s    0.0005s
  Gen  1         1 colls,     0 par    0.00s    0.00s     0.0010s    0.0010s
3,838,048 bytes allocated in the heap
         689,592 bytes copied during GC
         148,368 bytes maximum residency (1 sample(s))
          27,040 bytes maximum slop
               2 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0         6 colls,     0 par    0.00s    0.00s     0.0001s    0.0003s
  Gen  1         1 colls,     0 par    0.00s    0.00s     0.0006s    0.0006s
import Data.Conduit as C
import qualified Data.Conduit.Binary as Cb
import qualified Data.Conduit.List as Cl
import qualified Data.Conduit.Text as Ct
import qualified Data.Text as T
import qualified Data.Text.IO as TI
import Control.Monad.Trans (MonadIO, liftIO)
import System.Environment
import System.IO (stdin)

grabField :: Monad m => String -> Conduit T.Text m T.Text
grabField = Cl.filter . T.isInfixOf . T.pack

printField :: MonadIO m => T.Text -> m ()
printField field = liftIO $ do
  TI.putStrLn field
  putStr "\n\n"

fastSplit :: Char -> Bool
fastSplit ','  = True
fastSplit '\n' = True
fastSplit _    = False

main :: IO ()
main = do
  field:_ <- getArgs
  runResourceT $ Cb.sourceHandle stdin
              $$ Ct.decode Ct.utf8
              =$ Cl.concatMap (T.split fastSplit)
              =$ grabField field
              =$ Cl.mapM_ printField
835,688 bytes allocated in the heap 8,576 bytes copied during GC 87,200 bytes maximum residency (1 sample(s)) 19,968 bytes maximum slop 1 MB total memory in use (0 MB lost due to fragmentation) Tot time (elapsed) Avg pause Max pause Gen 0 1 colls, 0 par 0.00s 0.00s 0.0000s 0.0000s Gen 1 1 colls, 0 par 0.00s 0.00s 0.0008s 0.0008s