Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.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 有没有一种方法可以让我的字数计算程序更快而不使用不纯的技巧?_Performance_Haskell_Functional Programming_Immutability - Fatal编程技术网

Performance 有没有一种方法可以让我的字数计算程序更快而不使用不纯的技巧?

Performance 有没有一种方法可以让我的字数计算程序更快而不使用不纯的技巧?,performance,haskell,functional-programming,immutability,Performance,Haskell,Functional Programming,Immutability,作为一个小练习,我用haskell编写了下面的单词计数程序。它统计文本文件中不同的单词,并输出50个最频繁的单词及其频率 import qualified Data.Map as Map import Data.List.Split import Data.List import Data.Ord -- Count words count = Map.toList . foldl' increment Map.empty where increment dict k =

作为一个小练习,我用haskell编写了下面的单词计数程序。它统计文本文件中不同的单词,并输出50个最频繁的单词及其频率

import qualified Data.Map as Map
import Data.List.Split
import Data.List
import Data.Ord

-- Count words
count = Map.toList . foldl' increment Map.empty
    where
        increment dict k = Map.insert k (1 + Map.findWithDefault 0 k dict) dict

-- Sort the counts
countAndSort = sortBy (flip $ comparing snd) . count

-- Pretty printing
pp :: Show a => [(String,a)] -> IO()
pp = putStrLn . foldl' format "" where
    format text (x,y) = text ++ "\n" ++ x ++ "\t" ++ show y

main = readFile  "pg13951.txt" >>= pp . take 50 .countAndSort . splitOn " "
问题是,它比我的python实现慢16倍,使用可变dict:

def increment(dic,word):
    dic[word] = dic.get(word,0) + 1
    return dic

print sorted(reduce(increment,open("pg13951.txt").read().split(),{}).items(),key=lambda e:-e[1])[:50]
我认为问题是因为ghc不断地重新分配新地图,而它可以反复重复使用同一张地图。运行时统计显示了大量分配:

$ ghc -rtsopts count.hs
$ ./count +RTS -sstderr

de      7682
et      4423
la      4238
<snip>
d'Artagnan      511
M.      502
c'est   443
d'Artagnan,     443

     705,888,048 bytes allocated in the heap
     655,511,720 bytes copied during GC
     139,823,800 bytes maximum residency (10 sample(s))
       1,049,416 bytes maximum slop
             287 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0      1366 colls,     0 par    2.16s    2.26s     0.0017s    0.0072s
  Gen  1        10 colls,     0 par    2.86s    3.09s     0.3093s    1.5055s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    3.18s  (  3.36s elapsed)
  GC      time    5.02s  (  5.36s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    8.20s  (  8.72s elapsed)

  %GC     time      61.2%  (61.4% elapsed)

  Alloc rate    221,831,366 bytes per MUT second

  Productivity  38.8% of total user, 36.5% of total elapsed
$ghc-rtsopts count.hs
美元/计数+RTS-sstderr
de 7682
et 4423
洛杉矶4238
达达尼安511
M.502
c'est 443
达塔南,443
堆中分配的705888048字节
GC期间复制的655511720字节
139823800字节最大驻留时间(10个示例)
1049416字节最大斜率
使用的总内存为287 MB(由于碎片而丢失0 MB)
Tot时间(已用)平均暂停最大暂停
Gen 0 1366 Cels,0 PAR 2.16S 2.26S 0.00 17S 0.00 72S
Gen 1 10 Cels,0 PAR 2.86S 3.09S 0.3093S 1.5055秒
初始时间0.00s(经过0.00s)
MUT时间3.18秒(经过3.36秒)
GC时间5.02秒(经过5.36秒)
退出时间0.00s(经过0.00s)
总时间8.20秒(经过8.72秒)
%GC时间61.2%(经过61.4%)
每分钟分配速率221831366字节
生产力占总用户的38.8%,占总运行时间的36.5%
我的问题是:有没有一种方法可以让这个程序运行得更好,而不必使用诸如在IO monad中工作、使用可变数据结构等肮脏的技巧


PS:数据文件可从以下URL获得:

以下是我尝试过的一些快速而简单的优化

我的计算机上的原始版本:

real    0m1.539s
user    0m1.452s
sys 0m0.076s
  • 您可以使用
    fromListWith
    来计数,而不是使用
    insert
    foldl'
    这些话

    count = Map.toList . Map.fromListWith (+) . flip zip (repeat 1)
    
    这比以前快了一倍多

    real    0m0.687s
    user    0m0.648s
    sys 0m0.032s
    
  • 字符串
    类型是一个字符的链接列表,它使 弦乐相当优雅,但效率低下。我们可以使用
    文本
    类型获取更多信息 高效的字符串处理。我还重写了
    pp
    函数以使用
    unlines
    代替
    foldl'
    并使用
    words
    代替
    splitOn
    进行原始拆分

    {-# LANGUAGE OverloadedStrings #-}
    
    import Data.Monoid
    import Data.Text (Text)
    import qualified Data.Text as T
    import qualified Data.Text.IO as T
    
    pp :: Show a => [(Text,a)] -> IO()
    pp = T.putStrLn . T.unlines . map format where
        format (x,y) = x <> "\t" <> (T.pack $ show y)
    
    main = T.readFile  "pg13951.txt" >>= pp . take 50 .countAndSort . T.words
    
  • 使用严格版本的
    Map

    import qualified Data.Map.Strict as Map
    
    大约20%的速度增加

    real    0m0.265s
    user    0m0.252s
    sys 0m0.008s
    

  • 使用
    alter
    代替
    find+insert
    应该会更好!这正是我希望得到的结果。请注意,
    words
    给出的结果与
    splitOn”“
    稍有不同,因为它在所有空格(包括换行符)上都会分割。但是,我认为这是本例中所需的行为。请注意,并不是
    fromListWith
    本身让您得到了改进。它的内部使用了一个更好的版本:<代码>增量>代码>,即“代码>增量DIST K= MAP.StUpToT(+)K 1 DICT<代码>。您也可以考虑使用<代码>数据。HASMAP。严格< <代码> > <无序容器>代码>,它在我的系统上运行得更好。如果使用DAT.MAP.Read,它是如何执行的?如果查看Data.Map的源代码,您将看到默认实现是Data.Map.Lazy,如果“最终需要存储所有值”和“存储的值不代表要延迟计算的大型虚拟数据结构”,则应使用Data.Map.Strict。在我看来,这正好描述了你的情况。@its布鲁斯:我试过了,但没什么变化。根据@shang的回答,我认为最大的问题是使用
    String
    而不是
    Data.Text
    。今晚我将进行更多测试。您还可以并行化代码,wordcount是mapReduce风格计算的典型示例;您还可以尝试使用
    -threaded
    编译,并使用
    +RTS-N
    运行,以在您的计算机上使用更多的内核machine@jev当然,但这不是问题的关键所在:至少你可以免费获得更多的线索,我不认为这是不正当的伎俩。
    real    0m0.265s
    user    0m0.252s
    sys 0m0.008s