C符号の性能向上のためのスパンの利用
私の経験では、アプリケーションのパフォーマンスを向上させるために主なことは、IOの呼び出しの数と期間を減らすことです.しかし、このオプションが実行されると、開発者が取る別のパスがスタック上のメモリを使用しています.スタックサイズが非常に小さいので、スタックは非常に高速な割り当てと解放を可能にします.また、スタックを使用すると、GC上の圧力を減らすことができます.スタックでメモリを割り当てるためにvalue types or
第2のオプションは、アンマネージメモリアクセスのAPIが全く冗長であるので、開発者によってめったに使用されません.
ラッピングヘッドの第一歩
読者のほとんどは参照によってメソッド引数を渡すのに慣れ親しんでいますが、現在C CHERHUNEは値自体の代わりに値への参照を返すことができます.
どうやって動くか調べましょう.我々は、伝統的な振舞いと新しいRef Return機能を示す著名な音楽家の配列のまわりで単純なラッパーを作成します.
値型がスタックに割り当てられるかもしれないということを知っているように.また、必ずしも値が使用されるコンテキストによっては必ずしも行われません.値が常にスタックに割り当てられるようにするためにref struct c≧7.0で導入された.
インデックスを見ることができるように
それを保証する
変換するコードを調べましょうLinux permissions を返します.アクセスできますhere
オリジナルコードはこちら
だから我々の最初の目標は、フィールドをマークすることです
結果を計りましょう
我々は、パフォーマンス向上の約10 %である50ナノ秒のアカウントをスピードアップに気付きます.つは、50ナノ秒はそれほどではないと主張することができますが、それはほとんど何も我々を達成するために原価計算!
今、私たちは、この改善を評価するために、それぞれ12文字の18ブロックを持っています.
あなたが見ることができるように、我々は0.5マイクロ秒または5 %のパフォーマンス改善を得ることができました.再び、それはささやかな業績のように見えるかもしれません.しかし、これが本当に絞首刑になる果物であったのを思い出してください.
他のタイプの配列を展開しましょう.例を考えるASP.NET Channels pipeline . 以下のコードの後ろの推論はデータがしばしば複数のバッファに同時に存在するかもしれないことを意味するネットワークの上のチャンクに到着するということです.この例では
バッファが一つのセグメントからなる場合には、
あなたが使うべきかどうか決めるとき、あなたが考慮するかもしれない若干のものは、ここにあります あなたのメソッドがデータの配列を受け入れて、そのサイズを変えないならば.入力を変更しない場合は、 あなたのメソッドが若干の統計を数えるためにストリングを受け入れるならば、あなたが受け入れるべき構文解析を実行するために データが短い配列を返した場合、
stackalloc
オペレータはアンマネージメモリの使用と結合した.第2のオプションは、アンマネージメモリアクセスのAPIが全く冗長であるので、開発者によってめったに使用されません.
Span<T>
異なるソースからのメモリの割り当てのない表現であるC .Span<T>
メモリとタイプの安全性を確保するより便利なファッションで連続的なメモリの領域と開発者が動作することができます.スパン実施
リターン
ラッピングヘッドの第一歩
Span<T>
C言語の更新プログラムに密接に従っていない人のための実装ref returns c≧7.0で紹介した.読者のほとんどは参照によってメソッド引数を渡すのに慣れ親しんでいますが、現在C CHERHUNEは値自体の代わりに値への参照を返すことができます.
どうやって動くか調べましょう.我々は、伝統的な振舞いと新しいRef Return機能を示す著名な音楽家の配列のまわりで単純なラッパーを作成します.
public class ArtistsStore
{
private readonly string[] _artists = new[] { "Amenra", "The Shadow Ring", "Hiroshi Yoshimura" };
public string ReturnSingleArtist()
{
return _artists[1];
}
public ref string ReturnSingleArtistByRef()
{
return ref _artists[1];
}
public string AllAritsts => string.Join(", ", _artists);
}
それでは、それらのメソッドを呼びましょうvar store = new ArtistsStore();
var artist = store.ReturnSingleArtist();
artist = "Henry Cow";
var allArtists = store.AllAritsts; //Amenra, The Shadow Ring, Hiroshi Yoshimura
artist = store.ReturnSingleArtistByRef();
artist = "Frank Zappa";
allArtists = store.AllAritsts; //Amenra, The Shadow Ring, Hiroshi Yoshimura
ref var artistReference = ref store.ReturnSingleArtistByRef();
artistReference = "Valentyn Sylvestrov";
allArtists = store.AllAritsts; //Amenra, Valentyn Sylvestrov, Hiroshi Yoshimura
最初の例と2番目の例のコレクションは、最終的な例では変更されませんが、コレクションの2番目のアーティストを変更することができました.あなたが記事のコースの間に後でこのような便利な機能を参照してくださいとして、我々は、ファッションのようなリファレンスでスタックに配置された配列を操作するのに役立ちます.ref構造体
値型がスタックに割り当てられるかもしれないということを知っているように.また、必ずしも値が使用されるコンテキストによっては必ずしも行われません.値が常にスタックに割り当てられるようにするためにref struct c≧7.0で導入された.
Span<T>
はref struct
したがって、常にスタックに割り当てられていることを確認します.スパン実施
Span<T>
はref struct
メモリへのポインタと、以下のようなスパンの長さが含まれます.public readonly ref struct Span<T>
{
private readonly ref T _pointer;
private readonly int _length;
public ref T this[int index] => ref _pointer + index;
...
}
ノートref
修飾子ポインタフィールドの近くに.このようなコンストラクトはプレーンCでは宣言できません.NETコアを介して実装されてByReference<T>
.インデックスを見ることができるように
ref return
スタック型の構造体に対して参照型の振る舞いを許す.スパン制限
それを保証する
ref struct
常にスタックに使用されます.つまり、それらがボックス化されることができないことを含む多くの制限を持っています.object
, dynamic
または任意のインターフェイスの種類には、参照型のフィールドをすることはできませんし、全体で使用することはできませんawait
and yield
境界.さらに、2つのメソッドを呼び出します.Equals
and GetHashCode
, スローするNotSupportedException
. Span<T>
はref struct
.文字列の代わりにスパンを使う
既存のコードベースの再処理
変換するコードを調べましょうLinux permissions を返します.アクセスできますhere
オリジナルコードはこちら
internal class SymbolicPermission
{
private struct PermissionInfo
{
public int Value { get; set; }
public char Symbol { get; set; }
}
private const int BlockCount = 3;
private const int BlockLength = 3;
private const int MissingPermissionSymbol = '-';
private readonly static Dictionary<int, PermissionInfo> Permissions = new Dictionary<int, PermissionInfo>() {
{0, new PermissionInfo {
Symbol = 'r',
Value = 4
} },
{1, new PermissionInfo {
Symbol = 'w',
Value = 2
}},
{2, new PermissionInfo {
Symbol = 'x',
Value = 1
}} };
private string _value;
private SymbolicPermission(string value)
{
_value = value;
}
public static SymbolicPermission Parse(string input)
{
if (input.Length != BlockCount * BlockLength)
{
throw new ArgumentException("input should be a string 3 blocks of 3 characters each");
}
for (var i = 0; i < input.Length; i++)
{
TestCharForValidity(input, i);
}
return new SymbolicPermission(input);
}
public int GetOctalRepresentation()
{
var res = 0;
for (var i = 0; i < BlockCount; i++)
{
var block = GetBlock(i);
res += ConvertBlockToOctal(block) * (int)Math.Pow(10, BlockCount - i - 1);
}
return res;
}
private static void TestCharForValidity(string input, int position)
{
var index = position % BlockLength;
var expectedPermission = Permissions[index];
var symbolToTest = input[position];
if (symbolToTest != expectedPermission.Symbol && symbolToTest != MissingPermissionSymbol)
{
throw new ArgumentException($"invalid input in position {position}");
}
}
private string GetBlock(int blockNumber)
{
return _value.Substring(blockNumber * BlockLength, BlockLength);
}
private int ConvertBlockToOctal(string block)
{
var res = 0;
foreach (var (index, permission) in Permissions)
{
var actualValue = block[index];
if (actualValue == permission.Symbol)
{
res += permission.Value;
}
}
return res;
}
}
public static class SymbolicUtils
{
public static int SymbolicToOctal(string input)
{
var permission = SymbolicPermission.Parse(input);
return permission.GetOctalRepresentation();
}
}
推論はとても簡単です.string
はchar
, では、なぜヒープの代わりにスタックに割り当てられないのか.だから我々の最初の目標は、フィールドをマークすることです
_value
of SymbolicPermission
ASReadOnlySpan<char>
の代わりにstring
. これを達成するために、我々は宣言しなければならないSymbolicPermission
ASref struct
フィールドまたはプロパティがタイプできないのでSpan<T>
Aのインスタンスでなければref struct
.internal ref struct SymbolicPermission
{
...
private ReadOnlySpan<char> _value;
}
今、我々はすべてを変更するstring
我々の範囲内でReadOnlySpan<char>
. 唯一の関心点はGetBlock
ここから私たちはSubstring
with Slice
.private ReadOnlySpan<char> GetBlock(int blockNumber)
{
return _value.Slice(blockNumber * BlockLength, BlockLength);
}
評価
結果を計りましょう
我々は、パフォーマンス向上の約10 %である50ナノ秒のアカウントをスピードアップに気付きます.つは、50ナノ秒はそれほどではないと主張することができますが、それはほとんど何も我々を達成するために原価計算!
今、私たちは、この改善を評価するために、それぞれ12文字の18ブロックを持っています.
あなたが見ることができるように、我々は0.5マイクロ秒または5 %のパフォーマンス改善を得ることができました.再び、それはささやかな業績のように見えるかもしれません.しかし、これが本当に絞首刑になる果物であったのを思い出してください.
配列の代わりにスパンを使う
他のタイプの配列を展開しましょう.例を考えるASP.NET Channels pipeline . 以下のコードの後ろの推論はデータがしばしば複数のバッファに同時に存在するかもしれないことを意味するネットワークの上のチャンクに到着するということです.この例では
int
.public unsafe static uint GetUInt32(this ReadableBuffer buffer) {
ReadOnlySpan<byte> textSpan;
if (buffer.IsSingleSpan) { // if data in single buffer, it’s easy
textSpan = buffer.First.Span;
}
else if (buffer.Length < 128) { // else, consider temp buffer on stack
var data = stackalloc byte[128];
var destination = new Span<byte>(data, 128);
buffer.CopyTo(destination);
textSpan = destination.Slice(0, buffer.Length);
}
else {
// else pay the cost of allocating an array
textSpan = new ReadOnlySpan<byte>(buffer.ToArray());
}
uint value;
// yet the actual parsing routine is always the same and simple
if (!Utf8Parser.TryParse(textSpan, out value)) {
throw new InvalidOperationException();
}
return value;
}
ここで起こることについて少しそれを壊しましょう.私たちの目標は、バイト数列を解析することですtextSpan
into uint
.if (!Utf8Parser.TryParse(textSpan, out value)) {
throw new InvalidOperationException();
}
return value;
では、入力パラメータをどのように設定するかを見てみましょうtextSpan
. 入力パラメーターは、一連のバイトを読み取ることができるバッファのインスタンスです.ReadableBuffer
はISequence<ReadOnlyMemory<byte>>
これは基本的に複数のメモリセグメントからなることを意味します.バッファが一つのセグメントからなる場合には、
Span
最初のセグメントから.if (buffer.IsSingleSpan) {
textSpan = buffer.First.Span;
}
それ以外の場合は、スタックにデータを割り当て、Span<byte>
それに基づきます.var data = stackalloc byte[128];
var destination = new Span<byte>(data, 128);
次に、メソッドを使用しますbuffer.CopyTo(destination)
バッファの各メモリセグメントを反復してコピー先をコピーするSpan
. その後、我々はスライスSpan
バッファの長さ.textSpan = destination.Slice(0, buffer.Length);
この例では、新しいSpan<T>
APIは、私たちが手動で、その到着前にはるかに便利なファッションでスタックに割り当てられたメモリで動作することができます.結論
Span<T>
提供する安全で使いやすい代替stackalloc
パフォーマンス向上を容易にすることができます.それの各々の使用からの利益が比較的小さい間、それの一貫した使用は千のカットによる死として知られていることを避けることができます.Span<T>
広く使われています.NETコア3.0のコードベースで、perfomance improvement comparing to the previous version .あなたが使うべきかどうか決めるとき、あなたが考慮するかもしれない若干のものは、ここにあります
Span<T>
:ReadOnlySpan<T>
. ReadOnlySpan<char>
. Span<T>
…の助けを得てSpan<T> buf = stackalloc T[size]
. 覚えてT
値型である必要があります.Reference
この問題について(C符号の性能向上のためのスパンの利用), 我々は、より多くの情報をここで見つけました https://dev.to/bohdanstupak1/using-span-t-to-improve-performance-of-c-code-3472テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol