update.はじめにSpanの公開に伴い、ReadOnlySequenceというクラスがnetcoreapp2.1のリリースと同時に公開された。このクラス、役割や使い方が分かり辛かったので、整理をしてみた。不連続な配列のためのデータクラス。不連続な配列というのは、[A][B][C][D][E][F][G][H][I]のように、メモリ的に連続してはいないものの、大きくみて一つの配列とみなせるものという意味。これが導入されたのは、System.IO.Pipelinesのためと言っても良く、内部でがっつり使っている。以下の三つのクラスを主に使うことになる。ReadOnlySequenceSegment<T>ReadOnlySequenceで使用される、単方向リンクを備えたデータクラス。classなので、継承して使用する。SequencePositionシーケンス内部の位置を表す構造体。ReadOnlySequenceSegmentと、その中での位置インデックスを持つ。同一値かどうかを判定はできるものの、相対的にどちらが先に進んでいるかは判定できない。ReadOnlySequence<T>上記二つを利用して、不連続領域を表すための構造体。基本的な使い方ReadOnlySequenceSegmentの自前定義まず、ReadOnlySequenceSegmentを自分で継承する。classだが、abstractなメソッドやプロパティは無いので、メンバーを追加しなくてもコンパイルは通る。次に、定義したReadOnlySequenceSegmentを使用して、データ領域を作成する。seg2;ReadOnlySequenceの作成さて、このようにしてセグメント群を設定したら、それらをつなげてシーケンスを作成する。seg2.Length);ReadOnlySequenceの利用ReadOnlySequenceを作成したら、後はそれを実際に利用する。開始点がSegmentの途中を指していた場合、開始点から切り取られた状態で取得ができる。}特に後処理等は必要ない。場所情報の取得ReadOnlySequence全体で見たときの、N番目の位置情報を取得したい場合は、ReadOnlySequence<T>.GetPosition()を使用する。サイズ128+128+128の三つのセグメントをReadOnlySequenceが抱えている状態で、GetPosition(192)した場合、GetObject()==二番目のセグメント、GetInteger()==64を値に持つSequencePositionが返ってくる。同一かどうかだけが確認可能となっているため注意が必要。ハイパフォーマンスのためにさて、ここまで基本的な使い方を書いてきたが、ここまでだといまいち存在意義がわからない人も多いと思う。使用するだけだと面倒なだけで、それならば一つの配列のみで回した方が楽というのは確かにその通り。ここで、Pipelinesが、このクラスをどのように使用しているかを見れば、そのメリットも見えてくるかもしれない。Streamの処理手順と問題点Streamの処理手順については大体以下のようになる。ここでの問題点は以下のようになる。大体以下のようになる。以上より、やや構造は複雑になっているものの、バッファの再確保とコピーが発生しないバッファポインタを共有していないため、並行処理が得意という利点が発生する。バッファの再確保とコピーが発生しないという点については、かなりの利点と言えるのではないかと思う。ここで問題になるのが、書き込み時点でPipe内ではバッファは細切れになって保持されているため、読出し側に渡す時に使いにくい状態になる。そこで細切れになったバッファをまとめてくれるのがReadOnlySequence<T>というわけなのである。使い回しの指針ReadOnlySequenceとSequencePositionは基本的に使い回さない。この点は、これらが構造体であるということからも間違いはないと思う。よって、使い回すのはReadOnlySequenceSegmentと、そのバッファ領域ということになる。使い回しの実装は、全て自作しても構わないが(実際Pipelinesはそうしている)、ArrayPoolを使えば、だいぶ楽はできると思う。まとめゼロアロケーションとゼロコピーによるパフォーマンス改善を目指してReadOnlySequenceを導入した、というのは、ここ最近のC#の動向としてかなり面白いと思う。ゼロアロケーションとゼロコピーすれば即ち速いかと言われると、そうでもないのがややこしいところ。Streamに関していえば、完全に同期的に使う場合、Pipelinesの方がやることが多いため、StreamがPipelinesに比べて速い場合が多い。比較的大きなデータを非同期で順次処理したい場合、Pipelinesというのはかなり使いでがあるクラスだと思う。この辺りは、無条件にPipelinesに移行するのではなく、用途を確認して計測するべき、という話になってしまうのが辛いところ。ここまで書いておいて何だけど、ストイックな事をしない限り、Pipelinesで使われているデータ構造なんだなー程度の理解で普段はいいと思う。upLoginユーザーは見つかりませんでした。