モノむドの豚に自分自身を信じお飛ぶように教える

前の蚘事で、機胜的か぀蚀語指向のプログラミング手法を䜿甚しお、仮想スタックマシン甚のプログラム゚グれキュヌタヌを構築する方法に぀いお説明したした。 蚀語の数孊的構造は、セミグルヌプずモノむドの抂念に基づいお、その翻蚳者の実装の基本構造を瀺唆したした。 このアプロヌチにより、矎しく拡匵可胜な実装を構築し、拍手を打぀こずができたしたが、聎衆からの最初の質問は挔壇を降りお再びEmacsに登りたした。



簡単なテストを実斜し、スタックのみを䜿甚する単玔なタスクで仮想マシンがスマヌトに動䜜し、「メモリ」ランダムアクセスの配列を䜿甚するず倧きな問題が発生するこずを確認したした。 プログラムのアヌキテクチャの基本原則を倉曎せずにそれらを解決し、プログラムの千倍の高速化を達成した方法に぀いお、この蚘事で説明したす。


∗ âˆ— âˆ—


Haskellは、特別なニッチを持぀独特の蚀語です。 その䜜成ず開発の䞻な目暙は、lingua francaが関数型プログラミングのアむデアを衚珟およびテストする必芁性でした。 これは、その最も顕著な機胜を正圓化したす怠iness、最倧限の玔床、型の匷調、およびそれらの操䜜。 これは、日垞の開発甚ではなく、産業甚プログラミング甚でも、広く䜿甚するためでもありたせん。 ネットワヌク業界やデヌタ凊理で倧芏暡プロゞェクトを䜜成するために実際に䜿甚されおいるずいう事実は、必芁に応じお開発者の善意、抂念実蚌です。 しかし、これたでのずころ、Haskellで曞かれた最も重芁で広く䜿甚されおいる驚くほど匷力な補品は... ghcコンパむラです。 そしお、これはその目的の芳点から完党に正圓化されたす-コンピュヌタサむ゚ンスの分野の研究のためのツヌルになるこず。 サむモン・ペむトン・ゞョン゜ンによっお宣蚀された原則「どんな犠牲を払っおも成功を避ける」は、蚀語がそのような道具であり続けるために必芁です。 Haskellは、半導䜓技術やナノ材料を開発しおいる研究センタヌの研究宀にある無菌宀のようなものです。 働くこずはひどく䞍䟿であり、日垞の実践では行動の自由も制限したすが、これらの䞍䟿さなしに、制限を劥協せずに遵守しなければ、埌に産業発展の基瀎ずなる埮劙な効果を芳察しお研究するこずはできたせん。 同時に、産業界では、最も必芁な量だけ無菌性が必芁になり、これらの実隓の結果は、ガゞェットの圢でポケットに衚瀺されたす。 星や銀河を研究するのは、それらから盎接利益を埗るこずを期埅するからではなく、これらの非実甚的な物䜓の芏暡で、量子効果や盞察論的効果が芳枬可胜になり、研究されるようになったためです。 そのため、「間違った」線、蚈算の非実甚的な怠inessさ、いく぀かの型掚論アルゎリズムの剛性、非垞に急な入力曲線により、最終的には膝やオペレヌティングシステムでスマヌトアプリケヌションを簡単に䜜成できたせん。 しかし、レンズ、モナド、コンビナトリアル解析、モノむドの広範な䜿甚、自動定理蚌明の方法、宣蚀型機胜パッケヌゞマネヌゞャヌ、線圢型および埓属型が実際の䞖界に近づいおいたす。 これにより、Python、Scala、Kotlin、F、Rustなどの蚀語で、無菌状態の䜎いアプリケヌションが芋぀かりたす。 しかし、関数型プログラミングの原則を教えるためにこれらの玠晎らしい蚀語を䜿甚するこずはありたせん。孊生を研究宀に連れお行き、明るくきれいな䟋でどのように機胜するかを瀺したす。倧きくおわかりにくいが、非垞に高速なマシン。 コヌヒヌメヌカヌを普及させるために電子顕埮鏡に入れようずする詊みに察する保護は、すべおの犠牲を払っお回避するこずです。 そしお、蚀語がよりクヌルな競技䌚では、Haskellは垞に通垞のノミネヌトから倖れたす。


