99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

第二十五章:性能分析與優(yōu)化

2018-08-12 22:20 更新

Haskell 是一門高級編程語言,一門真正的高級編程語言。 我們可以一直使用抽象概念、幺半群、函子、以及多態(tài)進(jìn)行編程,而不必與任何特定的硬件模型打交道。Haskell 在語言規(guī)范方面下了很大的功夫,力求語言可以不受制于某個(gè)特定的求值模型。這幾層抽象使得我們可以把 Haskell 作為計(jì)算本身的記號,讓編程人員關(guān)心他們問題的關(guān)鍵點(diǎn),而不用操心低層次的實(shí)現(xiàn)細(xì)節(jié),使得人們可以心無旁騖地進(jìn)行編程。

但是,本書介紹的是真實(shí)世界中的編程行為,而真實(shí)世界中的代碼都運(yùn)行在資源有限的硬件之上,并且程序也會(huì)有時(shí)間和空間上的限制。因此,我們需要掌握好表達(dá)程序數(shù)據(jù)的方法,準(zhǔn)確地理解使用惰性求值和嚴(yán)格求值策略帶來的后果,并學(xué)會(huì)如何分析和控制程序在執(zhí)行時(shí)的時(shí)間和空間。

在這一章,我們將看到 Haskell 編程中可能遇到的典型空間和時(shí)間問題,并且如何有條理地分析、理解并解決它們。為此我們將研究使用一系列的技術(shù):時(shí)間和空間使用分析,運(yùn)行時(shí)統(tǒng)計(jì),以及對嚴(yán)格求值和惰性求值。我們也會(huì)看下編譯器優(yōu)化對性能的影響,以及高級優(yōu)化技術(shù)在純函數(shù)式編程語言中的應(yīng)用。那么,讓我們用一個(gè)挑戰(zhàn)開始吧:調(diào)查一個(gè)簡單程序中不必要內(nèi)存的使用問題。

Haskell程序性能分析?

請看下面這個(gè)列表處理程序,它用于計(jì)算某個(gè)大型列表的平均值。這里展示的只是程序的其中一部分代碼(并且具體的實(shí)現(xiàn)算法我們并不關(guān)心),這是我們經(jīng)常會(huì)在真實(shí)的 Haskell 程序中看到的典型的簡單列表操作代碼,這些代碼大量地使用標(biāo)準(zhǔn)庫函數(shù),并且包含了一些因?yàn)槭韬龃笠舛鴮?dǎo)致的性能問題。這里也展示了幾種因疏忽而易出現(xiàn)的性能問題。

-- file: ch25/A.hs
import System.Environment
import Text.Printf

main = do
    [d] <- map read `fmap` getArgs
    printf "%f\n" (mean [1..d])

mean :: [Double] -> Double
mean xs = sum xs / fromIntegral (length xs)

這個(gè)程序非常簡單:我們引用了訪問系統(tǒng)環(huán)境的函數(shù)(即 getArgs ),和 Haskell 版的 printf 函數(shù)來格式化輸出。接著這個(gè)程序從命令行讀入一個(gè)數(shù)字來構(gòu)建一個(gè)由浮點(diǎn)數(shù)組成的列表。我們用這個(gè)列表的和除以列表的長度得到平均值,然后以字符串的形式打印出來。我們來將此代碼編譯成機(jī)器代碼(打開優(yōu)化開關(guān))然后用 time 執(zhí)行它看看情況吧:

$ ghc --make -rtsopts -O2 A.hs
[1 of 1] Compiling Main             ( A.hs, A.o )
Linking A ...
$ time ./A 1e5
50000.5
./A 1e5  0.05s user 0.01s system 102% cpu 0.059 total
$ time ./A 1e6
500000.5
./A 1e6  0.26s user 0.04s system 99% cpu 0.298 total
$ time ./A 1e7
5000000.5
./A 1e7  63.80s user 0.62s system 99% cpu 1:04.53 total

