Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/arduino/2.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
使用GHC';s分析统计数据/图表以确定问题区域/提高Haskell代码的性能_Haskell_Profiling - Fatal编程技术网

使用GHC';s分析统计数据/图表以确定问题区域/提高Haskell代码的性能

使用GHC';s分析统计数据/图表以确定问题区域/提高Haskell代码的性能,haskell,profiling,Haskell,Profiling,TL;Dr:基于Haskell代码及其相关的剖析数据,我们可以得出什么结论,让我们修改/改进它,以便我们可以缩小性能差距,与用命令语言编写的相同算法(即C++ + Python/C语言,但特定语言并不重要)? 背景 我写了下面的一段代码,作为对一个流行站点上的一个问题的回答,该站点包含许多编程和/或数学性质的问题。(你可能听说过这个网站,有些人把它的名字读作“oiler”,有些人把它读作“yoolurr”),因为下面的代码是解决其中一个问题的方法,所以我有意避免在问题中提及这个网站的名字或任何特

TL;Dr:<强>基于Haskell代码及其相关的剖析数据,我们可以得出什么结论,让我们修改/改进它,以便我们可以缩小性能差距,与用命令语言编写的相同算法(即C++ + Python/C语言,但特定语言并不重要)? 背景 我写了下面的一段代码,作为对一个流行站点上的一个问题的回答,该站点包含许多编程和/或数学性质的问题。(你可能听说过这个网站,有些人把它的名字读作“oiler”,有些人把它读作“yoolurr”),因为下面的代码是解决其中一个问题的方法,所以我有意避免在问题中提及这个网站的名字或任何特定的术语。也就是说,我说的是问题103

(事实上,我在该网站的论坛上看到了许多来自Haskell居民向导的解决方案:p)

为什么我选择分析这段代码? 这是(在上述网站上)我遇到的第一个问题,即Haskell代码与C++/Python/C#代码(两者使用类似的算法)在性能上存在差异(以执行时间衡量)。事实上,所有问题都是这样(到目前为止,我已经做了100个问题,但不是连续的),优化的Haskell代码与最快的C++解决方案是非常相称的,当然是算法的例外。

然而,论坛中针对这个特定问题的帖子表明,这些其他语言中的相同算法通常最多需要1到2秒,最长需要10-15秒(假设相同的启动参数;我忽略了非常幼稚的算法,需要2-3分钟+)。相比之下,下面的Haskell代码在我的(体面的)计算机上需要约50秒(禁用评测;启用评测时,需要约2分钟,如下所示;注意:使用
-fllvm
编译时,执行时间相同)。规格:i5 2.4ghz笔记本电脑,8gb内存

在努力学习Haskell的过程中,它可以成为命令式语言的一个可行的替代品,我在解决这些问题中的一个目标是学习编写代码,在可能的范围内,它的性能与那些命令性语言相当。在这种情况下,我仍然认为这个问题还没有被我解决(因为在性能上几乎有25~x的差异)。 到目前为止我做了什么? 除了简化代码本身的明显步骤(尽我所能),我还执行了“真实世界Haskell”中推荐的标准评测练习

但我很难得出结论,告诉我哪些部分需要修改。这就是我希望人们能够提供一些指导的地方