しかし、その人は匱く、悪魔も私の䞭に䜏んでいるので、他人の前で「私の奜きな蚀語」を比范、評䟡、擁護したす。 だから、このアむデアが私のために働くかどうかを芋るずいう唯䞀の目的で、モノむダル構成に基づいお積み重ねられたマシンの゚レガントな実装を曞いた埌、私は実装が芋事に、しかしひどく非効率的であるこずに気づいたこずにすぐに動揺したした たるで本気で真剣な仕事に䜿ったり、PythonやJavaの仮想マシンが提䟛されおいるのず同じ垂堎でスタックドマシンを販売したりするようなものです。 しかし、それを気にしお、䌚話党䜓が始たった子豚に぀いおの蚘事はそのようなおいしい数字を䞎えたす子豚の数癟ミリ秒、Pythonの秒...そしお私の矎しいモノむドは1時間で同じタスクに察凊できたせん 私は成功しなければなりたせん 私の顕埮鏡は、研究所の廊䞋にあるコヌヒヌメヌカヌず同じくらい゚スプレッ゜を醞造したす クリスタルパレスは分散しお宇宙に打ち䞊げるこずができたす


しかし、あなたはあきらめる準備ができおいたすか、数孊者の倩䜿は私に尋ねたすか 宮殿建築の玔床ず透明性は プログラムから他の゜リュヌションぞの準同型性が提䟛する柔軟性ず拡匵性 悪魔ず倩䜿はどちらも頑固であり、私も私に生きさせおいる賢明な道教埒は、私たちが䞡方に合った道を取り、可胜な限りそれに埓うこずを瀺唆したした。 ただし、勝者を特定するこずを目的ずするのではなく、パス自䜓を知り、それがどの皋床進んでいるかを調べ、新しい経隓を埗るために。 そしお、私は最適化の無駄な悲しみず喜びを知っおいたした。


始める前に、効果の芳点からの蚀語の比范は無意味であるず付け加えたす。 翻蚳者通蚳者たたはコンパむラ、たたは蚀語を䜿甚するプログラマのパフォヌマンスを比范する必芁がありたす。 最終的に、Cプログラムが最速であるずいう䞻匵は、たずえばBASICで完党なCむンタプリタを曞くこずで簡単に論砎できたす。 そのため、Haskellずjavascriptを比范するのではなく、 ghcによっおコンパむルされたghcによっお実行されたプログラムず、たずえば特定のブラりザヌで実行されたプログラムのパフォヌマンスを比范しおいたす。 豚の甚語はすべお、積み重ねられた機械に関する刺激的な蚘事に由来しおいたす。 蚘事に付随するすべおのHaskellコヌドは、 リポゞトリで調べるこずができたす 。


快適地垯を離れる


開始䜍眮は、 EDSLの圢匏でのモノむダルスタックマシンの実装です。これは、2ダヌスのプリミティブを組み合わせお仮想スタックマシンのプログラムをレンダリングできる小さなシンプルな蚀語です。 圌が2番目の蚘事に入ったらすぐに、圌にmonopigずいう名前をmonopigたす。 スタックマシンの蚀語は、連結操䜜ず空の操䜜を1぀の単䜍ずしおモノむドを圢成するずいう事実に基づいおいたす。 したがっお、圌自身は機械の状態のモノむド倉換の圢で構築されたした。 状態には、スタック、ベクトル圢匏のメモリ芁玠ぞのランダムアクセスを提䟛する構造、緊急停止フラグ、およびデバッグ情報を蓄積するためのモノむダルバッテリが含たれたす。 この構造党䜓が、操䜜から操䜜ぞの内郚準同型のチェヌンに沿っお送信され、蚈算プロセスを実行したす。 プログラムコヌドの同型構造は 、プログラムが圢成する構造から構築され、それから、匕数ずメモリの数の芳点からプログラムの芁件を衚す他の有甚な構造に準同型倉換されたす。 構築の最終段階は、クレむズリヌカテゎリでのモノむド倉換の䜜成でした。これにより、任意のモナドに蚈算を浞すこずができたす。 そのため、マシンには入出力ずあいたいな蚈算の機胜がありたした。 この実装から始めたす。 圌女のコヌドはここにありたす 。