程序在處理少量輸入時(shí)運(yùn)行得非常好,但是當(dāng)輸入的列表元素?cái)?shù)量達(dá)到一千萬個(gè)時(shí),程序的運(yùn)行速度就會(huì)變得相當(dāng)緩慢。從這點(diǎn)我們就能感覺到應(yīng)該有什么地方做得不對,但我們并不清楚它的資源使用情況。 我們需要研究下。

記錄運(yùn)行時(shí)統(tǒng)計(jì)數(shù)據(jù)?

為了能收集這些情報(bào),GHC支持傳入 +RTS-RTS 這些特殊選項(xiàng)。這些選項(xiàng)是傳給 Haskell 運(yùn)行時(shí)本身的,Haskell程序?yàn)闀?huì)感知到它們的存在。

特別地,我們可以用 -s 選項(xiàng)來讓運(yùn)行時(shí)系統(tǒng)收集內(nèi)存和垃圾收集性能參數(shù)(并可以用 -N來控制系統(tǒng)線程的數(shù)量,或調(diào)整棧和堆的大小)。我們將用運(yùn)行時(shí)選項(xiàng)來打開性能分析的幾種不同變量。Haskell 運(yùn)行時(shí)所能接受的所有選項(xiàng)列表可以參見GHC用戶手冊。

那么讓我們用 +RTS -sstderr 來運(yùn)行程序收集我們所需要的結(jié)果吧。

$ ./A 1e7 +RTS -sstderr
5000000.5
1,689,133,824 bytes allocated in the heap
697,882,192 bytes copied during GC (scavenged)
465,051,008 bytes copied during GC (not scavenged)
382,705,664 bytes maximum residency (10 sample(s))

       3222 collections in generation 0 (  0.91s)
         10 collections in generation 1 ( 18.69s)

        742 Mb total memory in use

  INIT  time    0.00s  (  0.00s elapsed)
  MUT   time    0.63s  (  0.71s elapsed)
  GC    time   19.60s  ( 20.73s elapsed)
  EXIT  time    0.00s  (  0.00s elapsed)
  Total time   20.23s  ( 21.44s elapsed)

  %GC time      96.9%  (96.7% elapsed)

  Alloc rate    2,681,318,018 bytes per MUT second

  Productivity   3.1% of total user, 2.9% of total elapsed

當(dāng)使用 -sstderr 時(shí),程序的性能數(shù)字會(huì)輸出到標(biāo)準(zhǔn)錯(cuò)誤流里,告訴我們很多關(guān)于程序在具體做什么的信息。具體地說,它告訴我們GC用了多少時(shí)間以及最大活動(dòng)內(nèi)存使用情況。原來程序計(jì)算一千萬個(gè)元素的平均值在堆上使用了多達(dá)742M的你內(nèi)存,并且 96.9% 的時(shí)間是花費(fèi)到垃圾收集上的!總共只有 3.1% 的時(shí)間是程序用來干正事的。

那么為什么我們的程序運(yùn)行情況這么差?我們?nèi)绾蝸硖岣咚??畢竟,Haskell是一個(gè)惰性語言:它不應(yīng)該只用恒定的內(nèi)存空間來處理列表嗎?

時(shí)間消耗分析?

慶幸的是,GHC提供了幾個(gè)工具可以分析程序的時(shí)間和空間使用情況。尤其是,我們可以在編寫程序時(shí)打開性能調(diào)試選項(xiàng),這樣程序在執(zhí)行時(shí)會(huì)得到每個(gè)函數(shù)所使用的資源使用的信息。性能調(diào)試過程分為三步:以性能調(diào)試方式編譯程序;用性能調(diào)試模式執(zhí)行程序;最后是分析收集到的結(jié)果數(shù)據(jù)。

編譯程序時(shí),我們可以打開 -prof 選項(xiàng)來記錄基本時(shí)間和空間性能信息。我們也需要給感興趣的函數(shù)標(biāo)記為 “cost centres” 以便讓性能分析程序知曉。一個(gè) cost centre 即是一個(gè)信息收集點(diǎn)。GHC會(huì)生成代碼來計(jì)算這些地方的資源消耗。我們可以用 SCC 語法使得任何語句成為 cost centre。

