Real World Haskell 25章

プロファイリングと最適化

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan,John Goerzen,Don Stewart
オライリージャパン / ¥ 3,990 ()
在庫あり。

  • GHCのランタイムには+RTS -RTSでランタイム専用の引数を渡せる
  • -fforce-recompで強制再コンパイル
  • ヒープの割り当て
  • WHNFに注意

融合

ストリーム融合というやつ

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)

臥龍梅の吟醸

久しぶりに臥龍梅のんだけど、すっきりしていてよかった。

1287485966

臥龍梅は純米(吟醸)よりは吟醸のほうが好きかも

エンジニア流ガベコレ

いわゆる参照カウントというやつですな。

ProductName ガベージコレクションのアルゴリズムと実装
中村 成洋,相川 光
秀和システム / ¥ 3,360 ()
在庫あり。

お見合いパーティーの文脈でマーク・アンド・スイープを考えてみると

  1. 本命に印をつける
  2. 本命に印をつけた女性から、1回の妥協で到達可能なすべての女性に印をつける(すでに印がついているものについては何もしない)
  3. 2の操作を、印がつかなくなるまで行う
  4. 印がついてない女性を無視する

参考

要するに次世代というか今風のGCは自分で断らなくてもマークしとくと親のほうが良きにはからってくれるみたいな感じ?

んー小町の見過ぎだな。過干渉GCとかなんかメリットを見いだせるかもしれんよ。介護GCとかforkされたプロセスが親プロセスの面倒をみるとか。

らーめん あかつき屋

情報処理の試験を受けに静岡に行ったので帰りに伊勢丹の裏手にあるあかつき屋に行ってきた。

1287309383

あっさりを注文。煮干が効いてて美味い。好みの味だった。

1287309380

来週も静岡に行く。Haskell読書会もあと三回。次回は切符遊びの章だな。

「Redmineによるタスクマネジメント実践技法」を読んだ

創薬研究というのは細かな作業が沢山あり、変化の激しいテスト工程なのに、そのインフラにはBTSとかITSとかないのが不自然だと思っていて、この前寄稿したときにちと触れといた。僕はソフトウェア業界にいるわけじゃないけど、問題解決の方法論としては似たような部分があるから参考になるんじゃないかと常々思っていたら、ズバリな本が出ることを知ってすぐに予約しといた。

で、一昨日届いたので今日の移動時に一気に読んだ。

ProductName Redmineによるタスクマネジメント実践技法
小川 明彦,阪井 誠
翔泳社 / ¥ 3,444 ()
在庫あり。

チケット駆動開発的なアプローチは自分の仕事管理では既に取り入れていたのであった。自分では文書駆動開発だと思っていたけど本書を読んだら、これは明らかにチケット駆動開発のコンテクストだよなと。

個人的には第一部の技法は非常に勉強になった。第二部は実際にRedmineをどう使うかという話はソフトウェア開発としては色々面白かったが、自分ではRedmine使ったことがないので、ピンと来ない話も幾つかあった。実際に使って見ながら本書を読み直せば新たな発見があるのだろうと思うので、今度、創薬のプロジェクトで、Redmine使えないか検討してみようっと。そうすれば創薬向けのITSとかBTSみたいなものがみえてくるような気がするんだよな。アジャイル創薬とかそういう方法論は一部試され始めているのでその先はこういうツールの重要性が認識されるだろうと思うんだよね。

それにしても、「ツールでサポートすれば行動が変わる。行動が変われば考え方が変わる」というのはいい言葉ですな。うちのインフラやってるチームにも本書を薦めてみようっと。

以下、メモ。

  • 紙の障害票は障害の歴史を残してもれなく対応するのが目的なのに対し、オープンソースのBTSは情報の共有を重視
  • チケット駆動開発のメリット
    • 開発メンバーの仕事が把握しやすくなる
    • ソースコードのコミット単位が明確になる
  • ソフトウェア開発プロジェクトには情報共有と見える化が必要
  • 測定できなければ制御できない
  • チケットの粒度
  • ニコニコカレンダー
  • MS Projectは顧客向けの進捗報告の視点、TracやRedmineは開発チーム内部の進捗管理の観点
  • ウォーターフォールは前工程の品質の悪さをそのまま引き継いでしまいがち
    • これは創薬でも一緒やな
  • アジャイルテストの四象限