゚ラトステネスのふるいの玠朎な実装でプログラムの有効性をテストしたす。これは、メモリ配列をれロず1で満たし、玠数をれロで衚したす。 アルゎリズムの手続きコヌドをjavascriptで提䟛したす。


 var memSize = 65536; var arr = []; for (var i = 0; i < memSize; i++) arr.push(0); function sieve() { var n = 2; while (n*n < memSize) { if (!arr[n]) { var k = n; while (k < memSize) { k+=n; arr[k] = 1; } } n++; } } 

アルゎリズムはすぐに最適化されたす。 すでに満たされおいるメモリセルを悪甚するこずはありたせん。 私の数孊者の倩䜿は、 PorosenokVMプロゞェクトの䟋からの本圓に玠朎なバヌゞョンには同意したせんでした。この最適化にはスタック蚀語の5぀の呜什しか必芁ないからです。 アルゎリズムがmonopig倉換される方法はmonopigです。


 sieve = push 2 <> while (dup <> dup <> mul <> push memSize <> lt) (dup <> get <> branch mempty fill <> inc) <> pop fill = dup <> dup <> add <> while (dup <> push memSize <> lt) (dup <> push 1 <> swap <> put <> exch <> add) <> pop 

そしお、 monopigず同じ型を䜿甚しお、このアルゎリズムの同等の実装を慣甚的なHaskell䞊でどのように曞くこずができるかを以䞋に瀺したす。


 sieve' :: Int -> Vector Int -> Vector Int sieve' km | k*k < memSize = sieve' (k+1) $ if m ! k == 0 then fill' k (2*k) m else m | otherwise = m fill' :: Int -> Int -> Vector Int -> Vector Int fill' knm | n < memSize = fill' k (n+k) $ m // [(n,1)] | otherwise = m 

Data.Vectorタむプずそれを操䜜するツヌルを䜿甚したすが、Haskellでの日垞的な䜜業にはあたり䞀般的ではありたせん。 匏m ! k m ! kはベクトルmのk番目の芁玠を返し、 m // [(n,1)] -番号n芁玠をm // [(n,1)]蚭定したす。Haskellで働いおいおも、助けが必芁なため、ここに曞いおいたす。ほが毎日。 実際のずころ、機胜実装でのランダムアクセスを備えた構造は非効率的であり、この理由から愛されおいたせん。


子豚に関する蚘事で指定された競合条件に埓っお、アルゎリズムは100回実行されたす。 特定のコンピュヌタヌを削陀するために、これら3぀のプログラムの実行速床を比范し、Chromeで実行されたjavascriptプログラムのパフォヌマンスを参照しおみたしょう。



ホラヌホラヌ!!! monopigはmonopigスロヌダりンするだけでなく、ネむティブバヌゞョンはそれほど良くありたせん もちろん、Haskellはクヌルですが、ブラりザで実行されおいるプログラムに劣りたせんか コヌチが教えおくれるように、あなたはそのように生きるこずはできたせん。Haskellが提䟛する快適ゟヌンを離れる時です


怠lazを克服する


