sosukesuzuki.dev

May, 25 2020

Deno に採用されたコードフォーマッター dprint が速い

Deno では以下のようにサブコマンドを実行することで、ソースコードのフォーマットをできます。

ref: https://deno.land/manual/tools/formatter

$ deno fmt example.ts

オフィシャルにコードフォーマッターを提供することによって、少なくとも Deno の世界のために記述されるコードのスタイルが開発者によってバラバラになったり、そのために議論が生まれるということはなさそうです。これは大変良いことです。

この deno fmt コマンドの内部では以前までは Prettier が使用されていたのですが、 deno v 0.32.0 から dprint という Rust で書かれたコードフォーマッターが使われるようになりました。

ref: https://github.com/denoland/deno/pull/3820

この dprint、どうやら実行速度がとても速いという噂を聞いたので、実際のコードでためして Prettier と比較してどの程度速いのか試してみようと思います。

実行マシンとベンチマーク方法

以下の性能の MacBook Pro で計測を行います。

MacBook Pro (13-inch, 2019, Four Thunderbolt 3 ports)
プロセッサ 2.4 GHz Intel Core i5
メモリ 16 GB 2133 MHz LPDDR3
グラフィックス Intel Iris Plus Graphics 655 1536 MB

以下のような形のディレクトリを作り、dprintprettierをローカルインストールします。/repositories下には https://github.com/prettier/prettierhttps://github.com/mictosoft/typescript がクローンされています。dprint.config.jsprettierrc はデフォルトのままにします。

.
├── dprint.config.js
├── package.json
├── repositories
   ├── prettier
   └── typescript
└── yarn.lock

hyperfineを使って、それぞれnode_modules/.bin/下にある bin を叩いて計測をします。

計測してみる

まず、そこまでコード量が多くない https://github.com/prettier/prettier/src ディレクトリ下の JavaScript ファイルを対象にして時間を計測してみます。

対象ファイル数: 117
対象コード行数: 29205
$  hyperfine "./node_modules/.bin/dprint \"repositories/prettier/src/**/**/*.js\"" "./node_modules/.bin/prettier \"repositories/prettier/src/**/**/*.js\" --write" --ignore-failure
Benchmark #1: ./node_modules/.bin/dprint "repositories/prettier/src/**/**/*.js"
  Time (mean ± σ):      1.209 s ±  0.206 s    [User: 4.046 s, System: 0.139 s]
  Range (min  max):    1.003 s …  1.663 s    10 runs

Benchmark #2: ./node_modules/.bin/prettier "repositories/prettier/src/**/**/*.js" --write
  Time (mean ± σ):      2.519 s ±  0.135 s    [User: 4.123 s, System: 0.136 s]
  Range (min  max):    2.318 s …  2.709 s    10 runs

Summary
  './node_modules/.bin/dprint "repositories/prettier/src/**/**/*.js"' ran
    2.08 ± 0.37 times faster than './node_modules/.bin/prettier "repositories/prettier/src/**/**/*.js" --write'

平均値を見てみます。

dprint: 1.209 s
prettier: 2.519 s

この規模のプロジェクトでも、 dprint のほうが 2 倍くらい速くなっています。

少し大きめのコードでも試すために、 https://github.com/microsoft/typescript/src 下の TypeScript ファイルに対してもやってみます。

対象ファイル数: 430
対象コード行数: 438366
$  hyperfine "./node_modules/.bin/dprint \"repositories/TypeScript/src/**/**/*.ts\"" "./node_modules/.bin/prettier \"repositories/TypeScript/src/**/**/*.ts\" --write" --ignore-failure
Benchmark #1: ./node_modules/.bin/dprint "repositories/TypeScript/src/**/**/*.ts"
  Time (mean ± σ):      5.295 s ±  0.143 s    [User: 7.884 s, System: 0.213 s]
  Range (min  max):    5.132 s …  5.548 s    10 runs

Benchmark #2: ./node_modules/.bin/prettier "repositories/TypeScript/src/**/**/*.ts" --write
  Time (mean ± σ):     22.387 s ±  0.242 s    [User: 38.089 s, System: 1.103 s]
  Range (min  max):   22.121 s … 22.848 s    10 runs

  Warning: Ignoring non-zero exit code.

Summary
  './node_modules/.bin/dprint "repositories/TypeScript/src/**/**/*.ts"' ran
    4.23 ± 0.12 times faster than './node_modules/.bin/prettier "repositories/TypeScript/src/**/**/*.ts" --write'

平均値を見てみます。

dprint: 5.295 s
prettier: 22.387 s

この規模になってくると差が顕著になってきますね。

速い

体感でわかるくらい速いし、測ってみたら速かったです。今後 Deno や dprint がどうなっていくかはわかりませんが、1.0 がリリースされる前の段階で dprint のような Rust 製のコードフォーマッターに移行したのは英断だったように思います。

コードフォーマッターとしての性能はまだ見れていないのでなんとも言えませんが、もしかしたら通常の TypeScript/JavaScript プロジェクトにも採用できるかもしれません。今度は、 dprint に Prettier のテストケースを食わせてちゃんとフォーマットできるかやってみようと思います。