Pythonのバッファプロトコルは、bytes、bytearray、およびarray.arrayといったPythonオブジェクトの、基盤となるメモリバッファへのアクセス方法を定義しています。Pythonのバッファプロトコルを使用すると、高レベルのAPIのみを通さずに、実装オブジェクトの基盤となるメモリに直接アクセスできるため、パフォーマンスが向上します。
バッファプロトコルのドキュメント化について
Nathan Goldbaum氏は、議論になりにくい提案として、ドキュメントの改善や、バッファプロトコルの仕様の断片化を解消することを挙げました。バッファは、「struct」形式の言語をサポートしており、これはPythonのstructフォーマット文字列に似ていますが、より多くの機能を持っています。「フォーマット言語には、詳細が不足している部分があります」。
現在、ユーザーは、PEP 3118やNumPyなどの実装を参照して、完全な文法を理解する必要があります。これには、「レコード、フィールド名、サブアレイ、バイトオーダー、アライメント、および複素数」が含まれます。Pythonのドキュメント全体にすべての機能を記述することは、非常に有益な改善となるでしょう。
バッファプロトコルの利用者を支援するために、Nathan氏は、バッファプロトコルのエクスポート側と利用側のためのHOWTOガイドを作成することを提案しました。これは、Cで実装された検証、クリーンアップ、所有権、およびスレッドの安全な使用に関する「完全な例」が不足しているためです。
安全な並行処理
Nathan氏は、主に講演の今後のセクションに関するフィードバックを求めており、バッファプロトコルの2つの欠点を解決するためのドラフトPEPが作成されています。それは、並行読み書きの調整と、カスタムデータ型です。
最初の問題は、Pythonのバッファプロトコルが「プロトコルレベルでの読み書きの調整をサポートしていない」ことです。ストレージのアドレスは安定していますが、内容は安定していません。バッファプロトコルは、読み取り専用属性をサポートしていますが、この値は単一のビューにのみ適用され、既存のAPIを使用して書き込みアクセスを排他的にすることはできません。
Nathan氏の提案は、読み込みと書き込みの安全な連携を実現するために、新しいバッファ「リース」C APIである`PyBufferLease`と`PyBufferAccessState`を導入するというもので、これはRustの借用メカニズムに似ています。これらのAPIを使用すると、バッファの利用者は、バッファにアクセスする前に、バッファをリースし、アクセスパターンを宣言することができます。以下は、これらの新しいAPIの使用例です。
```
PyBufferLease *lease = PyObject_AcquireBufferLease(obj, PyBUF_CONTIG_RO, Py_BUFFER_ACCESS_SHARED_READ);
if (lease == NULL) {
return -1;
}
const Py_buffer *view = PyBufferLease_GetView(lease);
Py_BEGIN_ALLOW_THREADS
consume_bytes(view->buf, view->len);
Py_END_ALLOW_THREADS
PyBufferLease_Release(lease);
```
リースは、`SHARED_READ`または`EXCLUSIVE_WRITE`モードで開くことができ、`PyObject_AcquireBufferLease()`を呼び出してもブロックすることはありません。代わりに、互換性のないリースが既に存在する場合は、すぐに成功または失敗します。`EXCLUSIVE_WRITE`モードでは、書き込み可能なアクセスが可能になり、重なり合うメモリ領域への他のすべてのアクセスが制限されます。
Nathan氏の提案では、既存の`PyObject_GetBuffer()`、`PyBuffer_Release()`、およびその他の関連インターフェースは変更されません。バッファのエクスポート機能は、新しいアクセスモードを明示的にサポートし、新しいアクセスAPIをサポートしていないエクスポート機能は、既存のAPIをそのまま使用し続けます。利用者は、`PyObject_GetBufferAccessModes()`を使用してエクスポート機能に問い合わせ、エクスポート機能が希望するアクセスモードを提供している場合は、リースを使用します。
この提案と類似した実装がすでに開発中です。Kumar Aditya氏は、NumPyに対する変更を開発しており、これによりPEPとの相互運用が可能になります。ただし、Nathan氏は、NumPyが生のポインタへのアクセスを許可することについて、「興味深い」課題があることを指摘しました。
Nathan氏は、PEPの草案を公開し、フィードバックを求めており、近日中にPEPの議論を開始する予定です。
カスタムデータ型
最後に、ネイサンは、彼とセバスチャン・バーグが作成した、カスタムデータ型を追加するためのBuffer Protocolへの拡張機能に関する提案について説明しました。現在のBuffer Protocolのフォーマットは「固定語彙」であり、カスタムデータ型をサポートするためには、サードパーティはPythonで新しい型とフォーマットコードを導入するか、「バンド外のシグナリングと規約」を使用する必要があります。Python Steering Councilの決定によって新しい型とフォーマットコードをブロックする必要がある代わりに、名前空間ベースのアプローチでカスタムデータ型を使用することができます。
この提案された拡張機能では、カスタムデータ型は角括弧([])で定義され、ドル記号($)がライブラリの名前空間とデータ型を区切ります。この定義をサポートする実装は、既知のカスタムデータ型を安全に解釈し、バッファを解析せずに不明なカスタムデータ型を拒否することができます。
ネイサンは、この提案に関するdiscuss.python.orgでの議論と、ドラフトPEPについて言及しました。
議論
ペトル・ヴィクトリンは、この提案に新しいAPIが必要なのか、または既存のPyObject_GetBuffer() APIを拡張できるのかを尋ねました。ネイサンは、Pythonの安定したABIの一部である既存のAPIは変更できないため、新しいフィールドを格納するのに十分なスペースがないと考えています。「内部フィールドはありますが、エクスポートに使用されると文書化されているため、それを使用することはできません」。 「構造体に情報を隠蔽的に含めることはできません。その情報を保持するために新しい構造体が必要になります。」
トーマス・ワウテルスは、ネイサンの提案がRustと比較してどのように異なるのかを尋ねました。排他的な書き込みアクセスを要求する場合、要求はブロックされるべきか、エラーを発生させるべきか?ネイサンは、エラーを発生させるのが最も簡単な実装であると答えました。デイビッド・ヒューイットは、実装の組み合わせ可能性について、また、「ブロック、非同期ブロック、またはスライスへの書き込みなど、追加の、おそらく望ましい機能」をサポートできるかどうかについて質問しました。
ラリー・ハスティングスは、この新しいAPIが、ユーザーが真面目に対応し、APIを正しく使用していることを前提としているのではないかと疑問に思いました。ネイサンは、APIが誤って使用された場合、メモリの破損が発生する可能性があることを確認しましたが、純粋なPythonを使用しているユーザーがそれを引き起こすことはできず、C拡張機能の作成者だけが引き起こす可能性があると述べました。メモリリークは起こりえないとのことです。
フッド・チャタムは、バッファエクスポート機能が、SHARED_READまたはEXCLUSIVE_WRITEを指定せずに、古いバッファアクセスパターンを使用しているユーザーを拒否することを許可されるのかどうかを尋ねました。もしそうであれば、Rustのバッファをバックストレージとして使用できるようになります。