-- file: ch25/SCC.hs
mean :: [Double] -> Double
mean xs = {-# SCC "mean" #-} sum xs / fromIntegral (length xs)

另外我們也可以用 -auto-all 選項(xiàng)來讓編譯器將所有頂級函數(shù)設(shè)為 cost centre。手動(dòng)添加 cost centre 的能力作為自動(dòng) cost centre 性能調(diào)試的輔助,使得我們可以在發(fā)現(xiàn)某個(gè)熱點(diǎn)(hot spot)有問題時(shí),針對性地分析一個(gè)函數(shù)的資源消耗。

另外需要注意的一個(gè)難點(diǎn)是:在 Haskell 這類惰性、純函數(shù)式編程語言里,沒有參數(shù)的值只會(huì)被計(jì)算一次(比如之前的程序展示過的巨大的列表),而計(jì)算的結(jié)果會(huì)被之后的所有其他計(jì)算所共享。這些值不是程序調(diào)用的其中一部分,因此它們不會(huì)每次都被計(jì)算。當(dāng)然我們?nèi)匀恍枰u估它們的唯一一次計(jì)算的資源占用情況。這些只會(huì)計(jì)算一次的值被成為 CAF (Constant Applicative Forms),它們的確切數(shù)量可以通過 -caf-all 選項(xiàng)來得到。

那么以分析性能的方式來編譯我們的程序吧(用 -fforce-recomp 選項(xiàng)來強(qiáng)制重新編譯所有部分):

$ ghc -O2 --make A.hs -prof -auto-all -caf-all -fforce-recomp
[1 of 1] Compiling Main             ( A.hs, A.o )
Linking A ...

現(xiàn)在我們可以執(zhí)行這個(gè)標(biāo)記了性能分析點(diǎn)的程序了 (標(biāo)記了性能分析的程序會(huì)變慢,所以我們用一個(gè)較小的輸入來執(zhí)行):

$ time ./A  1e6 +RTS -p
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize' to increase it.
./A 1e6 +RTS -p  1.11s user 0.15s system 95% cpu 1.319 total

程序竟然把??臻g耗完了!這就是使用 profiling 時(shí)需要注意的主要影響:給程序加Cost Centre會(huì)使它的優(yōu)化發(fā)生變化,甚至改變它的運(yùn)行時(shí)行為,因?yàn)槊恳粋€(gè)表達(dá)式都被附加了額外代碼來檢測它們的執(zhí)行情況。對于我們這樣情況,修正起來很簡單 —— 只需要用GHC運(yùn)行時(shí)標(biāo)記 -K 來增加棧空間上限即可(并附帶指示大小的后綴):

$ time ./A 1e6 +RTS -p -K100M
500000.5
./A 1e6 +RTS -p -K100M  4.27s user 0.20s system 99% cpu 4.489 total

運(yùn)行時(shí)會(huì)將性能信息生成到一個(gè)名字為 A.prof (以程序本身名字命名) 的文件中。其中含有以下信息:

$ cat A.prof

Time and Allocation Profiling Report  (Final)

       A +RTS -p -K100M -RTS 1e6

    total time  =        0.28 secs   (14 ticks @ 20 ms)
    total alloc = 224,041,656 bytes  (excludes profiling overheads)

COST CENTRE  MODULE               %time %alloc

CAF:sum      Main                  78.6   25.0
CAF          GHC.Float             21.4   75.0

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

MAIN        MAIN            1           0   0.0    0.0   100.0  100.0
 main       Main          166           2   0.0    0.0     0.0    0.0
  mean      Main          168           1   0.0    0.0     0.0    0.0
 CAF:sum    Main          160           1  78.6   25.0    78.6   25.0
 CAF:lvl    Main          158           1   0.0    0.0     0.0    0.0
  main      Main          167           0   0.0    0.0     0.0    0.0
 CAF        Numeric       136           1   0.0    0.0     0.0    0.0
 CAF        Text.Read.Lex 135           9   0.0    0.0     0.0    0.0
 CAF        GHC.Read      130           1   0.0    0.0     0.0    0.0
 CAF        GHC.Float     129           1  21.4   75.0    21.4   75.0
 CAF        GHC.Handle    110           4   0.0    0.0     0.0    0.0

