haskell-jp / code-review #4

それから,要件がどうなのかは分からないのですが,JSONログを吐けるならそっちを解析するといいのかなという感じがしました
https://github.com/input-output-hk/cardano-sl/blob/8d25c2ad3ca2354af8f8c43a2972d1b9a31bf440/lib/src/Pos/Client/CLI/NodeOptions.hs#L152
ご指摘ありがとうございます!
ベンチマークも実装してみます。
ログに関しては商用で利用されているCardano-slのログファイルがJSONではないため難しいと思います。。より解析しやすいフォーマットへの変更は現在検討しています。
[email protected]:~/haskell/cardano-diagnosis-program$ stack exec diagnosis "/home/hiroto/Downloads/logs (6).zip" -- +RTS -s
Running analysis on logs
Analysis done successfully!! See result-2018-04-19.html
   2,031,484,272 bytes allocated in the heap
     791,850,816 bytes copied during GC
     189,980,952 bytes maximum residency (12 sample(s))
       3,383,016 bytes maximum slop
             433 MB total memory in use (0 MB lost due to fragmentation)

                                     Tot time (elapsed)  Avg pause  Max pause
  Gen  0      1910 colls,  1910 par    3.579s   0.844s     0.0004s    0.1591s
  Gen  1        12 colls,    11 par    0.018s   0.004s     0.0004s    0.0012s

  Parallel GC work balance: 5.67% (serial 0%, perfect 100%)

  TASKS: 18 (1 bound, 17 peak workers (17 total), using -N8)

  SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)

  INIT    time    0.000s  (  0.001s elapsed)
  MUT     time    0.716s  (  0.818s elapsed)
  GC      time    3.597s  (  0.848s elapsed)
  EXIT    time    0.000s  (  0.023s elapsed)
  Total   time    4.313s  (  1.691s elapsed)

  Alloc rate    2,839,001,278 bytes per MUT second

  Productivity  16.6% of total user, 49.8% of total elapsed

gc_alloc_block_sync: 18951
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 7944

mizunashiさんの言うとおりGCにかなり時間を食ってました。
この場合はストリームライブラリなどを使ったほうがよいのでしょうか。
https://github.com/input-output-hk/cardano-diagnosis-program/blob/master/src/Classifier.hs を読んだ感じ、無理に State を使う必要はないように感じます。
少なくともトップレベルの型を見る限り、 State Analysis () な型の関数しか見当たらないためです。
素直に Analysis -> Analysis な関数にすればよいのではないでしょうか。
RTSオプションでメモリ制限をした場合に,かなり遅くなる場合は一般的にストリームライブラリを使うのがいいと思います.ガッと読んでガッと書き込むとGC無しの場合速い場合がありますが,GC込みの場合ちょびちょび読む(ストリーム処理)より遅いみたいなことがよくあるので,一旦試してみるのがいい気がします.
で、気になる点にお答えしますと、
1. どのようなテストを行えばいいのかわかりません。(ダミーファイルを作ってそれをパースするとか?)
詳しく見ないとなんともいえませんが、それが一番要求に近い部分をテストしているっぽいので、いいんじゃないでしょうか?
2. ログを解析する部分(`Classifier.hs`)が総当りに近いです...
いい例はぱっと思いつきませんが、とりあえず今の実装でも十分にシンプル( State Analysis () をやめることでもっとシンプルになりますが)なのでよいのではないでしょうか。
効率の観点で言えばもしかしたら https://quasimal.com/posts/2018-01-08-prefix-tree-parsing.html とかが使える、かもしれません(まだ実験的なライブラリーのようなので興味があったら、程度ですが)。

とくにユーザー名に半角英数字以外を使用しているユーザーに対して動作するのか

これはほかはおそらく大丈夫ですが、Windowsでは結構厳しいでしょうね。。。
残念ながら現状Haskellの日本語パスの扱いはWindowsではエラーが起こりやすかったように思います。
Windowsきついですよね。。
英語版Windowsでユーザー名が日本語のユーザーを作成したらプロンプトが??になってて「あ、これだめだ」って思いました。