问题描述: 我建议您访问上述网站上的问题一百零三的网站,但这里有一个简短的总结:目标是找到一个由七个数字组成的组,使(该组的)任何两个不相交的子组满足以下两个属性(出于上述原因,我试图避免使用“s-e-t”一词……:

  • 没有两个子组的总和相同
  • 元素越多的子组的和越大(换句话说,最小的四个元素的和必须大于最大的三个元素的和)
特别是,我们正试图找到七个数之和最小的一组

我(无可否认)的观察 警告:其中一些评论可能完全错误,但我想至少尝试根据我在现实世界Haskell和其他与评测相关的帖子中读到的内容来解释评测数据

  • 考虑到三分之一的时间是如何花在垃圾收集上的(37.1%),似乎确实存在效率问题。第一张图表显示~172gb被分配到堆中,这看起来很可怕。。。(也许有更好的结构/功能用于实现动态规划解决方案?)
  • 毫不奇怪,绝大多数(83.1%)的时间都花在检查规则1上:(i)41.6%在
    value
    子函数中,该子函数确定要填入动态规划(“DP”)表的值;(ii)29.1%在
    table
    函数中,该函数生成DP表;(iii)12.4%在
    规则1
    函数中,检查生成的DP表,以确保只能以一种方式(即从一个子组)计算给定的总和
  • 然而,我确实感到惊讶的是,相对于
    规则1
    函数,
    函数花费了更多的时间,因为它是三个函数中唯一一个不构建数组或不过滤大量元素的函数(实际上只执行O(1)查找并在
    Int
    类型之间进行比较,您认为这会相对较快)。所以这是一个潜在的问题领域。也就是说,
    value
    函数不太可能驱动高堆分配
坦率地说,我不知道该如何看待这三张图表

堆配置文件图表(即下面的第一个字符):

  • 老实说,我不确定标记为
    钉住的红色区域代表什么。
    dynamic
    函数有一个“尖头”内存分配是有意义的,因为每次
    construct
    函数生成满足前三个条件的元组时,都会调用它,并且每次调用它时,都会创建一个相当大的DP数组。此外,我认为存储元组(由构造生成)的内存分配在整个程序过程中不会是平坦的
  • 在“钉住”红色区域的澄清之前,我不确定这个区域是否告诉我们任何有用的信息
分配
{-# LANGUAGE NoImplicitPrelude #-}
{-# OPTIONS_GHC -Wall #-}

import CorePrelude
import Data.Array
import Data.List
import Data.Bool.HT ((?:))
import Control.Monad (guard)

main = print (minimum construct)

cap = 55 :: Int
flr = 20 :: Int
step = 1 :: Int

--we enumerate tuples that are potentially valid and then
--filter for valid ones; we perform the most computationally
--expensive step (i.e., rule 1) at the very end
construct :: [[Int]]
construct = {-# SCC "construct" #-} do
  a <- [flr..cap]                         --1st: we construct potentially valid tuples while applying a
  b <- [a+step..cap]                      --constraint on the upper bound of any element as implied by rule 2
  c <- [b+step..a+b-1]
  d <- [c+step..a+b-1]
  e <- [d+step..a+b-1]
  f <- [e+step..a+b-1]
  g <- [f+step..a+b-1]
  guard (a + b + c + d - e - f - g > 0)   --2nd: we screen for tuples that completely conform to rule 2
  let nn = [g,f,e,d,c,b,a]
  guard (sum nn < 285)                    --3rd: we screen for tuples of a certain size (a guess to speed things up)
  guard (rule1 nn)                        --4th: we screen for tuples that conform to rule 1
  return nn

rule1 :: [Int] -> Bool
rule1 nn = {-# SCC "rule1" #-} 
    null . filter ((>1) . snd)           --confirm that there's only one subgroup that sums to any given sum
  . filter ((length nn==) . snd . fst)   --the last column us how many subgroups sum to a given sum
  . assocs                               --run the dynamic programming algorithm and generate a table
  $ dynamic nn

dynamic :: [Int] -> Array (Int,Int) Int
dynamic ns = {-# SCC "dynamic" #-} table
  where
    (len, maxSum) = (length &&& sum) ns
    table = array ((0,0),(maxSum,len)) 
      [ ((s,i),x) | s <- [0..maxSum], i <- [0..len], let x = value (s,i) ]
    elements = listArray (0,len) (0:ns)
    value (s,i)
      | i == 0 || s == 0 = 0
      | s ==  m = table ! (s,i-1) + 1
      | s > m = s <= sum (take i ns) ?: 
          (table ! (s,i-1) + table ! ((s-m),i-1), 0)
      | otherwise = 0
      where
        m = elements ! i
% ghc -O2 --make 103_specialsubset2.hs -rtsopts -prof -auto-all -caf-all -fforce-recomp
[1 of 1] Compiling Main             ( 103_specialsubset2.hs, 103_specialsubset2.o )
Linking 103_specialsubset2 ...
% time ./103_specialsubset2.hs +RTS -p -sstderr
zsh: permission denied: ./103_specialsubset2.hs
./103_specialsubset2.hs +RTS -p -sstderr  0.00s user 0.00s system 86% cpu 0.002 total
% time ./103_specialsubset2 +RTS -p -sstderr
SOLUTION REDACTED
 172,449,596,840 bytes allocated in the heap
  21,738,677,624 bytes copied during GC
         261,128 bytes maximum residency (74 sample(s))
          55,464 bytes maximum slop
               2 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0     327548 colls,     0 par   27.34s   41.64s     0.0001s    0.0092s
  Gen  1        74 colls,     0 par    0.02s    0.02s     0.0003s    0.0013s

  INIT    time    0.00s  (  0.01s elapsed)
  MUT     time   53.91s  ( 70.60s elapsed)
  GC      time   27.35s  ( 41.66s elapsed)
  RP      time    0.00s  (  0.00s elapsed)
  PROF    time    0.00s  (  0.00s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time   81.26s  (112.27s elapsed)

  %GC     time      33.7%  (37.1% elapsed)

  Alloc rate    3,199,123,974 bytes per MUT second

  Productivity  66.3% of total user, 48.0% of total elapsed

./103_specialsubset2 +RTS -p -sstderr  81.26s user 30.90s system 99% cpu 1:52.29 total
    Wed Dec 17 23:21 2014 Time and Allocation Profiling Report  (Final)

       103_specialsubset2 +RTS -p -sstderr -RTS

    total time  =       15.56 secs   (15565 ticks @ 1000 us, 1 processor)
    total alloc = 118,221,354,488 bytes  (excludes profiling overheads)

COST CENTRE     MODULE  %time %alloc

dynamic.value   Main     41.6   17.7
dynamic.table   Main     29.1   37.8
construct       Main     12.9   37.4
rule1           Main     12.4    7.0
dynamic.table.x Main      1.9    0.0


                                                                    individual     inherited
COST CENTRE               MODULE                  no.     entries  %time %alloc   %time %alloc

MAIN                      MAIN                     55           0    0.0    0.0   100.0  100.0
 main                     Main                    111           0    0.0    0.0     0.0    0.0
 CAF:main1                Main                    108           0    0.0    0.0     0.0    0.0
  main                    Main                    110           1    0.0    0.0     0.0    0.0
 CAF:main2                Main                    107           0    0.0    0.0     0.0    0.0
  main                    Main                    112           0    0.0    0.0     0.0    0.0
 CAF:main3                Main                    106           0    0.0    0.0     0.0    0.0
  main                    Main                    113           0    0.0    0.0     0.0    0.0
 CAF:construct            Main                    105           0    0.0    0.0   100.0  100.0
  construct               Main                    114           1    0.6    0.0   100.0  100.0
   construct              Main                    115           1   12.9   37.4    99.4  100.0
    rule1                 Main                    123      282235    0.6    0.0    86.5   62.6
     rule1                Main                    124      282235   12.4    7.0    85.9   62.6
      dynamic             Main                    125      282235    0.2    0.0    73.5   55.6
       dynamic.elements   Main                    133      282235    0.3    0.1     0.3    0.1
       dynamic.len        Main                    129      282235    0.0    0.0     0.0    0.0
       dynamic.table      Main                    128      282235   29.1   37.8    72.9   55.5
        dynamic.table.x   Main                    130   133204473    1.9    0.0    43.8   17.7
         dynamic.value    Main                    131   133204473   41.6   17.7    41.9   17.7
          dynamic.value.m Main                    132   132640003    0.3    0.0     0.3    0.0
       dynamic.maxSum     Main                    127      282235    0.0    0.0     0.0    0.0
       dynamic.(...)      Main                    126      282235    0.1    0.0     0.1    0.0
    dynamic               Main                    122      282235    0.0    0.0     0.0    0.0
    construct.nn          Main                    121    12683926    0.0    0.0     0.0    0.0
 CAF:main4                Main                    102           0    0.0    0.0     0.0    0.0
  construct               Main                    116           0    0.0    0.0     0.0    0.0
   construct              Main                    117           0    0.0    0.0     0.0    0.0
 CAF:cap                  Main                    101           0    0.0    0.0     0.0    0.0
  cap                     Main                    119           1    0.0    0.0     0.0    0.0
 CAF:flr                  Main                    100           0    0.0    0.0     0.0    0.0
  flr                     Main                    118           1    0.0    0.0     0.0    0.0
 CAF:step_r1dD            Main                     99           0    0.0    0.0     0.0    0.0
  step                    Main                    120           1    0.0    0.0     0.0    0.0
 CAF                      GHC.IO.Handle.FD         96           0    0.0    0.0     0.0    0.0
 CAF                      GHC.Conc.Signal          93           0    0.0    0.0     0.0    0.0
 CAF                      GHC.IO.Encoding          91           0    0.0    0.0     0.0    0.0
 CAF                      GHC.IO.Encoding.Iconv    82           0    0.0    0.0     0.0    0.0
-- List.hs -- using list comprehensions

import Control.Monad

cap = 55 :: Int
flr = 20 :: Int
step = 1 :: Int

construct :: [[Int]]
construct =  do
  a <- [flr..cap]                         
  b <- [a+step..cap]                      
  c <- [b+step..a+b-1]
  d <- [c+step..a+b-1]
  e <- [d+step..a+b-1]
  f <- [e+step..a+b-1]
  g <- [f+step..a+b-1]
  guard (a + b + c + d - e - f - g > 0)
  guard (a + b + c + d + e + f + g < 285)
  return  [g,f,e,d,c,b,a]
  -- guard (rule1 nn)

main = do
  forM_ construct print


-- Loops.hs -- using imperative looping

import Control.Monad

loop a b f = go a
  where go i | i > b     = return ()
             | otherwise = do f i; go (i+1)

cap = 55 :: Int
flr = 20 :: Int
step = 1 :: Int

main =
  loop flr cap $ \a ->
  loop (a+step) cap $ \b ->
  loop (b+step) (a+b-1) $ \c ->
  loop (c+step) (a+b-1) $ \d ->
  loop (d+step) (a+b-1) $ \e ->
  loop (e+step) (a+b-1) $ \f ->
  loop (f+step) (a+b-1) $ \g ->
    if (a+b+c+d-e-f-g > 0) && (a+b+c+d+e+f+g < 285)
      then print [g,f,e,d,c,b,a]
      else return ()
                          Lists.hs    Loops.hs
  Heap allocation        44,913 MB    2,740 MB
  Max. Residency            44,312      44,312
  %GC                        5.8 %       1.7 %
  Total Time             9.48 secs   1.43 secs