這些信息呈現(xiàn)給一些我們關(guān)于程序的運(yùn)行時(shí)行為的情況。里面包含了程序的名字以及執(zhí)行程序時(shí)用到的選項(xiàng)和參數(shù)?!簍otal time』是運(yùn)行時(shí)系統(tǒng)視角所見的程序運(yùn)行的確切總時(shí)長。『Total allocation』是程序在運(yùn)行過程中分配的內(nèi)存總字節(jié)數(shù)(不是程序運(yùn)行時(shí)內(nèi)存使用的峰值;那個(gè)峰值大概是700MB)

報(bào)告中的第二小節(jié)是各個(gè)函數(shù)所消耗的時(shí)間和空間部分。第三小節(jié)是Cost Centre報(bào)告:其結(jié)構(gòu)為調(diào)用關(guān)系樹(比如我們可以看到 mean 是被 main 調(diào)用的)?!癷ndividual”和”inherited”列提供了每個(gè)Cost Centre其本身、其整體、以及其子節(jié)點(diǎn)所消耗的資源。最下面那些 CAF 是執(zhí)行一些常量的一次性消耗(例如大列表中浮點(diǎn)數(shù)以及列表本身)。

我們能從這些信息得出什么結(jié)論呢?我們可以看出兩個(gè) CAF 占用了大多數(shù)時(shí)間。分別是計(jì)算總和相關(guān)和浮點(diǎn)數(shù)相關(guān)。這基本解釋了所有程序運(yùn)行的消耗。加上之前我們觀察到的關(guān)于GC的壓力,我們就可以推測出列表節(jié)點(diǎn)和浮點(diǎn)數(shù)值可能是問題之源。

簡單的性能熱點(diǎn)檢測,特別是對于我們難以知道時(shí)間花費(fèi)點(diǎn)的大型程序,這個(gè)時(shí)間分析會(huì)突出一些特定的問題模塊或高層函數(shù)。這往往足夠顯示出問題所在了。就像我們的程序,一旦我們窄化了問題所在,我們就可以用更加成熟的分析工具來拿到更多的信息。

空間性能調(diào)試?

GHC除了可以進(jìn)行基本的時(shí)間和空間分析外,還能為程序整個(gè)執(zhí)行期間的堆內(nèi)存使用情況生成圖表。這能完美檢測內(nèi)存泄露問題。內(nèi)存泄露是指不再需要的內(nèi)存沒有被釋放。這會(huì)對GC造成壓力,就像在我們的例子程序中見到的那樣。

構(gòu)建內(nèi)存占用情況的性能調(diào)試和構(gòu)建一般時(shí)間占用調(diào)試的步驟是一樣,都需要用到 -prof -auto-all -caf-all 選項(xiàng)。 但當(dāng)執(zhí)行程序時(shí),我們會(huì)讓運(yùn)行時(shí)系統(tǒng)收集關(guān)于堆使用的最多細(xì)節(jié)。堆使用信息能夠以幾種方式分解:cost-centre,模塊(module),構(gòu)建器,數(shù)據(jù)類型。不同分解可以展現(xiàn)不同信息。對 A.hs 進(jìn)行內(nèi)存調(diào)試所得的原始信息會(huì)被記錄到一個(gè)名為 A.hp 的文件里面,之后只要使用 hp2ps 處理這個(gè)文件,就可以得到一個(gè)基于 PostScript 的內(nèi)存占用歷史可視圖像。

程序的一般內(nèi)存占用情況可以通過使用 -hc 選項(xiàng)來獲?。?/p>

$ time ./A 1e6 +RTS -hc -p -K100M
500000.5
./A 1e6 +RTS -hc -p -K100M  4.15s user 0.27s system 99% cpu 4.432 total

一個(gè)內(nèi)存使用的性能調(diào)試的log文件 A.hp 被會(huì)創(chuàng)建,其內(nèi)容為以下形式:

