🌇

Dart製のベンチマークHakariを開発!forEachやmap遅すぎィ!なことを即時測定デキルヨ!

まとめ

  • JavaScript系のベンチマーク、mitataのようなリッチベンチマークをDartでも取りたい
  • シンプルな記法で、リッチな結果を得られるpackage、Hakariを開発したよ!
  • x倍棒グラフで測定結果をリッチに確認できるHakariはGood!

JavaScript系のベンチマーク、mitataに痺れる、憧れるゥ!

https://twitter.com/yusukebe/status/1689124758984724480?s=20

HonoというNode, Deno, Bunなどで動くJavaScriptフレームワークを開発している @yusukebeさんがよく mitata というベンチマークソフトを使っているのを見かけます。

mitata は簡単なテストを書くようにシンプルに記載できて、かつ結果が美しく、わかりやすく表示されるのが特徴です。クロスランタイムなベンチマークをとれるのがmitataの大きな特徴ですが、単純な書きやすさと、結果の見やすさは目を見張るものがあります。

Dart製のシンプルかつ、美しいベンチマークパッケージを作りたい

Dart/Flutterでは目立って優れたデファクトスタンダードと言えるようなベンチマークライブラリは発見できませんでした。

でも、

  • 同種のライブラリの処理時間を比較したい
  • Rustを呼び出してDartネイティブよりも優れているか測定したい
  • アルゴリズムを適用してパフォーマンスが向上したか証明したい

そんなタイミングはDart/Flutterの開発現場でも発生するものです。

https://zenn.dev/minedia/articles/d6f713fbd4b0d5

実際、僕も以前の記事でRust製のMarkdown parserとDart製のMarkdown parserを比較する記事を書いたことがありますが、その際に自前の測定ライブラリもどきを実装したりするのが面倒でした。

なんなら、そのライブラリもどきの実装に時間を取られて、本当にフォーカスするべき実装や、記事の執筆に集中できなくてイライラしたりするBadな体験が発生していました。

mitataのような、手軽でクールなライブラリ、ないなら作っちゃえばいいじゃん。

そう思い、作ってみました👌

Hakariについて

https://pub.dev/packages/hakari

Hakariは、mitataにインスパイアされた、クールでシンプルなベンチマーク用packageです!

ベンチマークは以下のように簡単に記載 → 実行することができます

// ベンチマーク用に10万個の要素を持つ配列を生成
final list = List.generate(100000, (index) => index);

// 各測定結果を比較するためのGroupを作成
final group = Group(
   name: 'Exploring fast loops in Dart', // 結果に表示されるグループ名を設定
  // それぞれのベンチを定義
  benches: [
  // 各ベンチの名称、処理内容を定義
    Bench('loop', () async {
      var count = 0;
      num eachElement = 0;
      while (count < list.length) {
        eachElement = pow(list[count], 3);
        count++;
      }
    }),
    Bench('for in', () async {
      num eachElement = 0;
      for (var element in list) {
        eachElement = pow(element, 3);
      }
    }),
    Bench('forEach', () async {
      num eachElement = 0;
      list.forEach((e) {
        eachElement = pow(e, 3);
      });
    }),
    Bench('map', () async {
      num eachElement = 0;
      list.map((e) {
        eachElement = pow(e, 3);
      }).toList();
    }),
  ],

// Hakariのインスタンスにグループを登録
// 複数のグループを登録することも可能
final hakari = Hakari(
    groups: [
      group,
    ],
  // optionも柔軟に設定可能
    option: HakariOption(
      maxLineCharNum: 80,
      iter: 100,
      // emphasisAnsiColor: some value [0-255],
      // barAnsiColor: some value [0-255],
    

// 測定開始!👍
await hakari.start();
  • Hakari
    • Group(複数登録可)
      • Bench(複数登録可)

上記の簡単な構造を作るだけで、ベンチマークを取れます。

ではさっそく結果を見てみましょう!

Hakariで測定してみた : forEach, mapはめちゃくちゃ遅いぞ

Hakariには以下の特徴があります。

  • 測定中のベンチマークの名称が表示される
  • 測定したCPUが表示される
  • 各ベンチマークの Avg , min-max が表示される
  • 最速のベンチマークのAvgが、他のベンチのAvgと比較してどれだけ早いか表示される
  • 各ベンチマークの処理速度が棒グラフによって可視化される
  • ターミナルの文字数や色HakariOption によって柔軟に変更可能

上記の画像は、10万個の要素をもつ配列を、 loop for in forEach map それぞれで処理してみた結果です。同じような処理ですが、結果は結構違っており、mapは圧倒的に遅くなりますし、forEachもずいぶんloopより遅いことがわかります。

まぁこの結果はDart界隈では有名な話なので、ここでは深く触れませんが、ちょっと重めの処理で loop を使ってみようというモチベーションにはなるかもしれませんね。

Hakariのユースケースとしては、まさに本件のように複数の書き方がある処理において、どれがベストプラクティスなのかを比較するケースを考えています。

また、新しい技術の導入を考えているときのエビデンスの1つにもなるでしょう

当然、フレームワークやライブラリの開発者のエビデンスとしても有効ですし、レビューのタイミングで「あれ?」と思った処理があったとき、Hakariの測定結果をコメントに残してあげたりすることも可能になります。

ちなみに、Hakariの内部実装は DateTime.now().**microsecondsSinceEpoch()** を利用しているため、minで0μsを記録していますが、測定結果はそこそこ信頼できるものだと考えています。

Happy Benchmarking!🚀

ぜひHakariを使って、パフォーマンスをガンガン上げていきましょう!

本当はCPU使用率やメモリ使用量についての指標も一緒に測定したいところですが、再現性のある有用な測定手法を見つけられず、今回Hakariへの実装は断念しました。

https://github.com/obutora/hakari

有力な測定方法、実装方法のほか、UXやその他の改善については、
IssueやPRもめちゃめちゃ歓迎です!

おまけ

likeください!!
https://twitter.com/hagakun_yakuzai

株式会社マインディアでは、Flutterリードエンジニア、Railsエンジニアを募集しております。
カジュアル面談などの場を用意しておりますので、気軽にお声がけください。

引き続き、Flutter周りの気になることを調査したり、ジュニアなエンジニアが思うことも記事にしていきますので、良かったらZennTwitterのフォローをお願いします!

株式会社マインディア テックブログ

Discussion