OCamlに新しい拡張機能セット登場!OxCamlとは?
引用元:https://news.ycombinator.com/item?id=44268782
OxCamlを作ったJanet Streetの人達のポッドキャスト聞いたよ。
OCamlのパフォーマンス、特に超低遅延が必要な高頻度取引でGC言語使うとGC一時停止が問題になるかもって話。
GC一時停止はHFTの最中だとヤバいかもね。これについて議論してる[0]のポッドキャスト、面白いよ。
[0] https://signalsandthreads.com/performance-engineering-on-har…
GCの問題? 市場が閉まってる時にゴミを集めれば良いじゃん。
超低遅延が必要なHFTは、たいてい市場が開いてる時間に行われるから、市場の開閉に合わせてGCすればOK。
年中無休の市場(cryptoとか?)は違うかもだけどね。
6時間以上もGCしないで放置するのは現実的な解決策なの?
マジで?
GC compactionsは確かにシステムによっては問題だったよ。
取引システムでは起動後はメモリ確保しないのが一般的な方針だった。
JS(Janet Streetのことかな?)には”Zero”っていう、メモリ確保しないやり方を提供してるライブラリがあるんだって。
”Zero”ライブラリ、6秒Google検索したけど見つかんなかったよ。
リンク貼ってくれる?
GC問題? 1分間にどれだけメモリ確保するか分かってて(予算内なら)、十分なRAMを買えば全然大丈夫だよ。
”Zero”ライブラリのことは、[0]のポッドキャストで言ってたよ。
問題はGC言語そのものじゃなく、stackやvalue typesの操作ができないGC言語だよ。
GC言語の生産性がありつつ、低レベルシステムコーディングもできるGC言語?
それならCedar, Oberon language family, Modula-3, D, Nim, Eiffel, C#, F#, Swift, Goなんかがあるよ。
”Zero”ライブラリもJavaScriptも、元の記事やこのスレッドでは全く言及されてないけど?
Go言語ってGCの設定項目(knobs)いっぱいあるの?
数年前はそうじゃなかったけど、最近のは知らないな。
Localityが減るからパフォーマンスが落ちるかも。TLBミスでガタガタになる可能性もあるね。
JSってJane Streetのことかなって思ってる。
何と比べてだよ?ガベージコレクタ(GC)を動かすのと比べて?
設定項目(knobs)はGCじゃなくて、言語機能にあるんだよ。Goコンパイラツールチェーンなら、スタックやグローバルメモリの静的確保、参照エスケープ追跡のコンパイラフラグ、OSバインディング経由の手動確保、unsafeパッケージとスライス、アセンブラ、CGOとか色々手段があるんだから。
これってOCamlに関係ある話?
超低レイテンシ必須のユースケースにGC言語を適用することに興味があったんだ。(高頻度取引みたいな)GCポーズは深刻な問題になりそうだから。GC全般(OCamlに限らず)の実行環境だと、JVMみたいに並列コレクションアルゴリズムでGCポーズを最小限にできる[0]。でもハードな保証はないから、必要な性能出すにはRAMを過剰に積む必要もあるかも。
もう一つ複雑なやり方は、サーバーを多めに用意して、一時的に使用プールから外して「オフラインGC」すること。これはリクエストルーターや他のサーバーとの連携が必要だから、財力があって常にアイドルCPUを用意できるなら、並列GCのためにサーバーを過剰に積んだ方が手っ取り早いかもね。
0 - https://docs.oracle.com/en/java/javase/17/gctuning/parallel-…
リンクは見てないんだけど、取引みたいに市場の開閉時間があるシナリオなら、GCを無効にして市場閉まった後にプログラム再起動するだけでいいんじゃない?
俺も君と同じGC回避のクモに噛まれた感じだよ。でもOCamlは難解すぎて好みじゃないな。
F#の方が柔らかくてモダンな気がする?でもdotnetじゃGCは避けられないと思うけど。
そうそう。
銀行のシステムで、市場が開いてる時間だけ動かすやつではよくあるデザインパターンだよ。
実はRon MinskyにTwitterでこれ exactly な質問したんだ[0]:
俺: 遅延 sensitive なアプリとか、Rustが意味を成す場所で、なんで Rust 使わないんですか?JS は Rust 使ってます?
Minsky: Rust は素晴らしいけど、コードの大半を single language にすることで得られる value は大きい。型、ツール、ライブラリ、idiom を share できるし、folk が project 間を move しやすい。
そして、Rust が bring する most important advantages を、OCaml で cleanly integrated、pay as you go 方式で得る well on our way だ。これは us には better outcome に見えるね。
あと、Rust について don’t love な thing もいくつかある: compile times が long、async/await がどう動くか folk who know more than I do are pretty sad、type discipline は quite complicated など。
でも mostly、より wider-spectrum な single language を at our disposal にしたいということだ。
[0]: https://x.com/arr_ohh_bee/status/1672224986638032897
その「途上」ってのは、interpreter のために ton の C言語 を書かなきゃいけないってことだろ。
critical sections にまだ Rust を使ってないのは、本当に imprudent だと思うね。
.NET の AOT compile はcomically 遅いね。
C++ が OCaml より速く見えるレベルだよ。
あれを serious なことに anyone が使ってるなんて想像もできない。
たぶん?
scale すると Locality は fairly important になる。
だから high-performance code では array-based の data structures が strongly preference されるんだ。
もし俺が彼らだったら、OCaml を使って zero allocation で run できる functional 「kernels」を build up するだろうな。
そして requests をこれらの kernels に dispatch させて、fast modern generational GC に dispatch の minor cost を clean up させるんだ。
most of the work は zero-allocation の kernels で happen する。
> これは俺が OCaml の dialect と呼びたいものだ。
時にはそう speak し、時には zero alloc OCaml と gently 言う。
そして the most notable thing about it は、the garbage collector に touching するのを avoid しようとすることだ …
金融だとC++がまだまだ普通で、JavaとかのGC言語は少ないと思うよ(OCamlはもっと珍しい)。GC言語を使ってる会社もあるみたいだけど、GCオフとかチューニングをどうしてるかはよく分かんないな。
数年前に見たHFTのJVMプロジェクトのGitHubリポジトリ、これらしいよ。外部からHFTのインフラを見る機会はほぼないから、参考になるかもね。https://github.com/OpenHFT
このOxCamlからOCaml本体に最初に取り込まれる機能は「ラベル付きタプル」だってさ。OCaml 5.4に入る予定だよ!
関連するGitHubのプルリクと公式フォーラムのリンクはこれ!
https://github.com/ocaml/ocaml/pull/13498
https://discuss.ocaml.org/t/first-alpha-release-of-ocaml-5-4…
うんうん、これ minor に見えるかもだけど、結構楽しみだよね!
この機能を作った人がML2024で発表した論文とトークの動画もあるよ。
https://www.youtube.com/watch?v=WM7ZVne8eQE
https://tyconmismatch.com/papers/ml2024_labeled_tuples.pdf
匿名でラベル付きの struct とか enum って、俺がプログラミング言語で一番欲しい機能の一つなんだ!
例えば Rust だと、ラベル付きとラベルなし(タプル)の struct は定義できるけど、関数から匿名で返せるのはタプルだけで、ラベル付き struct は無理なんだよね。
fn can() → (i32, i32)
fn cant() → {sum: i32, product: i32}
Dartでは、タプルとレコードを一つのものにまとめたんだ。
レコードは位置指定フィールド、名前付きフィールド、その両方を持てるし、型アノテーションとしてどこでも使えるよ。
DartだとこういうのがOK:
(int, int) can() => (1, 2);
({int sum, int product}) alsoCan() => (sum: 1, product: 2);
(int, {int remainder}) evenThis() => (1, remainder: 2);
型アノテーションの波括弧は名前付きフィールドと位置指定フィールドを分けるため。この構文はあんまり好きじゃないけど、関数パラメータリストと揃えてるんだ。https://dart.dev/language/records
Dartの records の話だけどさ、位置指定フィールドと名前付きフィールドを両方持つタプルと、フィールドとして record を持つタプルって、どう区別するの?
例えば (1, {sum:2}) の型ってどう書くの? (1, sum :2) とは違う?
もっとコメントを表示(1)
構文は歴史的な理由でちょっと変わってるんだよね。
波括弧は record 型の構文の一部としてだけ使うんだ。
型アノテーションだと他に波括弧を使うところが無い(関数型のパラメータリストの中の名前付きパラメータは別だけど)から、そこで紛らわしくなることはないよ。
じゃあ、俺の例だと (1, (sum:2)) と (1, sum:2) って書き方になる、ってことで合ってる?
うん、その通りだよ。
うーん。まず、君が何を気にしてるのか確認させてね。
struct First(this: i8, that: i64)
struct Second(this: i8, that: i8)
struct Third(that: i64, this: i8)
struct Fourth(this: i8, that: i64)
struct Fifth(some: i8, other: i64)
First と Fourth を同じ型にしたいけど、Second と Third は違う、ってこと? Fifth はどう?
これって Rust のプロダクト型とは違う理解だね、Rust だと First と Fourth はいつも違う型になるから。
あとさ、これが欲しい具体的な例をいくつか教えてくれる?正直、俺はこれが欲しいと思ったことないんだけど、それは経験が違うだけかもだしさ。
構造的型付けの変更じゃなく、名前付きフィールドを持つ一時的な匿名型を作って渡したいだけなんだって。タプルのusizeとか意味不明だし、名前付けりゃ便利じゃん。内部実装はタプルと同じでいいと思うけどな。
1への返信。匿名型と構造的型付けって結局同じじゃね?って話。特に複雑なコードだとそう。フィールド順で意味変わるならタプルでいいじゃん。usize問題はRustのドキュメントで解決できるし。
「ランダムなusize」じゃないならNewtype使えよ。RustだとOption<OwnedFd>みたいにCのintと同じマシンコードでも安全に使える。FileSizeとかちゃんと名前付けろって話。そしたら混乱しないじゃん。
面白いことに、最初は名前付きstructだけだった言語も、結局構造的型付けのレコードを取り入れる傾向があるんだって。C#とか最初structだけだったのに、タプルとか名前付きタプルを追加したじゃん。
PHPには全部あるんだって!これって、厳密な型付けか緩い型付けかどっちが好きかって話だよね。両極端はダメだけど。型付けの厳密さはチームとかプロジェクト規模で決めりゃいいと思うな。プロトタイプは緩い方が楽だけど、大規模コードだと厳密な型が役立つじゃん。
常に相反する動機がぶつかり合ってる感じ。
記事の「ラベル付きタプルだと順序間違いを防げる」って話について、「うーん、F#の匿名レコード {| product = 6; sum = 5 |} の方がいいな」だって。これだとタプルじゃないからフィールド順序関係ないし。
それらが同じじゃない理由の一つは、メモリ表現が違うから(ある程度ね)。タプルを勝手に並べ替え可能にしたら、FFIが壊れるぜ。
それってただの普通のOCamlの { product = 6; sum = 5 } と同じじゃないの?(構文がちょっと違うだけじゃね?)
{ … } と {| … |} の違いは、後者(F#の匿名レコード)の型が匿名だってこと。だから、事前に宣言しなくていいんだよ。
ああ、なるほどね。いい指摘じゃん。内部でどう表現されてるんだろ?フィールドはアルファベット順?拡張できる匿名struct(デフォルト付き)も欲しいけど、それってグローバルな分析とか必要なのかな。
Labeled tuples (ラベル付きタプル) は実質的に順序に依存しないんだ。実装順序はインターフェースに合わせなきゃいけないけど、呼び出し側はどんな順序でもlabeled tuplesを破壊できるし、コンパイラが必要な並べ替えをしてくれるよ(recordsを破壊したり、labeled arguments(ラベル付き引数)で関数を呼ぶのと同じ)。これはF#で君が言ってることとそんなに違いはないと思うな。ただlabeled tuplesは1つの値にラベルを付けられないだけ(つまり1-tupleがない、普通のtuplesもそうだけど)。
Immutable arrays (イミュータブル配列) もこのフォークから移植されて、OCaml 5.4に取り込まれたんだよ。たぶん文法は違うと思うけどね。
このフォークがSIMDをサポートしてるなんて知らなかったよ!これとunboxed types、明示的なスタック割り当てがあるlocal modeがあれば、F#への興味がほとんどなくなっちゃったな。Windowsもサポートされれば、ゲーム開発とかコンシューマー向けのシナリオでも実際に使えるようになるかも。
そうなんだよ、これめちゃくちゃいいよね!今は128-bitのSSEとNEONだけが動いてるけど、AVXもすぐ来るよ。Windowsも技術的には何も問題ないんだけど、ちょっと作業が必要なんだ。(SIMDサポートは俺がOxCamlに入れたんだ!)
ちなみに、「Get OxCaml」ページにはARMのSIMDはまだサポートされてないって書いてあるよ。もし実際に動くなら、Known Issuesのリストから消した方がいいかもね。URLはこちら: https://oxcaml.org/get-oxcaml/
確かに、そう書いてあるのは、NEON intrinsicsのライブラリ(ocaml_simd_sseみたいなの)がまだないからなんだ。でも、拡張機能自体は動くよ。
あー、それで分かったよ。ありがとう!全部楽しみにしてるね。(興味ある人向けに、ここにSSEライブラリがあるよ: https://github.com/janestreet/ocaml_simd/tree/with-extension…)
Windowsサポートを追加するのに技術的な障害がないって聞いて嬉しいな!趣味のプロジェクトでOxCamlを試してみようって気持ちになったよ。128-bit SSEで俺のユースケースとターゲットスペックには十分そうだし。
David Allsoppが数ヶ月前にWindowsでコンパイルできるOxCamlのブランチを持ってたから、開発のキューには入ってるよ…
新しいopam switchを試してる人に役立つ情報だよ!環境変数OCAMLPARAM=”alert=-unsafe_multidomain,_,”をつけてopam install cohttp-lwt-unixするといいかも。
alertsがerrorsに昇格されるせいで、既存パッケージのインストールが不必要に壊れるんだよね。OCAMLPARAMの環境変数を使えば、そのalertを無効にできてインストールが進められるよ。
OCamlのちっちゃい版みたいなやつだよ:http://t3x.org/mlite/index.html
Golang向けのvscode(俺の場合はvscodiumだけど)のプラグインが素晴らしすぎて慣れてるんだけど、vscodeの環境との連携って何か予定ある?
セットアップが超簡単になるからさ!
OCamlのvscodeプラグインって、すでにduneとかmenhirとかreasonとか新しいシンタックスにたくさん対応してるみたいだから、OxCamlが広まれば時間の問題じゃないかな。
まあ、俺自身はemacs使ってるから詳しくは言えないんだけどね。
oxcaml.orgにあるインストール手順に従えば、LSPとかに対応したパッチ済みのMerlinが手に入るよ。
完璧じゃないけど、だいたいVSCodeとOCaml Platform extensionでそのまま動くんだ。
VS Codeを再起動したら、エラーの波線表示とかocamlformatが動かなくなっちゃったんだよね、普通のOCamlと比べて。
Dune CLIからはちゃんと動くんだけど。
vscode extensionが使うopam switchを変えてみた?
oxcaml switchに設定する必要があるんだ。「Ocaml: Select a Sandbox for this Workspace」コマンドでできるよ。
もっとコメントを表示(2)
うん、やったよ。opamの扱いは慣れてるし、最初にswitchを変えた時はVS Codeを再起動するまで動いてたんだ。
でもそれ以降は、また動くようにする方法が分からなくなっちゃった。
うーん。OxCaml switchにocaml-lsp-serverはインストールした?
俺はduneをpolling mode(-w)で実行することが多いんだけど、それが関係あるかは分からないな…
参考までに言うと、俺もdebugnikさんと同じ経験したよ。
ocaml-lsp-serverはインストールしたし、polling modeでビルドしたかどうかにかかわらずだった。
へー、OxCamlってMLの方言の拡張のまた拡張なんだ。
次のが出るのが待ちきれないね。
俺も同じこと考えたわ。でもさ、既存言語を機能でどんどん太らせるプログラマと、ただでさえ多いのにまた新しい言語作るプログラマ、どっちがマシなんだろうね?
(俺は後者だけどw)
たぶんプログラマって、ツールをそのままにしておけない遺伝子でも持ってんだろうな。
これ見て、趣味で作ってたプログラミング言語プロジェクトが正直ストップしちゃったよ。機能結構かぶってるし、元々遅かったしね。
とりあえずOxCaml試してみて、自分のと比較してみるつもり。
一番良いのは自分のやめてこれ使うこと、最悪でも何が良くて何がダメか学べるからね。
これってさ、LLMがタダで情報をインデックスできるように出して、公開モデルをファインチューニングするんじゃなくて、自社のコードベースで公開モデル使うため、とかない?
LLMって、OxCamlより学習データがはるかに多い普通のOCamlでさえ結構ダメダメなこと考えたら、たぶん無いんじゃない?
その目的なら、ドキュメントのMCP(機能改善提案)の方がよっぽど建設的だよ。
全然無いね。そんな強いシグナルにはならないよ。
例えばLLMはGleamのコード補完とかマジでクソだよ。
すぐ近くに真似すべきパターンが書いてあるファイル見せたり、よくある間違いを具体的に指示してもダメ。
じゃあこれって、Rustと同じ機能(例えば”恐れを知らない並行処理”とか、GC回避とか)目指してるから”Oxidized”なの?
本当にRust使ってるわけじゃないんだよね?
ちょっと紛らわしいな。
皮肉なことに、プログラミング言語のRustは、酸化鉄じゃなくて「さび」って意味の菌類の名前から来てるんだよね。
その通りだよ。
Jane Streetはずっと「Oxidizing OCaml」っていうタイトルのブログ記事シリーズを公開してるんだ。
「Oxidize」って言葉、前々から使われてたみたいだよ。この取り組みに関する最近の技術論文のタイトルも「Oxidizing OCaml with Modal Memory Management」だったしね。でも、論文の中では「oxidize」自体は一度も言及も定義もされてないんだ。ちょっと変だよね、でもなんか catchy ではあると認めるわ。
Rustは多分、カスタム tracing GC (一般的な graph-like data を扱うのに役立つし、できる限り最高のパフォーマンスを出したいならね) で使えるようになる方が、この取り組みが Rust と genuine feature parity に達するよりずっと早いんじゃない?これに大した意味が見えないんだけど、もしかしたら彼らは lowest-hanging fruit に集中してて、気にかけてる大きな O(x)Caml コードベースがあるのかもしれないね。
OxCaml は Rust とは違って、locarity の encoding に別のアプローチを取ってるんだ。Rust は型システムにこの情報を overburden させがちだけど (arguably ね)、OxCaml ではこれは return types とは orthogonal なんだ。その点では algebraic effects にちょっと似てるよ。個人的には、最近 OCaml にはかなり bullish だね。
> 意味が見えない
OCaml は良い言語だし、既存の OCaml プログラマーやプログラムにはこれらの拡張機能はとても歓迎されるよ。Jane Street が追加した他の多くの拡張機能と同じようにね。ここであなたが何を言いたいのか理解できないんだけど。
OxCaml で見るもの全部、「これ OCaml にあったら nice だろうな」って思うものばっかりなんだよね。
むしろこれ、F# ユーザーにとっては escape opportunity だと思うんだ。言語は大好きなんだけど、runtime と C# ecosystem が、cleaner semantics を持つことから足を引っ張ってるだけでなく (C# team がその価値を認識するまではね)、breaking changes と新しい incompatible APIs で積極的に梯子を蹴り落としてるから。
F# は Fable のおかげで .NET から mostly liberated されてるよ。compiler は strictly .NET だけどね。