この記事では、プログラムのパフォーマンスを向上させるために .Net Core で ref と Span8742468051c85b06f0a0af9e3e506b5c を使用する簡単な実装コードを主に紹介します。 ref については、多くの学生はすでによく知っています。Ref は
C#7.0 の言語機能であり、開発者にローカルの 変数参照 を返すメカニズムを提供します。
Span も ref 構文に基づく複雑なデータ型 です。記事の後半では、その使用方法を示す例を示します。 2. Refキーワード
refにしてもoutキーにしても、
におけるポインタの操作など、理解や操作が比較的難しい言語機能ですが、そのような高度な構文とは何でしょうか?これにはいくつかの副作用が伴いますが、これは大したことではないと思います。また、すべての C# 開発者がこれらの内部動作メカニズムを深く理解する必要があるわけではありません。複雑さに関係なく、人々に自由な選択を提供しているだけだと思います。リスクと柔軟性は決して両立しません。 参照とポインターの類似性を説明するために、いくつかの例を見てみましょう。 もちろん、次の使用法は C# 7.0 以前でも使用できます: public static void IncrementByRef(ref int x)
{
x++;
}
public unsafe static void IncrementByPointer(int* x)
{
(*x)++;
}
上記の 2 つの
は、それぞれ ref と非安全なポインターを使用します。パラメータ +1 を完成させます。
int i = 30; IncrementByRef(ref i); // i = 31 unsafe{ IncrementByPointer(&i); } // i = 32
C# 7.0で提供される機能は以下のとおりです:
1.ref locals (ローカル変数参照)int i = 42;
ref var x = ref i;
x = x + 1;
// i = 43
この例では、xの値が次の場合のローカル変数iの参照xです。変更すると、変数 i の値も変更されます。
returns (戻り値参照)
ref returns は、C# 7 の強力な機能です。次のコードは、その機能を最もよく反映しています。この関数は、特定の int
array 参照を返します。 item: public static ref int GetArrayRef(int[] items, int index) => ref items[index];
添字を介して配列内の項目の参照を取得します。参照値が変更されると、それに応じて配列も変更されます。
System.Span は、System.Memory.
dll アセンブリの下にある .Net Core コアの一部です。現在、この機能は独立していますが、将来 CoreFx に統合される可能性があります 使用方法?次の NuGet パッケージは、.Net Core 2.0 SDK によって作成されたプロジェクトの下で参照されます: <ItemGroup>
<PackageReference Include="System.Memory" Version="4.4.0-preview1-25305-02" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0-preview1-25305-02" />
</ItemGroup>
上では、ref キーワードを使用して提供できる単一値
を操作するポインター (T*) のような方法を見てきました。基本的に、.NET システムではポインターの操作は適切なイベントとは見なされません。 もちろん、.NET は単一値の参照を安全に操作するための ref を提供します。しかし、単一の値は、「ポインター」を使用するユーザーのニーズのほんの一部にすぎません。ポインターの場合、より一般的な状況は、連続したメモリ空間で一連の「要素」を操作する場合です。 スパンは、既知の長さとタイプの連続したメモリ ブロックとして表されます。メモリ領域ポインタへの安全なアクセスを提供するという点で、多くの点で T[] または ArraySegment と非常に似ています。実際、これは .NET の操作 (void*) ポインターの抽象化であると私は理解しています。C/C++ に詳しい開発者は、これが何を意味するのかをよく理解する必要があります。
スパン機能は次のとおりです: •配列、アンマネージ ポインタ、スタック ポインタ、固定または固定された管理データ、および値の内部領域への参照を含む、すべての連続メモリ空間の型システムを抽象化します
• CLR 標準のオブジェクト型と値型をサポートします•ジェネリクスをサポートします
•独自に管理および解放する必要があるポインターとは異なり、GC をサポートします 構文的にも意味的にも ref: に関連する Span の定義を見てみましょう。 public struct Span<T> {
ref T _reference;
int _length;
public ref T this[int index] { get {...} }
...
}
public struct ReadOnlySpan<T> {
ref T _reference;
int _length;
public T this[int index] { get {...} }
...
}
次に、直感的な例を使用して Span の使用シナリオを説明します。例として、文字インターセプトと文字変換 (整数への変換) を取り上げます。 "content-length:123", 123 を整数に変換するには、まず Substr
string content = "content-length:123"; Stopwatch watch1 = new Stopwatch(); watch1.Start(); for (int j = 0; j < 100000; j++) { int.Parse(content.Substring(15)); } watch1.Stop(); Console.WriteLine("\tTime Elapsed:\t" + watch1.ElapsedMilliseconds.ToString("N0") + "ms");
string content = "content-length:123"; ReadOnlySpan<char> span = content.ToCharArray(); span.Slice(15).ParseToInt(); watch.Start(); for (int j = 0; j < 100000; j++) { int icb = span.Slice(15).ParseToInt(); } watch.Stop(); Console.WriteLine("\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms");
这里将string转换为int的算法利用ReadonlySpan实现,这也是Span的典型使用场景,官方给的场景也是如些,Span适用于多次复用操作连续内存的场景。
转换代码如下:
public static class ReadonlySpanxtension { public static int ParseToInt(this ReadOnlySpan<char> rspan) { Int16 sign = 1; int num = 0; UInt16 index = 0; if (rspan[0].Equals('-')){ sign = -1; index = 1; } for (int idx = index; idx < rspan.Length; idx++){ char c = rspan[idx]; num = (c - '0') + num * 10; } return num * sign; } }
四、最后
上述两段代码100000次调用的时间如下:
String Substring Convert: Time Elapsed: 18ms ReadOnlySpan Convert: Time Elapsed: 4ms
目前Span的相关支持还够,它只是最基础架构,之后CoreFx会对很多API使用Span进行重构和实现。可见.Net Core的性能日后会越来越强大。
以上が.Net Core でプログラムのパフォーマンスを向上させるために ref と Span