이 글에서는 프로그램 성능 향상을 위해 .NetC에서 ref와 Span
을 사용하는 간단한 구현 코드를 주로 소개합니다.1. 서문
사실 ref에 관해서는 많은 학생들이 이미 알고 있는 내용입니다. C# 7.0의 언어 기능입니다. 개발에 사용되는 것은 직원이 로컬 변수 참조
및 값 참조를 반환하는 메커니즘을 제공합니다. Span은 ref 구문을 기반으로 하는 복잡한 데이터 유형
2. Ref 키워드
ref든 out key든 이해하고 조작하기 어려운 언어 기능입니다. C 언어
는 연산 포인터와 동일하기 때문에 이러한 고급 구문은 항상 부작용을 가져오지만 이것이 아무 것도 아니라고 생각하며 모든 C# 개발자가 이에 대해 깊이 이해할 필요는 없습니다. 내부 운영 메커니즘은 아무리 복잡해도 사람들에게 자유 선택만을 제공할 뿐이라고 생각합니다. 위험과 유연성은 항상 양립할 수 없습니다. 참조와 포인터의 유사성을 설명하기 위해 몇 가지 예를 살펴보겠습니다. 물론 C# 7.0 이전에는 다음과 같은 사용 방법을 사용할 수 있습니다.public static void IncrementByRef(ref int x) { x++; } public unsafe static void IncrementByPointer(int* x) { (*x)++; }위의 두 함수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이 예에서는 로컬 i 변수입니다. 참조 x는 x 값이 변경되면 i 변수의 값도 변경됩니다.
2.ref returns(반환 값 참조)
ref return은 C# 7의 강력한 기능입니다. 다음 코드는 가장 효과적인 특성을 반영하여 이 함수는 int에 의해 반환된
배열의 항목에 대한 참조를 제공합니다.public static ref int GetArrayRef(int[] items, int index) => ref items[index];는 참조 값이 있을 때 배열의 항목에 대한 참조를 얻습니다. 변경되면 배열도 그에 따라 변경됩니다.
3. Span
System.Span은 System.Memory의 .Net Core 코어의 일부입니다.dl
l 어셈블리 아래. 이 기능은 현재 독립적이며 향후 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>위에서 단일 값 객체그렇군요. 기본적으로 포인터 작동은 .NET 시스템에서 좋은 이벤트로 간주되지 않습니다. 물론 .NET은 단일 값 참조를 안전하게 작동하기 위한 참조를 제공합니다. 그러나 단일 값은 포인터에 "포인터"를 사용하려는 사용자의 요구 사항 중 일부일 뿐이며, 연속적인 메모리 공간에서 일련의 "요소"를 작동할 때 더 일반적인 상황입니다.
스팬은 길이와 유형이 알려진 연속 메모리 블록으로 표시됩니다. 여러 면에서 메모리 영역 포인터에 대한 안전한 액세스를 제공한다는 점에서 T[] 또는 ArraySegment와 매우 유사합니다. 실제로 이는 .NET의 작업(void*) 포인터를 추상화한 것임을 이해합니다. C/C++에 익숙한 개발자는 이것이 무엇을 의미하는지 더 잘 알아야 합니다.
Span의 특징은 다음과 같습니다.
• 배열, 관리되지 않는 포인터, 스택 포인터, 고정 또는 고정된 관리 데이터 및 값의 내부 영역에 대한 참조
•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의 사용 시나리오를 설명하겠습니다. ; 예를 들어 문자 가로채기 및 문자 변환(정수형으로 변환):
string<code><a href="http://www.php.cn/wiki/57.html" target="_blank">string</a> content = "content-length:123",
string이 있는 경우 content = "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");이 예를 사용하는 것은 일반적인 하위 문자열 사용 시나리오입니다. 문자열이 연산될 때마다 새로운 문자열 객체가 생성됩니다. 물론, 수행할 때 문자열 객체가 반복적으로 연산되는 것은 아닙니다. int.Parse. 많은 작업을 수행하면 GC 압력이 손상됩니다. Span을 사용하여 이 알고리즘을 구현합니다.
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