エラー処理はPR作成したので、まずテスト、そのあとストリームの実装に着手します。
reactionがなかったので念のためもう一度貼り付けておきます。
確認済みでしたらすみません。 :bow:
https://haskell-jp.slack.com/archives/C8KBGEBR7/p1524104464000235?thread_ts=1524038560.000043&cid=C8KBGEBR7
Stateの取り払いはすぐできそうなので、まずそれしますね。
取り払ったら使用メモリもかなり減りました
   2,030,484,352 bytes allocated in the heap
     552,951,344 bytes copied during GC
     130,383,808 bytes maximum residency (10 sample(s))
       2,306,112 bytes maximum slop
             251 MB total memory in use (0 MB lost due to fragmentation)

なるほど。lazyなStateだからスペースリークが起きていたと。。。こちらとしても勉強になります :smirk:
その考えに沿ってMapもStrictにしたほうがいいと思って書き換えてみたらかなり消費が抑えられました。。
[email protected]:~/haskell/cardano-diagnosis-program$ stack exec diagnosis "/home/hiroto/Downloads/logs (6).zip" -- +RTS -s
Running analysis on logs
Analysis done successfully!! See result-2018-04-19.html
   2,011,567,408 bytes allocated in the heap
     127,421,808 bytes copied during GC
      26,902,264 bytes maximum residency (10 sample(s))
         432,904 bytes maximum slop
              69 MB total memory in use (0 MB lost due to fragmentation)

                                     Tot time (elapsed)  Avg pause  Max pause
  Gen  0      1893 colls,  1893 par    1.366s   0.185s     0.0001s    0.0090s
  Gen  1        10 colls,     9 par    0.011s   0.001s     0.0001s    0.0003s

  Parallel GC work balance: 5.20% (serial 0%, perfect 100%)

  TASKS: 18 (1 bound, 17 peak workers (17 total), using -N8)

  SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)

  INIT    time    0.001s  (  0.001s elapsed)
  MUT     time    0.742s  (  0.693s elapsed)
  GC      time    1.376s  (  0.186s elapsed)
  EXIT    time    0.002s  (  0.011s elapsed)
  Total   time    2.121s  (  0.891s elapsed)

  Alloc rate    2,710,634,667 bytes per MUT second

  Productivity  35.1% of total user, 79.0% of total elapsed

