LoginSignup
17
0

More than 5 years have passed since last update.

elmのコンパイラがめっちゃ遅かったから速くしたけどまだ遅かったからもっと早くした

Last updated at Posted at 2018-12-21

Fringe81 Advent Calendar 2018の21日目です。

elmのコンパイラがめっちゃ遅かったから速くしたの続編

忙しい人のためのネタバレ

Elmのコンパイラバージョンを上げてコンパイルが100倍早くなった

前回までのあらすじ

Elmで実装されたフロントエンドアプリケーションのコンパイル時間が半端なく長くなってしまった!
そこでヒープサイズを盛ることによって3倍くらい改善して、250秒くらいから30秒くらいにはなった!

前回のエピローグより...

0.19ではRTSオプション気にしなくても超早くなりました。

Road To 0.19

超早くなるといってもプロダクトのコードやってなかったのでやっていくぜ!

やる前のインプット

読み物によってインプットしたことを実施していく

  • まずはelm-upgradeをポチッとな。
  • あとはelm-upgradeが出してくれたあとはコレやってねリストにしたがっていく...

「ここからが本当の地獄だ...]

  • コレやってねリスト -> 注文が多い
    • 自分らで作ってたイケてる演算子を抹消 & 関数呼び出しに置き換える
    • Dateモジュールに依存していたところをTimeモジュールを使っていい感じにするようによしなにする
    • CustomTypeに対してtoStringを使っていたところを専用のtoStringを定義してあげる
    • Html.styleを使っていたところをどうにかする
    • elm/regexの変更に対応するように変更する
    • Debug.crashを抹消する
    • etc...

やること多いな!

モジュールが新しくなったり、関数の返す型が変わったりプロダクトのソースコードもかなり変更が必要だなというのがelm-upgradeによってわかったところですべての対応を一気にやろうとするのは骨が折れそうなのでチェックポイントを設けて実施することにしました。

フロントエンドアプリの構成が当時は2つのElmアプリケーションからできていて、片方は1画面でもう片方が複数ページを扱うような感じでした。

なので1画面アプリケーションのほうから着手してみて問題がないこと、やりきれること、がわかったらもう片方も着手することにしました。

さらに、Debug.crash等のDebugモジュールは抹消しなくてもビルドやアプリケーションを動作させることは叶うので作業を分けてみることにしました。とくにDebug.crashはMaybe型を強制的にunwrapするために使ってたという事実があり抹消が困難だろうという目算がありました。

そこで立てた計画が以下になります

1画面アプリケーションの0.19移行
- `Html.style`等を0.18のころのように扱えるファサードの実装
- 各種ライブラリのバージョンアップ追従のための変更
- Debugモジュールの抹消

*↓は↑が行けたら行く *

複数アプリケーションの0.19移行
- 各種ライブラリのバージョンアップ追従のための変更
- Debugモジュールの抹消

↑を実施する上での戦いを紹介しておきます↓

Html.styleを使っていたところをどうにかする

  • もうElmからあんまstyleを定義すんなよ! とHtml.Attributes.style関数のドキュメントにあるようにもうあんまやっちゃだめなんだな!とわかりつつ、今までElm側でstyleを指定していたものをcssに分離するのは大変だなあという甘えがあったので以下のような関数を定義して逃しました。
styles : List ( String, String ) -> List (Html.Attribute msg)
styles =
    List.map (\( propname, propvalue ) -> Attr.style propname propvalue)

使う側としては

-- elm 0.18 ver
viewStyle = style [("width", "100px")]

view = div [ viewStyle ] []
-- elm 0.19 ver
viewStyle = styles [("width", "100px")]

view = div ([] ++ viewStyle) []

美しくないな!やCSSかけや。と感じつつ一旦コンパイルを通すという目標のために逃げました。
真似せずCSSを書いてクラス名でしたほうがいいですね!

Dateモジュールに依存していたところをTimeモジュールを使っていい感じにするようによしなにする