Real World Haskell 24章 10

MapReduce、ただし、サンプルの通りでは動かない

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan,John Goerzen,Don Stewart
オライリージャパン / ¥ 3,990 ()
在庫あり。

Control.Parallel.Strategiesを読むと

  • NFDataはControl.DeepSeqに移った。
  • rnfはもはやStrategyにはないのでかわりにrdeepseqを使え

とのことなのでそう書きなおしてみた。

$ time ./LineCount +RTS -N1 -RTS test.log 
test.log: 2146144

real    0m1.273s
user    0m0.709s
sys     0m0.318s
$ time ./LineCount +RTS -N2 -RTS test.log 
test.log: 2146144

real    0m0.652s
user    0m0.781s
sys     0m0.373s

お、速くなった。

MapReduce.hs

module MapReduce
    (
      mapReduce
    , simpleMapReduce
    -- exported for convenience
--    , rnf
--    , rwhnf
    ) where

import Control.Parallel (pseq)
import Control.Parallel.Strategies

simpleMapReduce
    :: (a -> b)
    -> ([b] -> c)
    -> [a]
    -> c

simpleMapReduce mapFunc reduceFunc = reduceFunc . map mapFunc

mapReduce
    :: Strategy b
    -> (a -> b)
    -> Strategy c
    -> ([b] -> c)
    -> [a]
    -> c

mapReduce mapStrat mapFunc reduceStrat reduceFunc input =
    mapResult `pseq` reduceResult
    where mapResult    = parMap mapStrat mapFunc input
          reduceResult = reduceFunc mapResult `using` reduceStrat

LineChunks.hs

module LineChunks
    (
     chunkedReadWith
    ) where

import Control.OldException (bracket, finally)
import Control.Monad (forM, liftM)
--import Control.Parallel.Strategies 
import Control.DeepSeq (NFData, rnf)
import Data.Int
import qualified Data.ByteString.Lazy.Char8 as LB
import GHC.Conc (numCapabilities)
import System.IO

data ChunkSpec = CS {
      chunkOffset :: !Int64
     ,chunkLength :: !Int64
    } deriving (Eq, Show)

withChunks :: (NFData a) =>
              (FilePath -> IO [ChunkSpec])
           -> ([LB.ByteString] -> a)
           -> FilePath
           -> IO a

withChunks chunkFunc process path = do
  (chunks, handles) <- chunkedRead chunkFunc path
  let r = process chunks
  (rnf r `seq` return r) `finally` mapM_ hClose handles

chunkedReadWith :: (NFData a) => 
                   ([LB.ByteString] -> a) -> FilePath -> IO a

chunkedReadWith func path =
    withChunks (lineChunks (numCapabilities * 4)) func path


chunkedRead :: (FilePath -> IO [ChunkSpec])
          -> FilePath
          -> IO ([LB.ByteString], [Handle])
chunkedRead chunkFunc path = do
  chunks <- chunkFunc path
  liftM unzip . forM chunks $ \spec -> do
                               h <- openFile path ReadMode
                               hSeek h AbsoluteSeek (fromIntegral (chunkOffset spec))
                               chunk <- LB.take (chunkLength spec) `liftM` LB.hGetContents h
                               return (chunk, h)

lineChunks :: Int -> FilePath -> IO [ChunkSpec]
lineChunks numChunks path = do
  bracket (openFile path ReadMode) hClose $ \h -> do
    totalSize <- fromIntegral `liftM` hFileSize h
    let chunkSize = totalSize `div` fromIntegral numChunks
        findChunks offset = do
          let newOffset = offset + chunkSize
          hSeek h AbsoluteSeek (fromIntegral newOffset)
          let findNewline off = do
                              eof <- hIsEOF h
                              if eof
                                 then return [CS offset (totalSize - offset)]
                                 else do
                                   bytes <- LB.hGet h 4096
                                   case LB.elemIndex '\n' bytes of
                                     Just n -> do
                                        chunks@(c:_) <- findChunks (off + n + 1)
                                        let coff = chunkOffset c
                                        return (CS offset (coff - offset):chunks)
                                     Nothing -> findNewline (off + LB.length bytes)
          findNewline newOffset
    findChunks 0