gc_alloc_block_sync: 17614
whitehole_spin: 0
gen[0].sync: 3
gen[1].sync: 334
余談ですが、Philip Wadler氏に「Haskellを1からやり直すとしたらなにを変えますか」ってきいたら「遅延評価をなくす」っていってました。
なーんとなくですが`mapM_`を呼び出すたびに読み取ったファイルを全て評価しているのが原因だったきがします。
stackを初めてちゃんと使ってみたんですが、予想以上に便利でしたという感想
https://github.com/sozysozbot/cerke/tree/master/cerkefs
気になっているのは、依存関係が面倒なことになっていることとか、速度とかで改善できる点があるのかどうか(`StateT` で Lazy の代わりに Strict 使っても速度に差はほとんどなかった) とかですね
会社の人にみてもらったら、ストリームよりfoldrの使い方が問題だといわれました。
(すぐにやってみたい
@yosikawa has joined the channel
さっと見て仕様を把握できませんでしたが何点か。
依存関係が面倒なことになっていることとか

https://github.com/sozysozbot/cerke/blob/0648daffb6100cb9652ec5f0476dc2eed1c7b830/cerkefs/package.yaml#L23-L26 を読んだ限り特に面倒な部分には見えませんが。。。

速度とかで改善できる点があるのかどうか

すべてはプロファイリングしてから考えましょう。
stack test --profile するだけでテスト時にプロファイリングまでしてくれるいい時代になりました。

あと、このアプリケーションについては特に問題が起きていないのだろうし、急いで直す必要はないかと思いますが、 StateT s IO a はおすすめしません。例外が発生した際に状態を戻すすべがないからです。
代わりに ReaderT (IORef s) IO a みたいな型で代用するのをおすすめします。

最近はFPCompleteさんがそういう風に勧めています
https://www.fpcomplete.com/blog/2017/06/readert-design-pattern
ありがとうございます。

すべてはプロファイリングしてから考えましょう。
たしかに。せっかくstack使っているんだから便利に使っていかねばですね

依存関係は、自分で定義したモジュール同志の依存関係の意図でした(Internalという名前のついているものが非Internalにインポートされ、それが更に別のInternalなものにインポートされているなど)

ReaderT (IORef s) IO a
の記事は私も目を通していました(「あっこれ直さなきゃ」ってなった)(とはいえ、現状のはデバッグ用のテストコードなので、そもそも全部書き直す可能性のほうが高そう)
@yishibashi has joined the channel
ReaderT (IORef s) IO aにしました。モナドという共通インターフェースのおかげでほとんど書き換える必要がなく楽でした
プロファイル取ってみましたが、んー、意外にもData.Mapがボトルネックになってしまっているみたいですねぇ
Data.IntMapにしたらかなり速くなりました
Data.Vectorにしたら逆に遅くなった(まあイミュータブルでやってるからしかたない)(IntMap、速いのな)
代わりにUnboxedVectorを使用するのはだめでしょうか?
http://hackage.haskell.org/package/vector-0.12.0.1/docs/Data-Vector-Unboxed.html
中に入るのが代数的データ型なんですよね。Maybe (Int8, Int8, Int8)みたいな構造なので、ビット演算でInt32に埋め込めばUnboxedも使えるかと思いますが、変換と逆変換のオーバーヘッドが気になるところです
あ、これは失礼。完全に寝ぼけてました
UnboxedVector
@sugano has joined the channel
@n.tatsuki.214 has joined the channel
先々週から2週間近く格闘していた、
extensibleにおける「レコードから指定したキーの要素を取り除いたレコードを返す関数」がようやくそれっぽい動きをするものが作れました。 :tada:
みなさんのご協力のおかげです。 :bow:

https://gist.github.com/igrep/3e87871e900c98c0850a3b43b7cbff96
extensibleの使い方に各種GHC拡張の使い方など、ご指摘あればいただきたいです。
特に https://haskell-jp.slack.com/archives/C5666B6BB/p1526621270000191?thread_ts=1526606658.000243&cid=C5666B6BB で触れられている
「Membershipが内部で持ってる位置をfilter後に移してやる必要があったりするんじゃないんですかね?」という指摘が若干気になります。
これからテストをいくつか書いてこちらでも確認してはみますが。
@igrep shared a file: without.hs
eagle.hokkaido2317
@eagle.hokkaido2317 has joined the channel
@luisemey has joined the channel
@km has joined the channel
先月のHaskell-jpもくもく会で @kayhide さんと話していて思い立ったんですが、HspecよりもRSpecらしく書けるテスティングライブラリーを作っています。
で、まだ実装はほとんど書いていないのですが、使い方について「こんな感じで使えたらどうか」というものを書いて :point_down: のGistに書いたのでご意見募集します。
https://gist.github.com/igrep/6d1d0f1c3d9e8e4aff3746d0a51aedb8
コメントに英語で書いたとおりなのですが、RSpecの let! ( let ではなく)に相当するものとして given という関数を用意したのですが、これで定義した値を参照するには refer を使わなければならない、という点が受け入れられるか気になっています。
ほかの実装方法や、よりよい単語などアイディアがあれば教えてください! :pray:
参考にならないかもしれないですが、個人的にRSpecを全く知らないので、よりRSpecらしく書けることによって何が便利になるのかピンときていません(将来的に公開される際にREADME.mdに書いてあると嬉しいなと思いました):sweat_smile:
すみません、その辺の文脈の共有を怠ってました…。:sweat_drops:ちょっと今から用事なのでまた落ち着いたときに書きます! :bow:
@pan.live has joined the channel
@mopemope has joined the channel
@imai.y has joined the channel
授業課題を題材にいい機会だったので,僕も気になっていたhigher order functorベースのSyntaxでPrinterとかを書いてみました.まだ,インタプリタとかは作ってないですが
https://github.com/mizunashi-mana/language-tf
@mizuki has joined the channel
@sakuemon has joined the channel
@hexirp cleared channel topic
もうずっと前に設定されたのが残ったままだったので消しました
lagrange.resolvent
@lagrange.resolvent has joined the channel
@yuichi has joined the channel