Hatena::Grouphaskell

Haskell卒業!

  Haskellの勉強 -> 演習 -> 卒業
  Haskell&プログラミング卒業しました。その他サイコなことは「route150の日記」に書いています。

2010年12月24日 金曜日

[]Haskell Advent 2010 01:38

Haskellコードの高速化


楽しみにしていたのですが、期待通りの非常に濃い内容でした。読むだけで1時間くらい掛かった...orz


題名は最適化ですが、実質、GHCコンパイラの代数的データ型の取り扱いの仕組みとか評価の仕組みがメインで、その面で色々と勉強になった。


文字のリストは1文字で40バイトくらいかぁ。やっぱ、これくらい大富豪でないとHaskellとは言えないよね...


やっぱ、ある程度、GHCの仕様がわかっているかいないかで結構違ってくるような...


(修正: 大いなる勘違いだったらしい ->)(正直、実行速度だけ考えると、悲しいかな、どう頑張ってもC++でゴリゴリ書くコードに遠く及ばない(エラトステネスの篩(STUArrayやプリミティブを使った実装)で十分に絶望的な差が付く)。つか、JavaやC#にすら並べないでしょう。速度が必要な場合は最初からC++で書いた方が楽...なのは皆が思っていることだけど...)


あと、非ボックス化タプルについての解説があって嬉しい(あんま理解できてないので、後でじっくり考える)。正直、タプル使うとスゴイ遅くなるから、高速化したい時はタプルは使用禁止なのだが、IOとか内部で非ボックス化タプルを返す関数になっていて悲しすぐる...


非ボックス化タプルって、Haskell的にはFast Call(x86ではecx、edxに引数を2個積む)みたいな感じで2引数を返すための仕様かと思っていたが、冷静に考えたら、STGなCコードを吐くわけだから、そこまではやれそうもない。C++のthiscallみたいに関数の第1引数は常にレジスタ渡しとかできないのかなぁ...


実行速度は度々ネタにされるだけに遅い...

[]GHC7.01... 20:02

よく考えたら、超高級電卓としてHaskellは残しておくべきだったと思い、やば、でも全部消しちゃったしと思って適当にGHC7.01を試しにインストールした...


とりあえず、Template Haskellをちょっとやっとくか...と思ってプラグマ適当に指定したらスペルミスを修正してくれた。賢いなぁ...


C:\Documents and Settings\user\My Documents\Programming\Haskell\Test.hs:1:31:
    Unsupported extension: QuasiQuates
    Perhaps you meant `QuasiQuotes' or `NoQuasiQuotes'
Failed, modules loaded: Main.

今日はクリスマスイブか...


七面鳥(漢字これで合ってる???)は食べそうにないなぁ。つか、もう夕御飯食べ終わったし。七面鳥にとっては大虐殺祭すぎるなぁ...

[][]応用文法(Template Haskell(1)) 21:21

以前のようにライブラリの定義とか一応一通りみるとかメンドイのでしない。例によって例の如く簡単な入門ページは見つからない。ある程度まで上達すればmr_konnさんの日記が参考になると思うのだが、まだ、BootStrapな段階なので基本部分を攻略しる...


{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
{-# LANGUAGE GADTs, ScopedTypeVariables, BangPatterns #-}
{-# OPTIONS_GHC -cpp #-}

-- 何気にArrowとかCプリプロセッサとか使う気満々...
import Language.Haskell.TH
import Control.Applicative
import Control.Monad
import Control.Arrow
import Data.List

-- えっ、実用的なプログラム。普通にC++とかJavaで書けばいいのでは。
-- Haskellは数学な問題とか、何か思考実験したい時に使うのが素直な
-- 気がするけど...

-- とりあえず、勉強の仕方はParsec、QuickCheckとかと同じ系統、要は
-- 使い方をひたすら覚える。理屈とか考えなくていい...

-- 適当にオックスフォード括弧(Oxford brackets)で括ると抽象構文木が
-- どんなかわかるらしい...へぇx1...
test1 :: IO ()
test1 = runQ [e| v |] >>= print
  where v = 1 + (1 :: Int)

test2 :: IO ()
test2 = runQ [e| 1 + (1 :: Int) |] >>= print

実行結果...


$> test1
LitE (IntegerL 2)

$> test2
InfixE (Just (LitE (IntegerL 1))) (VarE GHC.Num.+) (Just (SigE (LitE (IntegerL 1
)) (ConT GHC.Types.Int)))

なんかこの括弧の使い方、バナナ括弧と同じじゃんと思ったら、同じRoss Patersonって人のライブラリだった。この人のライブラリはメンテが良くて、説明も丁寧だし、一貫性があるからイケてる。ただ、1人で支えてるというのもどうかと思うが...

[][]応用文法(Template Haskell(2)) 21:36

久々に文法は楽しい...


{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
{-# LANGUAGE GADTs, ScopedTypeVariables, BangPatterns #-}
{-# OPTIONS_GHC -cpp #-}

import Language.Haskell.TH
import Control.Applicative
import Control.Monad
import Control.Arrow
import Data.List

-- で、できた抽象構文木を実際のコードの中に埋め込む(接合する
-- というらしい)ことができる。$()を使う。遅延適用の($)とは違うので注意...
test1 :: IO ()
test1 = print $([e| v |])
  where v = 1 + (1 :: Int)

test2 :: IO ()
test2 = print $([e| 1 + (1 :: Int) |])

ということで、適当に自分で抽象構文木を作ってコンパイル時に接合するみたいなことをするのがTemplate Haskellの基本っぽい。構文木の構成は多少慣れが必要かも...


実行結果...


$> test1
2
$> test2
2

ところで,


class (Monad m, Functor m) => Quasi m where
...

とか、MonadとFunctorの両方を書かねばならないのは、なんだかなーという感じ...

mkothamkotha2010/12/25 00:02最適化の記事を書いた者です。楽しんでもらえたようで嬉しいです。

非ボックス化タプルについてですが、GHCが生成するCコードは通常の呼び出し/戻りの機構を使わず、GCC拡張を使ってレジスタ操作を行なっているので、問題なく多値をレジスタで返すことができます。ただしx86だとレジスタ数が足りないので結局スタック返しになっているようです。(GHCのソースのincludes/stg/MachRegs.hにSTGレジスタとマシンレジスタの対応が書いてあります)

エラトステネスの篩の速度がC++に遠く及ばないというのがちょっと信じられなかったので、unsafe*を使って書いてみたのですが、C++のものより速くなってしまいました。

http://d.hatena.ne.jp/mkotha/20101224

参考になれば幸いです。

route150route1502010/12/25 07:02mkothaさん、初めまして。

{-# OPTIONS_GHC -monly-2-regs #-}(x86専用)とか、スゴイ怪しいプラグマを指定すれば多分レジスタでやってくれそうな気はしていたのですが、イマイチ意味がわからず、ほったからしにしていました...

GHCのソースを読んでどうこうする技術レベルにはないので、まぁこれは仕方ないと個人的に思っています。エラトステネスの篩は、逆に自分で検証できる範囲なので、もう一度確認してみようと思います。

コメントありがとうございました。