正しくしたしょう。 これを行うには、 -rtsoptsフラグをmonopigしお-rtsoptsプログラムをコンパむルし、実行時の統蚈を-rtsopts 、゚ラトステネスシヌブを1回実行する必芁があるものを確認したす。


 $ ghc -O -rtsopts ./Monopig4.hs [1 of 1] Compiling Main ( Monopig4.hs, Monopig4.o ) Linking Monopig4 ... $ ./Monopig4 -RTS -sstderr "Ok" 68,243,040,608 bytes allocated in the heap 6,471,530,040 bytes copied during GC 2,950,952 bytes maximum residency (30667 sample(s)) 42,264 bytes maximum slop 15 MB total memory in use (7 MB lost due to fragmentation) Tot time (elapsed) Avg pause Max pause Gen 0 99408 colls, 0 par 2.758s 2.718s 0.0000s 0.0015s Gen 1 30667 colls, 0 par 57.654s 57.777s 0.0019s 0.0133s INIT time 0.000s ( 0.000s elapsed) MUT time 29.008s ( 29.111s elapsed) GC time 60.411s ( 60.495s elapsed) <--   ! EXIT time 0.000s ( 0.000s elapsed) Total time 89.423s ( 89.607s elapsed) %GC time 67.6% (67.5% elapsed) Alloc rate 2,352,591,525 bytes per MUT second Productivity 32.4% of total user, 32.4% of total elapsed 

最埌の行は、プログラムが生産的なコンピュヌティングに埓事したのは3分の1に過ぎなかったこずを瀺しおいたす。 残りの時間、ガベヌゞコレクタヌはメモリから実行され、遅延蚈算のためにクリヌンアップされたした。 小児期に怠は良くないず䜕床も蚀われたした ここで、Haskellの䞻な機胜は、数十億の遅延ベクトルおよびスタック倉換を䜜成しようずしお、私たちに損害を䞎えたした。


この時点で数孊の倩䜿は指を持ち䞊げお、アロンゟ教䌚の時代以来、蚈算戊略が結果に圱響を䞎えないずいう定理があるずいう事実に぀いお喜んで語っおいたす。これは、効率の理由から自由に遞択できるこずを意味したす。 蚈算を厳密に倉曎するこずはたったく難しくありたせん! スタックおよびメモリのタむプの宣蚀で、これらのフィヌルドを厳密にしたす。


 data VM a = VM { stack :: !Stack , status :: Maybe String , memory :: !Memory , journal :: !a } 

他のものは倉曎せず、すぐに結果を確認したす。


 $ ./Monopig41 +RTS -sstderr "Ok" 68,244,819,008 bytes allocated in the heap 7,386,896 bytes copied during GC 528,088 bytes maximum residency (2 sample(s)) 25,248 bytes maximum slop 16 MB total memory in use (14 MB lost due to fragmentation) Tot time (elapsed) Avg pause Max pause Gen 0 129923 colls, 0 par 0.666s 0.654s 0.0000s 0.0001s Gen 1 2 colls, 0 par 0.001s 0.001s 0.0006s 0.0007s INIT time 0.000s ( 0.000s elapsed) MUT time 13.029s ( 13.048s elapsed) GC time 0.667s ( 0.655s elapsed) EXIT time 0.001s ( 0.001s elapsed) Total time 13.700s ( 13.704s elapsed) %GC time 4.9% (4.8% elapsed) Alloc rate 5,238,049,412 bytes per MUT second Productivity 95.1% of total user, 95.1% of total elapsed 

生産性が倧幅に向䞊したした。 デヌタの䞍倉性により、合蚈メモリコストは䟝然ずしお印象的でしたが、最も重芁なこずは、デヌタの遅延を制限したこずで、ガベヌゞコレクタヌが遅延する機䌚があり、䜜業の5だけが残っおいるこずです。 評䟡に新しい゚ントリを入力したす。



さお、厳密な蚈算により、Haskellのネむティブコヌドの速床に近づき、仮想マシンなしでは恥ずかしく遅くなりたす。 これは、䞍倉ベクトルを䜿甚するオヌバヌヘッドが、スタックされたマシンを維持するコストを倧幅に䞊回るこずを意味したす。 これは、メモリの䞍倉性に別れを告げる時が来たこずを意味したす。


人生を倉える


Data.Vector型Data.Vector適切ですが、それを䜿甚しお、コンピュヌティングプロセスの玔床を維持するために、コピヌに倚くの時間を費やしたす。 これをData.Vector.Unpackedタむプに眮き換えるず、少なくずも構造䜓のパッケヌゞングは​​節玄されたすが、これにより画像が根本的に倉わるこずはありたせん。 正しい解決策は、マシンの状態からメモリを削陀し、Claysleyカテゎリを䜿甚しお倖郚ベクトルぞのアクセスを提䟛するこずです。 さらに、玔粋なベクトルずずもに、いわゆる可倉可倉ベクトルData.Vector.Mutable䜿甚できたす。


適切なモゞュヌルを接続し、クリヌンで機胜的なプログラムで可倉デヌ​​タを凊理する方法を考えたす。


 import Control.Monad.Primitive import qualified Data.Vector.Unboxed as V import qualified Data.Vector.Unboxed.Mutable as M 

これらのダヌティタむプは、玔粋な倧衆から隔離されるこずになっおいたす。 それらは、 PrimMonadクラスのモナド STたたはIO含むに含たれおおり、クリヌンプログラムは、貎重な矊皮玙にクリスタル関数型蚀語で蚘述されたアクションの指瀺を泚意深く挿入したす。 したがっお、これらの汚れた動物の行動は厳密に正統的なシナリオによっお決定され、危険ではありたせん。 私たちのマシンのすべおのプログラムがメモリを䜿甚するわけではないので、アヌキテクチャ党䜓をIOモナドに没頭させる必芁はありたせん。 monopig蚀語のクリヌンなサブセットずずもに、メモリぞのアクセスを提䟛する4぀の呜什を䜜成したす。これらの呜什のみが危険な領域にアクセスできたす。


クリヌンマシンの皮類は短くなっおいたす。


 data VM a = VM { stack :: !Stack , status :: Maybe String , journal :: !a } 

プログラムの蚭蚈者ずプログラム自䜓はこの倉曎にほずんど気付かないでしょうが、そのタむプは倉わりたす。 さらに、シグニチャヌを簡玠化するために、いく぀かのタむプの同矩語を定矩するこずは理にかなっおいたす。


 type Memory m = M.MVector (PrimState m) Int type Logger ma = Memory m -> Code -> VM a -> m (VM a) type Program' ma = Logger ma -> Memory m -> Program ma 

コンストラクタには、メモリアクセスを衚す別の匕数がありたす。 実行者、特に蚈算ログを保持しおいる人は、倉数ベクトルの状態を尋ねる必芁があるため、倧幅に倉わりたす。 完党なコヌドはリポゞトリで確認および調査できたすが、ここで最も興味深いのは、メモリを操䜜するための基本コンポヌネントの実装であり、これがどのように行われるかを瀺したす。


 geti :: PrimMonad m => Int -> Program' ma geti i = programM (GETI i) $ \mem -> \s -> if (0 <= i && i < memSize) then \vm -> do x <- M.unsafeRead mem i setStack (x:s) vm else err "index out of range" puti :: PrimMonad m => Int -> Program' ma puti i = programM (PUTI i) $ \mem -> \case (x:s) -> if (0 <= i && i < memSize) then \vm -> do M.unsafeWrite mem ix setStack s vm else err "index out of range" _ -> err "expected an element" get :: PrimMonad m => Program' ma get = programM (GET) $ \m -> \case (i:s) -> \vm -> do x <- M.read mi setStack (x:s) vm _ -> err "expected an element" put :: PrimMonad m => Program' ma put = programM (PUT) $ \m -> \case (i:x:s) -> \vm -> M.write mix >> setStack s vm _ -> err "expected two elemets" 

putiおよびgetiむンデックスはプログラム䜜成の段階で既知であり、誀った倀を事前に陀去できるため、オプティマむザヌデヌモンはすぐにメモリ内の蚱容むンデックス倀のチェックをもう少し節玄するこずを提案したした。 putおよびgetコマンドの動的に定矩されたむンデックスはセキュリティを保蚌せず、数孊者の゚ンゞェルはそれらぞの危険な呌び出しを蚱可したせんでした。


メモリを個別の匕数に入れるこずに関するこのすべおの隒ぎは耇雑に思えたす。 しかし、デヌタは堎所によっお倉曎されるこずを非垞に明確に瀺しおいたす。デヌタは倖郚にある必芁がありたす。 ピザ配達人を無菌怜査宀に連れお行こうずしおいるこずを思い出させおください。 玔粋な機胜は䜕をすべきかを知っおいたすが、これらのオブゞェクトは決しお䞀流の垂民になるこずはなく、実隓宀でピザを準備する䟡倀はありたせん。


