「労働者が自分の仕事をうまくやりたいなら、まず自分の道具を研ぎ澄まさなければなりません。」 - 孔子、「論語。陸霊公」
表紙 > プログラミング > ZigとPythonを備えたパフォーマンスと拡張可能なWebサーバー

ZigとPythonを備えたパフォーマンスと拡張可能なWebサーバー

2025-03-22に投稿されました
ブラウズ:305

序文

私はソフトウェア開発への関心、特に可能な限り少ない妥協を行いながら最も広範な問題を解決する人間工学に基づいたソフトウェアシステムを作成するパズルに情熱を注いでいます。また、Andrew Kelleyの定義により、作業中のシステムを完全に理解することに関心のある開発者を意味するシステム開発者と考えたいと思います。このブログでは、次の問題を解決するための私のアイデアを共有しています。かなりの挑戦ですね。ブログでは、「パフォーマンスのWebサーバー」の部分に焦点を当てています。これは、残りが十分に踏まれているか、追加するものがないので、新鮮な視点を提供できると感じています。 大きな警告 -

はコードサンプル

がありません。実際にこれをテストしていません。うん、これは大きな欠陥ですが、実際にこれを実装するには多くの時間がかかりますが、私は持っていません。欠陥のあるブログを公開してまったく公開しない間、私は前者に固執しました。あなたは警告されています。

A performant and extensible Web Server with Zig and Pythonそして、アプリケーションをどのピースから組み立てますか?

快適なフロントエンドですが、最小限の依存関係が必要な場合は、wasm form htmxにzigがあります。
  • Linuxカーネルと密接に統合されたZig Webサーバー。これは、このブログで焦点を当てるパフォーマンスの部分です。
  • zigと統合されたPythonバックエンド。これが複雑な部分です。
  • 一時的で流れやすいなどの耐久性のある実行システムとの統合。これは信頼性を支援し、ブログでは議論されません。
  • 私たちのツールが決定された状態で、始めましょう!

とにかくCoroutinesは過大評価されていますか?

ZigにはCoroutinesの言語レベルのサポートがありません:(コルーチンは、すべてのパフォーマントWebサーバーが構築されているものです。

保持、最初にシステムプログラマーの帽子をかぶってみましょう。コルーチンは銀の弾丸ではなく、何もありません。実際の利点と欠点は何ですか?

Coroutines(ユーザースペーススレッド)がより軽量で高速であることは一般的な知識です。しかし、正確にどのような方法で? (ここでの答えは主に憶測であり、一粒の塩を取り、自分でそれをテストしてください)

デフォルトでは、スタックスペースが少ない(4MBではなく2kb)から始めます。しかし、これは手動で調整できます。

    ユーザースペーススケジューラとの協力をよりよく協力します。カーネルスケジューラは先制であるため、スレッドで実行されるタスクにはタイムスライスが割り当てられます。実際のタスクがスライスに適合しない場合、一部のCPU時間が無駄になります。たとえば、Goroutinesとは対照的に、さまざまなゴルチンによって実行されたマイクロタスクが多く、OS-Threadのスライスにできるだけ同じ時間に適合します。

たとえば、GOランタイムは、OSスレッドにゴルチンをマルチプレックスします。スレッドは、プロセスが所有する他のリソースと同様に、ページテーブルを共有します。ミックスにCPUの分離と親和性を導入すると、スレッドがそれぞれのCPUコアで継続的に実行されると、すべてのOSデータ構造はメモリに留まり、交換する必要はありません。競争は可能ですか?A performant and extensible Web Server with Zig and Python

パフォーマンスの勝利は、スレッドのOSレベルの抽象化を傍観し、それをゴルーチンの抽象化に置き換えることで達成されます。しかし、翻訳では何も失われていませんか?

カーネルと協力できますか?

独立した実行ユニットの「真の」OSレベルの抽象化はスレッドでさえないと主張します - 実際にはOSプロセスです。実際、ここでの区別はそれほど明白ではありません - スレッドとプロセスを区別するのは、異なるPIDとTID値だけです。ファイル記述子、仮想メモリ、信号ハンドラー、追跡されたリソースについては、これらが「クローン」syscallの引数で指定されているかどうか。したがって、「プロセス」という用語を使用して、主にCPU時間、メモリ、オープンファイル記述子を所有する独自のシステムリソースを所有する実行スレッドを意味します。

今、なぜこれが重要なのですか?実行の各ユニットには、システムリソースに対する独自の要求があります。各複雑なタスクはユニットに分解できます。各タスクでは、それぞれがリソースの予測可能なリクエスト - メモリとCPU時間を作成できます。そして、より一般的なタスクに向かって、あなたが行くサブタスクの木をさらに上に上げると、システムリソースグラフは長い尾を持つベル曲線を形成します。そして、テールがシステムリソースの制限をオーバーランしないようにすることはあなたの責任です。しかし、それはどのように行われ、その制限が実際にオーバーランした場合はどうなりますか?A performant and extensible Web Server with Zig and Python

単一のプロセスのモデルと独立したタスクに多くのコルーチンを使用すると、1つのコルーチンがメモリの制限をオーバーランする場合 - メモリ使用量がプロセスレベルで追跡されるため、プロセス全体が殺されます。それは最良のケースです - あなたがcgroupsを使用する場合(これは自動的にポッドあたりのcgroupを持っているKubernetesのポッドの場合です) - cgroup全体が殺されます。信頼できるシステムを作成するには、これを考慮に入れる必要があります。そして、CPU時間はどうですか?当社のサービスが同時に多くの計算集約型のリクエストでヒットした場合、それは反応しなくなります。その後、締め切り、キャンセル、再試行、再起動が続きます。

ほとんどの主流のソフトウェアスタックのこれらのシナリオに対処する唯一の現実的な方法は、システムに「脂肪」を残し、ベルカーブの尾の未使用のリソース - と同時リクエストの数を制限することです。そして、それでも、私たちは時々OOMを殺したり、時々反応したりします。この妥協は多くの人に受け入れられ、実際にソフトウェアシステムに十分にサービスを提供しています。しかし、私たちはもっとうまくやることができますか?

並行性モデル

リソースの使用はプロセスごとに追跡されるため、理想的には、小さな予測可能な実行ユニットごとに新しいプロセスを生成します。次に、CPUの時間と記憶のためにUlimitを設定します - そして、私たちは行ってもいいです! Ulimitにはソフトとハードリミットがあります。これにより、プロセスはソフトリミットを打つと優雅に終了することができます。それが発生しない場合は、おそらくバグが原因で、ハードリミットを打つと強制的に終了します。残念ながら、Linuxで新しいプロセスを生み出すことは遅く、リクエストごとに新しいプロセスを生み出すことは、多くのWebフレームワークやThipporalなどの他のシステムではサポートされていません。さらに、プロセスの切り替えはより高価です - これは牛とCPUのピン留めによって軽減されますが、それでも理想的ではありません。残念ながら、長期にわたるプロセスは避けられない現実です。

短命のプロセスのきれいな抽象化からさらに進むほど、OSレベルの作業が必要になります。しかし、多くの実行スレッド間でIOをバッチするためにIO_IRINGを使用するなど、得られるメリットもあります。実際、大規模なタスクがサブタスクで構成されている場合、私たちは彼らの個々のリソースの利用を本当に気にしますか?プロファイリングのみ。しかし、大きなタスクのために、リソースベルカーブの尾を管理する(切り取る)ことができれば、それは十分に良いでしょう。したがって、同時に処理したい要求と同じくらい多くのプロセスを生み出し、それらを長寿命にし、新しいリクエストごとにulimitを再調整することができます。そのため、リクエストがリソースの制約をオーバーランすると、OS信号が取得され、他のリクエストが認められず、優雅に終了することができます。または、高いリソースの使用が意図的である場合、クライアントに、より高いリソースの割り当てにお金を払うように伝えることができます。私にはかなりいいですね。A performant and extensible Web Server with Zig and Python

しかし、リクエストあたりのコルーチンのアプローチと比較して、パフォーマンスは依然として損なわれます。まず、プロセスメモリテーブルの周りにコピーするのは高価です。テーブルにはメモリページへの参照が含まれているため、巨大なページを利用できるため、コピーするデータのサイズが制限されます。これは、Zigなどの低レベルの言語でのみ直接可能です。さらに、OSレベルのマルチタスクは先制的であり、協調性ではなく、常に効率が低くなります。それとも?

Linuxを使用した協同マルチタスク

Syscall Sched_yieldがあります。これにより、スレッドが作業の一部を完了したときにCPUを放棄することができます。非常に協力的なようです。特定のサイズの時間スライスを要求する方法はありますか?実際、スケジューリングポリシーSched_Deadlineにあります。これはリアルタイムポリシーです。つまり、要求されたCPU時間スライスでは、スレッドが途切れることなく実行されます。しかし、スライスがオーバーランしている場合 - 先制が始まり、スレッドが交換されて剥奪されます。また、スライスがアンダーランの場合 - スレッドはSched_Yieldを呼び出して早期フィニッシュを信号し、他のスレッドを実行できるようにします。それは両方の世界の最高のように見えます - 協同組合と前提条件のモデル。

制限とは、sched_deadlineスレッドがフォークできないという事実です。これにより、2つのモデルが並行しています。リクエストごとのプロセス、それ自体の締め切りを設定し、効率的なIOのためにイベントループを実行するか、それぞれが独自の期限を設定し、互いに通信するためのキューを使用する各マイクロタスクのスレッドを生成するプロセスを実行します。前者はよりシュトラフワードですが、ユーザースペースでイベントループが必要であり、後者はカーネルをより多く使用します。A performant and extensible Web Server with Zig and Python

両方の戦略は、カーネルと協力することにより、Coroutineモデルと同じ終わりを達成します。

埋め込まれたスクリプト言語としてのPython

これは、ジグが輝く場所の高性能で低レベルの低レベルの側面のためのすべてです。しかし、アプリケーションの実際のビジネスに関しては、柔軟性は待ち時間よりもはるかに価値があります。プロセスに本物の人々がドキュメントにサインオフすることを含む場合、コンピューターの遅延は無視できます。また、パフォーマンスに苦しんでいるにもかかわらず、オブジェクト指向の言語は、開発者にビジネスのドメインをモデル化するためのより良いプリミティブを提供します。そして、これの最も遠い端で、FlowableやCamundaなどのシステムにより、管理スタッフと運用スタッフは、より柔軟性とより低い入場障壁でビジネスロジックをプログラムすることができます。 Zigのような言語はこれには役に立たず、あなたの邪魔になるだけです。

Pythonは、一方で、最も動的な言語の1つです。クラス、オブジェクト - それらはすべてフードの下の辞書であり、あなたが好きなように実行時に操作することができます。これにはパフォーマンスのペナルティがありますが、クラスやオブジェクトを使用してビジネスをモデル化し、多くの巧妙なトリックを実用的にします。 Zigはそれの反対です - Zigには意図的に巧妙なトリックはほとんどありません。最大限のコントロールを提供します。彼らの力を相互運用させることで彼らの力を組み合わせることができますか?

実際には、両方ともC ABIをサポートしているためです。 PythonインタープリターをZigプロセス内から実行することができますが、別のプロセスとしてではなく、ランタイムコストと接着コードでオーバーヘッドを削減できます。これにより、Python内のZigのカスタムアロケーターを使用することができます。これは、個々のリクエストを処理するためのアリーナを設定するため、ガベージコレクターのオーバーヘッドを排除しない場合、メモリキャップを設定することができます。大きな制限は、Garbage CollectionとIOのCpythonランタイムスポーニングスレッドですが、それがそうであるという証拠は見つかりませんでした。 AbstractMemoryloopの「コンテキスト」フィールドを使用することにより、パイソンがZigのカスタムイベントループにZigのカスタムイベントループに接続できます。可能性は無限です。

A performant and extensible Web Server with Zig and Python 結論

並行性、並列性、およびOSカーネルとのさまざまな形態の統合のメリットについて説明しました。探索にはベンチマークとコードがありません。これは、提供されるアイデアの品質で補うことを願っています。似たようなことを試しましたか?あなたの考えは何ですか?フィードバック歓迎:)

さらに読む

https://linux.die.net/man/2/clone

https://man7.org/linux/man-pages/man7/sched.7.html

https://man7.org/linux/man-pages/man2/sched_yield.2.html
  • https://rigtorp.se/low-latency-guide/
  • https://eli.thegreenplace.net/2018/measuring-context-switching-and-memory-overheads-for-linux-threads/
  • https://hadar.gr/2017/lightweight-goroutines
リリースステートメント この記事は、https://dev.to/brogrammerjohn/a-performant-and-extensible-web-server with zig-and-python-4adl?1に再現されています。
最新のチュートリアル もっと>

免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。

Copyright© 2022 湘ICP备2022001581号-3