중요합니다
이 섹션에 설명된 기술은 코드의 핫 경로에 적용할 때 성능을 향상 시킵니다 . 핫 경로는 일반 작업에서 자주 반복적으로 실행되는 코드베이스의 해당 섹션입니다. 자주 실행되지 않는 코드에 이러한 기술을 적용하면 영향을 최소화할 수 있습니다. 성능을 개선하기 위해 변경하기 전에 기준을 측정하는 것이 중요합니다. 그런 다음, 해당 기준을 분석하여 메모리 병목 현상이 발생하는 위치를 확인합니다. 진단 및 계측 섹션에서 애플리케이션의 성능을 측정하는 여러 플랫폼 간 도구에 대해 알아볼 수 있습니다. Visual Studio 설명서에서 메모리 사용량을 측정 하기 위해 자습서에서 프로파일링 세션을 연습할 수 있습니다.
메모리 사용량을 측정하고 할당을 줄일 수 있다고 결정한 후에는 이 섹션의 기술을 사용하여 할당을 줄입니다. 각 연속 변경 후 메모리 사용량을 다시 측정합니다. 각 변경 내용이 애플리케이션의 메모리 사용량에 긍정적인 영향을 주는지 확인합니다.
.NET의 성능 작업은 코드에서 할당을 제거하는 것을 의미하는 경우가 많습니다. 할당하는 모든 메모리 블록은 결국 해제되어야 합니다. 할당을 줄이면 가비지 수집에 소요되는 시간이 줄어듭니다. 이를 통해 특정 코드 경로에서 가비지 수집을 제거하여 실행 시간을 더 예측할 수 있습니다.
할당을 줄이기 위한 일반적인 전략은 class
유형에서 struct
유형으로 중요한 데이터 구조를 변경하는 것입니다. 이 변경 내용은 이러한 형식을 사용하는 의미 체계에 영향을 줍니다. 이제 매개 변수와 반환이 참조가 아닌 값으로 전달됩니다. 값 복사 비용은 형식이 작거나 3단어 이하인 경우 무시할 수 있습니다(한 단어가 정수의 자연 크기인 경우 고려). 측정 가능하며 더 큰 유형에 실제 성능에 영향을 미칠 수 있습니다. 복사의 영향을 방지하기 위해 개발자는 이러한 형식을 ref
로 전달하여 의도한 의미 체계를 복구할 수 있습니다.
C# ref
기능을 사용하면 전반적인 유용성에 부정적인 영향을 주지 않고 형식에 대해 struct
원하는 의미 체계를 표현할 수 있습니다. 이러한 향상된 기능 이전에 개발자는 동일한 성능 영향을 얻기 위해 unsafe
포인터 및 원시 메모리가 있는 구문에 의존해야 했습니다. 컴파일러는 새로운 관련 기능에 대해 ref
생성합니다.
확인 가능한 안전한 코드 는 컴파일러가 가능한 버퍼 오버런을 감지하거나 할당되지 않거나 해제된 메모리에 액세스한다는 것을 의미합니다. 컴파일러는 일부 오류를 검색하고 방지합니다.
참조로 전달 및 반환
C#의 변수는 값을 저장합니다. 형식에서 struct
값은 형식 인스턴스의 내용입니다. 형식에서 class
값은 형식의 인스턴스를 저장하는 메모리 블록에 대한 참조입니다.
ref
수식자를 추가하면, 변수가 값에 대한 참조를 저장합니다. 형식에서 struct
참조는 값이 포함된 스토리지를 가리킵니다. 형식에서 class
참조는 메모리 블록에 대한 참조를 포함하는 스토리지를 가리킵니다.
C#에서 메서드에 대한 매개 변수는 값으로 전달되고 반환 값은 값으로 반환됩니다. 인수의 값 이 메서드에 전달됩니다. 반환 인수의 값 은 반환 값입니다.
ref
, in
, ref readonly
또는 out
한정자는 인수가 참조로 전달됨을 나타냅니다. 스토리지 위치에 대한 참조 가 메서드에 전달됩니다. 메서드 시그니처에 추가 ref
하면 반환 값이 참조로 반환됩니다. 스토리지 위치에 대한 참조 는 반환 값입니다.
ref 할당을 사용하여 변수가 다른 변수를 참조하도록 할 수도 있습니다. 일반적인 할당은 오른쪽 값을 할당의 왼쪽에 있는 변수에 복사합니다.
ref 할당은 오른쪽에 있는 변수의 메모리 위치를 왼쪽의 변수에 복사합니다.
ref
이제 원래 변수를 참조합니다.
int anInteger = 42; // assignment.
ref int location = ref anInteger; // ref assignment.
ref int sameLocation = ref location; // ref assignment
Console.WriteLine(location); // output: 42
sameLocation = 19; // assignment
Console.WriteLine(anInteger); // output: 19
변수를 할당 하면 해당 값이 변경 됩니다. ref에서 변수를 할당 하면 변수가 참조하는 내용을 변경합니다.
ref
값을 저장소에 직접 접근하여 작업할 수 있으며, 변수의 참조 전달과 참조 할당을 사용할 수 있습니다. 컴파일러에 의해 적용되는 범위 규칙은 스토리지로 직접 작업할 때 안전을 보장합니다.
ref readonly
및 in
한정자는 모두 인수를 참조로 전달해야 하며 메서드에서 다시 할당할 수 없음을 나타냅니다. 차이점은 메서드가 ref readonly
매개 변수를 변수로 사용한다는 것입니다. 메서드는 매개 변수를 캡처하거나 읽기 전용 참조로 매개 변수를 반환할 수 있습니다. 이러한 경우 한정자를 ref readonly
사용해야 합니다. 그렇지 않으면 in
수정자가 더 많은 유연성을 제공합니다.
in
수정자는 in
매개 변수에 대한 인수에 추가할 필요가 없으므로 in
수정자를 사용하여 기존 API 서명을 안전하게 업데이트할 수 있습니다. 인수에 ref
또는 in
한정자를 추가하지 않으면 ref readonly
매개 변수에 대해 컴파일러가 경고를 발생시킵니다.
참조 안전한 컨텍스트
C#에는 참조하는 스토리지가 ref
더 이상 유효하지 않은 식에 액세스할 수 없도록 하는 식 규칙이 ref
포함되어 있습니다. 다음 예제를 고려하세요.
public ref int CantEscape()
{
int index = 42;
return ref index; // Error: index's ref safe context is the body of CantEscape
}
컴파일러는 메서드에서 지역 변수에 대한 참조를 반환할 수 없으므로 오류를 보고합니다. 호출자가 참조되는 스토리지에 액세스할 수 없습니다.
ref safe 컨텍스트는 ref
표현식에 액세스하거나 수정하는 것이 안전한 범위를 정의합니다. 다음 표에서는 변수 형식에 대한 ref safe 컨텍스트를 나열합니다 .
ref
필드는 a class
또는 비 ref struct
에서 선언할 수 없으므로 해당 행은 테이블에 없습니다.
선언 | ref 안전한 문맥 |
---|---|
비참조 로컬 | 지역 변수가 선언된 블록 |
비-ref 매개 변수 | 현재의 메서드 |
ref , ref readonly , in 매개변수 |
메서드 호출 |
out 매개 변수 |
현재의 메서드 |
class 필드 |
메서드 호출 |
비-ref struct 필드 |
현재의 메서드 |
ref 의 필드 ref struct |
메서드 호출 |
ref
가 호출 메서드인 경우 변수는 반환될 수 있습니다.
ref safe 컨텍스트가 현재 메서드 또는 블록인 ref
경우 반환이 허용되지 않습니다. 다음 코드 조각은 두 가지 예를 보여 줍니다. 메서드를 호출하는 범위에서 멤버 필드에 액세스할 수 있으므로 클래스 또는 구조체 필드의 ref safe 컨텍스트 가 호출 메서드입니다.
또는 ref
한정자가 있는 매개 변수의 in
는 전체 메서드입니다. 둘 다 멤버 메서드에서 ref
반환될 수 있습니다.
private int anIndex;
public ref int RetrieveIndexRef()
{
return ref anIndex;
}
public ref int RefMin(ref int left, ref int right)
{
if (left < right)
return ref left;
else
return ref right;
}
비고
매개변수에 ref readonly
또는 in
한정자를 적용하면 해당 매개변수를 ref readonly
로 반환할 수 있으며, ref
로는 반환할 수 없습니다.
컴파일러는 참조가 참조 안전 컨텍스트를 이스케이프할 수 없도록 합니다.
ref
매개 변수, ref return
및 ref
지역 변수를 안전하게 사용할 수 있습니다. 컴파일러가 스토리지가 유효하지 않을 때 ref
표현식에 액세스할 수 있는 코드가 실수로 작성되었는지를 감지하기 때문입니다.
안전한 컨텍스트 및 참조 구조체
ref struct
형식은 안전하게 사용할 수 있도록 더 많은 규칙이 필요합니다.
ref struct
형식은 ref
필드를 포함할 수 있습니다. 이를 위해서는 안전한 컨텍스트를 도입해야 합니다. 대부분의 형식에서 안전한 컨텍스트 는 호출 메서드입니다. 즉, ref struct
이 아닌 값은 항상 메서드에서 반환될 수 있습니다.
비공식적으로, 안전한 컨텍스트란 모든 ref struct
필드에 액세스할 수 있는 범위를 뜻합니다. 즉, 모든 필드의 참조 안전 컨텍스트의 교집합입니다. ref
다음 메서드는 멤버 필드로 ReadOnlySpan<char>
를 반환하므로, 이 메서드가 안전한 컨텍스트를 제공합니다.
private string longMessage = "This is a long message";
public ReadOnlySpan<char> Safe()
{
var span = longMessage.AsSpan();
return span;
}
반면, 다음 코드는 멤버가 스택 할당 정수 배열을 참조하기 때문에 ref field
Span<int>
오류를 내보낸다. 메서드에서 탈출할 수 없습니다.
public Span<int> M()
{
int length = 3;
Span<int> numbers = stackalloc int[length];
for (var i = 0; i < length; i++)
{
numbers[i] = i;
}
return numbers; // Error! numbers can't escape this method.
}
메모리 형식 통합
System.Span<T> 및 System.Memory<T>의 도입은 메모리 작업을 위한 통합 모델을 제공합니다.
System.ReadOnlySpan<T>와 System.ReadOnlyMemory<T>은 메모리에 접근하기 위한 읽기 전용 버전을 제공합니다. 이들은 모두 비슷한 요소의 배열을 저장하는 메모리 블록에 대한 추상화 정보를 제공합니다. 차이점은 Span<T>
와 ReadOnlySpan<T>
는 ref struct
형식인 반면, Memory<T>
와 ReadOnlyMemory<T>
는 struct
형식이라는 것입니다. 범위에는 ref field
가 포함됩니다. 따라서 범위의 인스턴스는 안전한 컨텍스트를 벗어날 수 없습니다.
안전한 컨텍스트는 해당 ref struct
의 참조 안전 컨텍스트ref field
입니다.
Memory<T>
및 ReadOnlyMemory<T>
의 구현은 이 제한을 제거합니다. 이러한 형식을 사용하여 메모리 버퍼에 직접 액세스합니다.
ref 안전성 기능을 활용하여 성능을 향상시키세요.
이러한 기능을 사용하여 성능을 향상시키려면 다음 작업이 포함됩니다.
-
할당 방지: 형식을 a에서 a
class
struct
로 변경하면 형식이 저장되는 방식을 변경합니다. 로컬 변수는 스택에 저장됩니다. 컨테이너 개체가 할당되면 멤버가 인라인으로 저장됩니다. 이렇게 변경하면 할당이 줄어들고 가비지 수집기가 수행하는 작업이 줄어듭니다. 가비지 수집기가 덜 자주 실행되도록 메모리 압력을 줄일 수도 있습니다. -
참조 의미 체계 유지: 형식을 a
class
에서 메서드로struct
변경하면 변수를 메서드에 전달하는 의미 체계가 변경됩니다. 매개 변수의 상태를 수정한 코드는 수정이 필요합니다. 이제 매개 변수가 되었struct
으므로 메서드는 원래 개체의 복사본을 수정합니다. 해당 매개 변수를ref
매개 변수로 전달하여 원래 의미 체계를 복원할 수 있습니다. 이 변경 후 메서드는 원래struct
항목을 다시 수정합니다. -
데이터 복사 방지: 더 큰
struct
형식을 복사하면 일부 코드 경로의 성능에 영향을 줄 수 있습니다. 값 대신 참조를 통해 메서드에 더 큰 데이터 구조를 전달하는 한정자를 추가할ref
수도 있습니다. -
수정 제한: 형식이 참조로
struct
전달되면 호출된 메서드가 구조체의 상태를 수정할 수 있습니다.ref
수정자를ref readonly
또는in
수정자로 바꿔 인수가 수정될 수 없음을 나타낼 수 있습니다. 메서드가 매개 변수를 캡처하거나 읽기 전용 참조로 반환하는 경우를 선호ref readonly
합니다.readonly struct
형식 또는struct
형식을readonly
멤버와 함께 생성하여struct
의 어떤 멤버를 수정할 수 있는지를 보다 세부적으로 제어할 수 있습니다. -
직접 메모리 조작: 일부 알고리즘은 데이터 구조를 요소 시퀀스를 포함하는 메모리 블록으로 처리할 때 가장 효율적입니다.
Span
및Memory
형식은 메모리 블록에 안전하게 액세스할 수 있도록 합니다.
이러한 기술 중 어느 것도 코드가 필요하지 않습니다 unsafe
. 현명하게 사용하면 이전에는 안전하지 않은 기술을 사용해야만 가능했던 안전 코드에서 성능 특성을 얻을 수 있습니다.
메모리 할당을 줄이는 방법에 대한 자습서에서 직접 기술을 사용해 볼 수 있습니다.
.NET