これらの倉曎で賌入したものを確認したしょう。


 $ ./Monopig5 +RTS -sstderr "Ok" 9,169,192,928 bytes allocated in the heap 2,006,680 bytes copied during GC 529,608 bytes maximum residency (2 sample(s)) 25,248 bytes maximum slop 2 MB total memory in use (0 MB lost due to fragmentation) Tot time (elapsed) Avg pause Max pause Gen 0 17693 colls, 0 par 0.094s 0.093s 0.0000s 0.0001s Gen 1 2 colls, 0 par 0.000s 0.000s 0.0002s 0.0003s INIT time 0.000s ( 0.000s elapsed) MUT time 7.228s ( 7.232s elapsed) GC time 0.094s ( 0.093s elapsed) EXIT time 0.000s ( 0.000s elapsed) Total time 7.325s ( 7.326s elapsed) %GC time 1.3% (1.3% elapsed) Alloc rate 1,268,570,828 bytes per MUT second Productivity 98.7% of total user, 98.7% of total elapsed 

これはすでに進行䞭です メモリ䜿甚量は8倍に削枛され、プログラムの実行速床は180倍に増加し、ガベヌゞコレクタヌはほが完党に䜜業なしで残りたした。



溶液はmonopig st。に芋えたした。 mut。 、これはjsのネむティブ゜リュヌションよりも10倍遅いですが、Haskellのネむティブ゜リュヌションずは異なり、可倉ベクトルを䜿甚しおいたす。 圌のコヌドは次のずおりです。


 fill' :: Int -> Int -> Memory IO -> IO (Memory IO) fill' knm | n > memSize-k = return m | otherwise = M.unsafeWrite mn 1 >> fill' k (n+k) m sieve' :: Int -> Memory IO -> IO (Memory IO) sieve' km | k*k < memSize = do x <- M.unsafeRead mk if x == 0 then fill' k (2*k) m >>= sieve' (k+1) else sieve' (k+1) m | otherwise = return m 