LineCount.hs

module Main where

import Control.Monad (forM_)
import Data.Int (Int64)
import qualified Data.ByteString.Lazy.Char8 as LB
import System.Environment (getArgs)
import LineChunks (chunkedReadWith)
import MapReduce (mapReduce)
import Control.Parallel.Strategies

lineCount :: [LB.ByteString] -> Int64
lineCount = mapReduce rdeepseq (LB.count '\n')
                      rdeepseq sum

main :: IO ()
main = do
  args <- getArgs
  forM_ args $ \path -> do
    numLines <- chunkedReadWith lineCount path
    putStrLn $ path ++ ": " ++ show numLines

今日の畑(101016)

そら豆が、カラスかなんかにほじられて全滅しているっぽい。やっぱポットで発芽させて植えたほうが無難か。まだ間に合うかな、間に合うようだったら明日種買ってきて、ダメだったら潔く苗を買おう。

一方、ラディッシュ、春菊、ほうれん草は無事に発芽。

ラディッシュ、間引かないと。

1287225079

春菊とほうれん草。

1287225067 1287225073

それにしても石ころ多いなぁ。土ふるいでも作って石取りしようかなぁ。

真澄 山廃

真澄の山廃

1287224858

しっかりしている感じ

仁王を見ていた

そろそろ転機かな?と思いながら仁王みてたけど、めぼしい職が見つからなかった。ちなみに自分の会社の募集は見つかって、おーなるほどと思ったくらい。

ProductName 起業のファイナンス ベンチャーにとって一番大切なこと
磯崎 哲也
日本実業出版社 / ¥ 2,310 ()
通常2~4週間以内に発送

Real World Haskell 24章 8,9

クィックソートを並列で。

正規形(NF),頭部正規形(HNF),弱頭部正規形(WHNF)を理解した。

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan,John Goerzen,Don Stewart
オライリージャパン / ¥ 3,990 ()
在庫あり。

module Sorting where

import Control.Parallel (par, pseq)

parSort :: (Ord a) => [a] -> [a]
parSort (x:xs) = force greater `par` (force lesser `pseq`
                                     (lesser ++x:greater))
    where lesser  = parSort [y|y <- xs, y<x]
          greater = parSort [y|y <- xs, y>=x]
parSort _ = []

force :: [a] -> ()
force xs = go xs `pseq` ()
    where go (_:xs) = go xs
          go [] = 1

sort :: (Ord a) => [a] -> [a]
sort (x:xs) = lesser ++ x:greater
    where lesser  = sort [y|y <- xs, y<x]
          greater = sort [y|y <- xs, y>=x]
sort _ = []

parSort2 :: (Ord a) => Int -> [a] -> [a]
parSort2 d list@(x:xs) 
   | d <= 0 = sort list
   | otherwise = force greater `par` (force lesser `pseq`
                                     (lesser ++x:greater))
       where lesser  = parSort2 d' [y|y <- xs, y<x]
             greater = parSort2 d' [y|y <- xs, y>=x]
             d' = d - 1
parSort2 _ _ = []

で実際に時間を測ってみると

-- parSort2
kzfm:ch24 kzfm$ ./SortMain +RTS -N2 -RTS 1000000
we have 1000000 elements to sort.
sorted all 1000000 elements.
11.419152s elapsed.

-- sort
kzfm:ch24 kzfm$ ./SortMain 1000000
we have 1000000 elements to sort.
sorted all 1000000 elements.
6.212093s elapsed.

-- parSort -N2
kzfm:ch24 kzfm$ ./SortMain +RTS -N1 -RTS 1000000
we have 1000000 elements to sort.
sorted all 1000000 elements.
6.188667s elapsed.

-- parSort -N2
kzfm:ch24 kzfm$ ./SortMain +RTS -N2 -RTS 1000000
we have 1000000 elements to sort.
sorted all 1000000 elements.
35.960397s elapsed.

GCのせいかな、よくわからん。