DateモジュールがリストラになってTimeモジュールを使ってネということになったので
Date型でフィールドを定義していたレコードはすべてTime.Posix型に変更しました。

yyyy-mm-ddとして表示するようなタイムゾーンに依存していたところはasia/tokyoのZone型の値を用意しておくような対応をしました。

-- 0.18時点
formatYYYYMMDD : Date -> String
-- 厳密にやると以下のようにしたいところですが。(Time.yearやTime.monthがZone型を取るため)
formatYYYYMMDD : Zone -> Time.Posix -> String

-- 関数の中でZone型の値はすべて`asia/tokyo`のものを勝手に渡すものとして以下のように定義していきました。
formatYYYYMMDD : Time.Posix -> String

プロダクト的に表示される時間はすべて日本時間でも問題なかったので↑のような逃げ方ができました。

elm/regexの変更に対応するように変更する

elm/parserを使ってネ!と丁寧にelm/regexのREADMEで書かれていた気がするが、オラア正規表現が使いたいんや。プロダクトのコードを変更させんでくれ。というお気持ちでelm/parserは華麗にスルーしたところで
elm/regexの変更へ追従していく変更を開始しました。

Regex.regexという引数に文字列をとるRegex型のファクトリ関数があったので今まで以下のようにしていました。


someRegex = Regex.regex "なんか正規表現文字列"

0.19からはRegex.fromStringという関数に変わりました。


someRegex = Regex.fromRegex "なんか正規表現文字列"

しかしこの関数Maybeを返すようになりました。
関数に渡した文字列が正規表現として間違っていたらNothingで返してくれるというなんとも便利な仕様です。

が、0.18のころからコードをあまり変えたくないというワガママな私には困ったものでした。
ということで↓のように関数を定義してここでも逃げました。

fromRegexUnsafe regexString = 
    Regex.fromRegex regexString |> Maybe.withDefault Regex.never

ここまで作業をするとelm makeは実行できるようになります。
そしてここからは--optimizeフラグを利用するためにDebugモジュールを使わないようにしていきます。

Debug.crashを抹消する

お恥ずかしいところですが、
Maybe型やResult型の値を強制的にとりだすためにDebug.crashを使っていました。

-- 真似しちゃだめよ!!!!!
sure : Maybe a -> a
sure maybeValue = case maybeValue of
    Just value -> value
    Nothing -> Debug.crash "ありえないんだぜ"

ここは地道にこういうことをしている関数がすべてMaybeを返す or Resultを返すように定義していきました。
とくにサーバサイドとのコミュニケーションをするようなコードではコレが蔓延していたのでとても辛い戦いになりました。

すべての作業が終わった結果


before(0.18)
npx elm-make src/Main.elm --output app.js  384.90s user 136.40s system 355% cpu 2:26.53 total

before(0.18 & https://qiita.com/4245Ryomt/items/20712197c9b4b76f0f5b)
npx elm-make src/Main.elm --output app.js 59.28s user 13.68s system 128% cpu 56.635 total

after(0.19)
npx elm make src/Main.elm --output app.js  1.88s user 0.34s system 65% cpu 3.380 total

圧倒的改善...!

300倍以上早くなってますね!

これで生産性も300倍!!!

 やり終わって

  • 0.18と0.19が混在していた時期に手の混んだwebpackのコンフィグ作ったりココに書き切れないくらい苦労はあった。
  • バージョンアップで苦労したところってこれはイイのかな。。。ホントは良くないんだろうな。。。と思っていたところだった。
    • Debug.crashをデバッグ以外の用途で使う
    • Date型を直接持つ(タイムゾーンを意識しないで時間をどうにかするアプリをつくることも)

結構学びはあった。

ご注意

複数ページアプリは0.19にして問題もでた。--debugフラグをつけてコンパイルできなくなっちゃう〜というコンパイラのバグに遭遇してelmの便利開発ツールが有効にできてない。
300倍のコンパイル速度と天秤にかけて、そのうち治してくれるだろうということで今は我慢している。
https://github.com/elm/compiler/issues/1817#issuecomment-440194948

17
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
0