次のように始たりたす


 main = do m <- M.replicate memSize 0 stimes 100 (sieve' 2 m >> return ()) print "Ok" 

そしお今、Haskellは぀いに圌がおもちゃの蚀語ではないこずを瀺しおいたす。 あなたはそれを賢く䜿甚する必芁がありたす。 ずころで、䞊蚘のコヌドは、タむプIO ()がプログラムの順次実行(>>)の操䜜でセミグルヌプを圢成し、 stimesの助けを借りおテスト問題の蚈算を100回繰り返したずいう事実を䜿甚しおいたす。


これで、関数配列がそんなに嫌いなのはなぜか、そしおなぜ圌らず䞀緒に䜜業するのか誰も芚えおいないのは明らかです。


コマンドの䞀郚を特別なゟヌンに入れるず、同型の合法性に疑問が生じたす  longleftrightarrowプログラム 。 結局のずころ、コヌドを同時に玔粋なプログラムずモナドなものに倉えるこずはできたせん。これにより、型システムはそうするこずができたせん。 ただし、型クラスはこの同型が存圚するのに十分な柔軟性を備えおいたす。 準同型写像  longrightarrowこのプログラムは珟圚、蚀語のさたざたなサブセットのいく぀かの準同型に分割されおいたす。 これがどのように行われるかは、プログラムの[コヌド]党䜓で確認できたす。


止たらないで


䞍芁な関数呌び出しをなくし、 {-# INLINE #-}プラグマを䜿甚しおコヌドを盎接埋め蟌むず、プログラムの生産性がわずかに倉わりたす。 このメ゜ッドは、再垰関数には適しおいたせんが、基本的なコンポヌネントおよびセッタヌ関数には適しおいたす。 テストプログラムの実行時間をさらに25 短瞮したす Monopig51.hsを参照。



次の劥圓なステップは、ログツヌルが䞍芁な堎合は削陀するこずです。 プログラムを衚す内郚準同型の圢成の段階で、起動時に決定する倖郚匕数を䜿甚したす。 スマヌトコンストラクタヌprogramおよびprogramMは、匕数ロガヌがない可胜性があるこずを譊告できたす。 この堎合、コンバヌタコヌドには䜙分なものは含たれおいたせん。機胜ずマシンのステヌタスの確認のみです。


 program code f = programM code (const f) programM code f (Just logger) mem = Program . ([code],) . ActionM $ \vm -> case status vm of Nothing -> logger mem code =<< f mem (stack vm) vm _ -> return vm programM code f _ mem = Program . ([code],) . ActionM $ \vm -> case status vm of Nothing -> f mem (stack vm) vm _ -> return vm 

珟圚、実行䞭の関数は、 noneスタブを䜿甚せnone 、 Maybe (Logger ma)タむプMaybe (Logger ma)を䜿甚しお、ロギングの有無を明瀺的に瀺す必芁がありたす。 ロギングがあるかどうかにかかわらず、プログラムコンポヌネントは実行の前に「最埌の瞬間」を芋぀けるので、なぜこれが機胜するのですか プログラムの構成を䜜成する段階で、䞍芁なコヌドを瞫い付けたせんか Haskellは怠zyな蚀語であり、ここで私たちの手に枡りたす。 最終コヌドが特定のタスク甚に最適化されるのは、実行前です。 この最適化により、プログラムの実行時間がさらに40短瞮されたした Monopig52.hsを参照。



これで、モノむドの子豚を加速する䜜業を完了したす。 圌はすでに倩䜿ず悪魔の䞡方が萜ち着くこずができるように十分に速く走っおいたす。 もちろん、これはCではなく、ただクリヌンリストをスタックずしお䜿甚しおいたすが、それを配列に眮き換えるず、コヌドが培底的にシャベルになり、基本的なコマンドの定矩で゚レガントなテンプレヌトの䜿甚が拒吊されたす。 最小限の倉曎で、䞻にタむプのレベルで察凊したかったのです。


ロギングにはいく぀かの問題が残っおいたす。 単玔なステップ数のカりントやスタックの䜿甚はうたく機胜したすログフィヌルドを厳密にしたしたが、それらのペアリングは既にメモリを消費し始めおいるので、 seqを䜿甚しおキックに悩たさなければなりたせん。 しかし、最初の数癟のタスクをデバッグできる堎合、誰が140億のステップを蚘録したすか したがっお、加速のために加速に時間を費やすこずはありたせん。


蚈算を最適化する方法の1぀ずしお、子豚に関する蚘事にそれを远加するだけです。トレヌスの提䟛コヌドの線圢セクションの割り圓お、メむンコマンドディスパッチサむクル switchブロックをバむパスしお蚈算を実行できるトレヌス 。 この堎合、プログラムコンポヌネントのモノむド構成は、EDSコンポヌネントからのプログラムの圢成䞭、たたはfromCode準同型がfromCodeいるずきに、そのようなトレヌスを䜜成したす。 この最適化方法は、いわば、建蚭によっお無料で提䟛されたす。


∗ âˆ— âˆ—


Haskell゚コシステムには、 ConduitsやPipesストリヌムなどのConduits高速な゜リュヌションが数倚くあり、優れたString眮換やblaze-htmlなどの軜快なXMLクリ゚ヌタヌがありたすattoparsecパヌサヌは、LL∞文法の組み合わせ分析の暙準です。 これはすべお通垞の操䜜に必芁です。 しかし、これらの決定に぀ながる研究がさらに必芁です。 Haskellはこれたでも珟圚も、䞀般の人々が必芁ずしない特定の芁件を満たす研究ツヌルです。 私はカムチャッカで、Mi-4ヘリコプタヌの゚ヌスが議論でマッチ箱を閉じ、空䞭にぶら䞋がっおいる間に車茪で着陞装眮を抌す方法を芋たした。 これは実行できたすが、クヌルですが必芁ではありたせん。


しかし、それにもかかわらず、これはクヌルです!!



Source: https://habr.com/ru/post/J430956/


All Articles