JOB "A 1e6 +RTS -hc -p -K100M"
SAMPLE_UNIT "seconds"
VALUE_UNIT "bytes"
BEGIN_SAMPLE 0.00
END_SAMPLE 0.00
BEGIN_SAMPLE 0.24
(167)main/CAF:lvl   48
(136)Numeric.CAF    112
(166)main   8384
(110)GHC.Handle.CAF 8480
(160)CAF:sum    10562000
(129)GHC.Float.CAF  10562080
END_SAMPLE 0.24

這些樣本是以程序的正常執(zhí)行步長取得的。我們可以用 -iN 選項(xiàng)來增加取樣頻率,這里的 N 是內(nèi)存取樣之間相隔的秒數(shù)(如 0.01 秒)。很明顯,取樣越多,得到的結(jié)果越精確,但程序會(huì)執(zhí)行得更慢。我們可以用 hp2ps 將調(diào)試結(jié)果生成一張圖表:

$ hp2ps -e8in -c A.hp

這就是生成的圖表 A.ps

[IMG#TODO]

我們能從圖片中看出什么?第一點(diǎn),程序執(zhí)行分兩個(gè)階段:在計(jì)算總和的同時(shí), 前一階段不斷分配大量的內(nèi)存,后一半清理釋放這些內(nèi)存。內(nèi)存初始化分配的同時(shí), sum 也開始干活,并消耗大量的內(nèi)存。如果用 -hy 調(diào)試選項(xiàng)來按類型分解的話,我們會(huì)得到一個(gè)稍有不同的圖像:

$ time ./A 1e6 +RTS -hy -p -K100M
500000.5
./A 1e6 +RTS -i0.001 -hy -p -K100M  34.96s user 0.22s system 99% cpu 35.237 total
$ hp2ps -e8in -c A.hp

以下是生成的圖像:

[IMG#TODO]

這里最有趣的是有很大部分的內(nèi)存被list類型([])和Double類型所占用;我們看到未知類型(圖中用 * 標(biāo)記)也占用了一些內(nèi)存。最后,讓我們用 -hd 選項(xiàng)來按構(gòu)建器分解一下結(jié)果:

$ time ./A 1e6 +RTS -hd -p -K100M
$ time ./A 1e6 +RTS -i0.001 -hd -p -K100M
500000.5
./A 1e6 +RTS -i0.001 -hd -p -K100M  27.85s user 0.31s system 99% cpu 28.222 total

下面就是我們最后一張展示了程序執(zhí)行的所有情況的圖像:

[IMG#TODO]

程序在分配雙精度浮點(diǎn)數(shù)列表上面花了不少功夫。列表在 Haskell 語言中的惰性的,所以含有上百萬個(gè)元素的列表都是在程序執(zhí)行過程中一點(diǎn)點(diǎn)地構(gòu)建出來的。但這些元素在被遍歷的同時(shí)并沒有被逐步釋放,所以導(dǎo)致內(nèi)存占用變得越來越大。最終,在程序執(zhí)行稍稍超過一半時(shí),終于將列表總和計(jì)算出來,并開始計(jì)算其長度。如果看下關(guān)于 mean 的程序片斷的話,我們就會(huì)知道內(nèi)存沒被釋放的確切原因:

-- file: ch25/Fragment.hs
mean :: [Double] -> Double
mean xs = sum xs / fromIntegral (length xs)

首先我們計(jì)算列表的總和,這會(huì)使得所有列表元素被分配到內(nèi)存。但我們現(xiàn)在還不能釋放列表元素,因?yàn)?length 還需要整個(gè)列表。一旦 sum 結(jié)束, length 會(huì)馬上開始訪問列表,同時(shí) GC 會(huì)跟進(jìn),逐步釋放列表元素,直到 length 結(jié)束。這兩個(gè)計(jì)算階段展示了兩種明顯不同的分配與釋放,并指出我們需要改進(jìn)的確切思路:只對列表遍歷一次,遍歷過程中同時(shí)計(jì)算總和與平均值。

-- file: ch25/B.hs
mean :: [Double] -> Double
mean xs = s / fromIntegral n
  where
    (n, s)     = foldl k (0, 0) xs
    k (n, s) x = (n+1, s+x)
$ ghc -O2 --make B.hs -fforce-recomp
$ time ./B 1e6
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize' to increase it.
./B 1e6  0.44s user 0.10s system 96% cpu 0.565 total
$ ghc -O2 --make B.hs -prof -auto-all -caf-all -fforce-recomp
[1 of 1] Compiling Main             ( B.hs, B.o )
Linking B ...
$ time ./B 1e6 +RTS -i0.001 -hc -p -K100M
500000.5
./B 1e6 +RTS -i0.001 -hc -p -K100M  38.70s user 0.27s system 99% cpu 39.241 total
-- file: ch25/C.hs
mean :: [Double] -> Double
mean xs = s / fromIntegral n
  where
    (n, s)     = foldl' k (0, 0) xs
    k (n, s) x = (n+1, s+x)
$ ghc -O2 --make C.hs
[1 of 1] Compiling Main             ( C.hs, C.o )
Linking C ...
$ time ./C 1e6
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize' to increase it.
./C 1e6  0.44s user 0.13s system 94% cpu 0.601 total
-- file: ch25/Foldl.hs
foldl' :: (a -> b -> a) -> a -> [b] -> a
foldl' f z xs = lgo z xs
    where lgo z []     = z
          lgo z (x:xs) = let z' = f z x in z' `seq` lgo z' xs
-- file: ch25/D.hs
mean :: [Double] -> Double
mean xs = s / fromIntegral n
  where
    (n, s)     = foldl' k (0, 0) xs
    k (n, s) x = n `seq` s `seq` (n+1, s+x)
$ ghc -O2 D.hs --make
[1 of 1] Compiling Main             ( D.hs, D.o )
Linking D ...
$ time ./D 1e6 +RTS -sstderr
./D 1e6 +RTS -sstderr
500000.5
256,060,848 bytes allocated in the heap
     43,928 bytes copied during GC (scavenged)
     23,456 bytes copied during GC (not scavenged)
     45,056 bytes maximum residency (1 sample(s))

        489 collections in generation 0 (  0.00s)
          1 collections in generation 1 (  0.00s)

          1 Mb total memory in use

  INIT  time    0.00s  (  0.00s elapsed)
  MUT   time    0.12s  (  0.13s elapsed)
  GC    time    0.00s  (  0.00s elapsed)
  EXIT  time    0.00s  (  0.00s elapsed)
  Total time    0.13s  (  0.13s elapsed)

  %GC time       2.6%  (2.6% elapsed)

  Alloc rate    2,076,309,329 bytes per MUT second

  Productivity  97.4% of total user, 94.8% of total elapsed

./D 1e6 +RTS -sstderr  0.13s user 0.00s system 95% cpu 0.133 total
-- file: ch25/E.hs
import System.Environment
import Text.Printf
import Control.Parallel.Strategies

main = do
    [d] <- map read `fmap` getArgs
    printf "%f\n" (mean [1..d])

foldl'rnf :: NFData a => (a -> b -> a) -> a -> [b] -> a
foldl'rnf f z xs = lgo z xs
    where
        lgo z []     = z
        lgo z (x:xs) = lgo z' xs
            where
                z' = f z x `using` rnf

mean :: [Double] -> Double
mean xs = s / fromIntegral n
  where
    (n, s)     = foldl'rnf k (0, 0) xs
    k (n, s) x = (n+1, s+x) :: (Int, Double)
-- file: ch25/F.hs
{-# LANGUAGE BangPatterns #-}
-- file: ch25/F.hs
mean :: [Double] -> Double
mean xs = s / fromIntegral n
  where
    (n, s)       = foldl' k (0, 0) xs
    k (!n, !s) x = (n+1, s+x)
$ ghc -O2 F.hs --make
$ time ./F 1e6 +RTS -sstderr
./F 1e6 +RTS -sstderr
500000.5
256,060,848 bytes allocated in the heap
     43,928 bytes copied during GC (scavenged)
     23,456 bytes copied during GC (not scavenged)
     45,056 bytes maximum residency (1 sample(s))

        489 collections in generation 0 (  0.00s)
          1 collections in generation 1 (  0.00s)

          1 Mb total memory in use

  INIT  time    0.00s  (  0.00s elapsed)
  MUT   time    0.14s  (  0.15s elapsed)
  GC    time    0.00s  (  0.00s elapsed)
  EXIT  time    0.00s  (  0.00s elapsed)
  Total time    0.14s  (  0.15s elapsed)

  %GC time       0.0%  (2.3% elapsed)

  Alloc rate    1,786,599,833 bytes per MUT second

  Productivity 100.0% of total user, 94.6% of total elapsed

./F 1e6 +RTS -sstderr  0.14s user 0.01s system 96% cpu 0.155 total
-- file: ch25/G.hs
data Pair a b = Pair !a !b
-- file: ch25/G.hs
mean :: [Double] -> Double
mean xs = s / fromIntegral n
  where
    Pair n s       = foldl' k (Pair 0 0) xs
    k (Pair n s) x = Pair (n+1) (s+x)
$ ghc -O2 -ddump-simpl G.hs
lgo :: Integer -> [Double] -> Double# -> (# Integer, Double #)

lgo = \ n xs s ->
    case xs of
      []       -> (# n, D# s #);
      (:) x ys ->
        case plusInteger n 1 of
            n' -> case x of
                D# y -> lgo n' ys (+## s y)
-- file: ch25/H.hs
data Pair = Pair !Int !Double

mean :: [Double] -> Double
mean xs = s / fromIntegral n
  where
    Pair n s       = foldl' k (Pair 0 0) xs
    k (Pair n s) x = Pair (n+1) (s+x)
lgo :: Int# -> Double# -> [Double] -> (# Int#, Double# #)
lgo = \ n s xs ->
    case xs of
      []       -> (# n, s #)
      (:) x ys ->
        case x of
            D# y -> lgo (+# n 1) (+## s y) ys
$ time ./H 1e7 +RTS -sstderr
./H 1e7 +RTS -sstderr
5000000.5
1,689,133,824 bytes allocated in the heap
    284,432 bytes copied during GC (scavenged)
         32 bytes copied during GC (not scavenged)
     45,056 bytes maximum residency (1 sample(s))

       3222 collections in generation 0 (  0.01s)
          1 collections in generation 1 (  0.00s)

          1 Mb total memory in use

  INIT  time    0.00s  (  0.00s elapsed)
  MUT   time    0.63s  (  0.63s elapsed)
  GC    time    0.01s  (  0.02s elapsed)
  EXIT  time    0.00s  (  0.00s elapsed)
  Total time    0.64s  (  0.64s elapsed)

  %GC time       1.0%  (2.4% elapsed)

  Alloc rate    2,667,227,478 bytes per MUT second

  Productivity  98.4% of total user, 98.2% of total elapsed

./H 1e7 +RTS -sstderr  0.64s user 0.00s system 99% cpu 0.644 total
-- file: ch25/I.hs
import System.Environment
import Text.Printf
import Data.Array.Vector

main = do
    [d] <- map read `fmap` getArgs
    printf "%f\n" (mean (enumFromToFracU 1 d))

data Pair = Pair !Int !Double

mean :: UArr Double -> Double
mean xs = s / fromIntegral n
  where
    Pair n s       = foldlU k (Pair 0 0) xs
    k (Pair n s) x = Pair (n+1) (s+x)
fold :: Int# -> Double# -> Double# -> (# Int#, Double# #)
fold = \ n s t ->
    case >## t limit of {
      False -> fold (+# n 1) (+## s t) (+## t 1.0)
      True  -> (# n, s #)
$ time ./I 1e7
5000000.5
./I 1e7  0.06s user 0.00s system 72% cpu 0.083 total
$ ghc -fforce-recomp --make -O2 -funbox-strict-fields -fvia-C -optc-O2 I.hs
[1 of 1] Compiling Main             ( I.hs, I.o )
Linking I ...
$ time ./I 1e7
5000000.5
./I 1e7  0.04s user 0.00s system 98% cpu 0.047 total
go:
  ucomisd     5(%rbx), %xmm6
  ja  .L31
  addsd       %xmm6, %xmm5
  addq        $1, %rsi
  addsd       .LC0(%rip), %xmm6
  jmp go
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號