- Incrementalは、入力が変化したときに複雑な計算を効率的に更新できるよう支援するライブラリ
- Umut Acar らの 自己調整計算(self-adjusting computation) の研究に着想を得ている
- スプレッドシートのような大規模計算がデータ変更に効率的に反応するよう構成できる
- GUI アプリケーションのビューに新しいデータを効率的に反映できる
- フィルタリングやマッピングの逆変換のような 派生データ が元データと継続的に同期されることを保証できる
活用分野
- 変化する入力に対応して 複雑な計算 を効率的に更新する
- スプレッドシート形式の大規模計算がデータ変化に反応するよう構成できる
- GUI ビューに新しいデータを効率的に統合できる
- 元データから計算された派生データを継続的に同期する
- データのフィルタリング
- マッピングの逆変換
設計背景とドキュメント
- Umut Acar らの自己調整計算の研究に着想を得ている
- 詳細な API と使い方は incremental/src/incremental_intf.ml で確認できる
- 非公式の入門資料として ブログ記事 と 紹介動画 を提供している
1件のコメント
Hacker Newsのコメント
この種のリアクティブプログラミングは、最近の JavaScript UI フレームワークでは signals という名前で広く使われており、標準化提案も進んでいる。
Vue、SolidJS、Svelte、Ember、Angular が採用しており、React には MobX や Jotai のような実装がある。変更伝播と有向非巡回グラフ(DAG)の評価アルゴリズムにもさまざまな種類があり、SolidJS 2 は Incremental に似た高さベースのアルゴリズムを使っていると理解している。
私はノードを
Int32Arrayアリーナに割り当て、連結リストでつなぐことで、依存エッジ数に比例する GC 負荷を避ける実装を試している。Rust にも複数の実装があり、UI フレームワークとしては Leptos、rust-analyzer が使う汎用の増分計算システムとしては Salsa がある。これを依存関係を自動追跡するビルドシステムと見ることもでき、tup はビルド作業を計測してどのファイルを読んだかを検出し、依存関係を設定する。著者の記事と古典的な Build Systems à la Carte も読む価値がある。
ただし 増分計算と関数型リアクティブプログラミング(FRP) は実際には別の領域だ。増分計算はデルタ上で動作する関数を明示的に導出するが、FRP は壊れた部分を見つけて修復する方式だけを使うこともある。
Incremental ライブラリは、元データが変わったときに計算グラフを 部分的に再具体化 する問題を解決しようとしているようだ。よく設計されたビルドシステムに似ており、関数型プログラミングでもよく使われる有用なアプローチだ。
増分計算の分野には Differential Dataflow、隣接技術である Timely Dataflow、DBSP もある。Feldera は DBSP ベースで、Materialize は Differential Dataflow の関係者が率いている。
私は金融データとワークロードに特化した別アプローチである modolap を開発している。解くべき大きく重要な問題がたくさんある。関連して、Signals and Threads のビルドシステム回も参考になる。
Goldman も約 30 年前に金融商品の価格算定で同じアプローチを使っていた。そこで約 13 年働くあいだ、Node Purpling に関する長い議論を覚えている。
コンピュータサイエンスは進歩しており、私の見る限りこれはグラフアプローチではないが、微分のような計算は高コストなので、実行回数を理論的最小値にできるだけ近づける必要がある。関連する HN 議論 もある。
専用の社内 IDE を見た瞬間に腹を立てて辞めないとしても、新人が適応するのに異様に時間がかかり、数か月たっても根本的に異なる要素を学び続けなければならないというくだりが、この問題を最もよく表している。私も作業内容を完全に理解するまで約 2 年半かかった。
再教育が必要だと気づくまでは、現代的な教育もほとんどなかった。最悪だったのは UI を作るためのコーディングで、新規プロジェクトでは使用が承認されなかった。
お気に入りの技術講演の 1 つは Seven Implementations of Incremental だ: https://www.janestreet.com/tech-talks/seven-implementations-of-incremental/
数年前、データフロープログラミング に強い関心があり、多くの人がさまざまな方向からこの問題に取り組んでいたように思う。このライブラリを見ると、すぐに Clojure の Javelin を思い出した。
興味があるなら、Incremental の上に構築された UI ライブラリ Bonsai も見る価値がある。
React のようなライブラリは仮想 DOM によって不要な作業を効率よく省くが、仮想 DOM 自体を作るのにも時間がかかる。Bonsai は 仮想 DOM まで増分化 しており、触っていて面白い。
私は現在は保守されていない Revery 向けにデスクトップ UI ライブラリを作ったが、かなり古い Bonsai バージョンを使っている。
これが、入力に新しい値を発行し、計算を経て新しい結果を購読者に渡すオブザーバブル・パターンとどう違うのか、完全には理解できていない。
変更検知や、値がそのままのときに伝播を止める最適化はあるのだろうが、それはオブザーバブルでも可能だ。
stabilizeで再計算前に変更を一括処理する部分も興味深いが、やはりオブザーバブルで実装できそうだ。核心的な違いが、内部的な探索によって計算グラフを自動構築する点にあるのか、それとももっと根本的な要素があるのか気になる。
一部のノードだけを観測する場合、グラフ全体を具体化する必要はない。計算はいつでも中断して、グラフを部分的にだけ更新された状態のままにし、入力をさらに変更したあとで、関心のあるノードの具体化を続けることもでき、アルゴリズムがすべての変更を整理してくれる。
増分計算は本質的にこうした特性を含む総称であり、同じシステムを観測者と購読者のモデルとして構成することもできる。古典的な Excel スプレッドシートがよい例で、Salsa アルゴリズムの説明も参考になる。
Ron Minsky の発表はとても良い。
数百個の中間ノードに分岐し、長さの異なる経路を通って再び合流するダイヤモンド型のサブグラフを考えてみるとよい。一部の経路に
min(A, B)があり、最大値側だけが変わるような場合もある。単純なオブザーバ方式では、計算量が潜在的に指数的に爆発し、並行性の問題も引き起こしかねない。このライブラリは、グラフ構造を動的に変更しても最適に近く、しかも正確だ。オブザーバなどでも同じ結果は出せるが、性能の崖を避けつつ正しく実装するのははるかに難しい。
Jane Street のプロジェクトの長所は、研究用途やニッチなシステムにあった発想を、開発者が実際に使える形にパッケージ化する点にある。ライブラリを導入しなくても、設計文書はたいてい読む価値がある。
Electric Clojure は、クライアント・サーバー境界をまたぐ増分レンダリングを提供する。最も近いのは SolidJS だと思うが、SolidJS はフロントエンドに限られる。
以前、似たようなものを作ったことがあるが、先例をほとんど見つけられなかった。用途がなくなってプロジェクトは終了したが、また見直してみたいと思っており、npm に
data-ramblerという名前で残っている。構想としては、JavaScript ランタイムにロードできる**ドメイン特化言語(DSL)**にデータストリームを供給する方式だった。モジュールがデータを複数の出力ストリームに変換し、それを別のレポートライブラリに渡して、テンプレートベースの動的レポートを生成する。
最初のバージョンからかなり強力だったが、複雑さを減らすために JavaScript らしい文法へ改善する大きな計画もあった。