C #에서 "in"매개 변수 수정자를 사용하는 이유는 무엇입니까?


84

그래서 나는 in매개 변수 수정자가 하는 일을 이해합니다 . 그러나 그것이하는 일은 상당히 중복되는 것처럼 보입니다.

일반적으로 a를 사용하는 유일한 이유 refin. 따라서 in참조로 전달하는 것은 논리적으로 값으로 전달하는 것과 같습니다.

성능상의 이점이 있습니까? 사물의 백엔드 측면에서 ref매개 변수는 최소한 변수의 물리적 주소를 복사해야하며, 이는 일반적인 객체 참조와 동일한 크기 여야한다는 것이 제 생각입니다.

그렇다면 더 큰 구조체에서만 이점이 있습니까, 아니면 다른 곳에서 매력적으로 만드는 몇 가지이면 컴파일러 최적화가 있습니까? 후자의 경우 왜 모든 매개 변수를 지정 하지 않아야 in합니까?


1
예, 성능상의 이점이 있습니다. 구조체 를 복사하는 대신 참조 ref로 전달하는 데 사용됩니다 . 구조체가 수정되지 않아야 함을 의미합니다. in
Panagiotis Kanavos

@dbc 아니, 이것은 interop과 관련이 없습니다
Panagiotis Kanavos

4
값 유형에 대한 성능. C # 7.2의 새로운 "in"키워드
Silvermind

1
여기에 자세한 설명이 있습니다 . 마지막 경고에 유의하십시오.It means that you should never pass a non-readonly struct as in parameter.
Panagiotis Kanavos

나는 이것이 중복이라고 맹세 할 수 있었지만 더 이상 찾을 수 없습니다
JAD

답변:


80

in 최근에 C # 언어에 도입되었습니다.

in실제로 ref readonly. 일반적으로 in도움이 될 수있는 사용 사례는 하나뿐입니다 readonly struct. 많은 큰 s를 처리하는 고성능 앱 입니다.

다음이 있다고 가정합니다.

readonly struct VeryLarge
{
    public readonly long Value1;   
    public readonly long Value2;

    public long Compute() { }
    // etc
}

void Process(in VeryLarge value) { }

이 경우 메서드 VeryLarge에서이 구조체를 Process사용할 때 (예 :를 호출 할 때 value.Compute()) 방어적인 복사본을 만들지 않고 구조체가 참조에 의해 전달되고 컴파일러가 구조체 불변성을 보장합니다.

수정 자 struct와 함께 읽기 전용이 아닌 것을 전달 in하면 컴파일러가 구조체의 메서드를 호출하고 위 메서드의 속성에 액세스 할 때 방어적인 복사본생성하여Process 성능에 부정적인 영향을 미칩니다!

주의 깊게 읽을 것을 권장 하는 정말 좋은 MSDN 블로그 항목 이 있습니다.

in-introducing에 대한 더 많은 역사적 배경을 알고 싶다면 C # 언어의 GitHub 저장소 에서이 토론 을 읽을 수 있습니다.

일반적으로 대부분의 개발자는 도입이 in실수로 보일 수 있다는 데 동의합니다 . 다소 이국적인 언어 기능이며 고성능 에지 경우에만 유용 할 수 있습니다.


그것을 억제 컴파일러가 생성 방어를 복사합니까, 또는 단순히 수동 그렇지 않으면 사용하는 것이 상황에서 방어 복사본을 만드는 프로그래머의 필요성 제거 할 ref수 있도록하여 예를 someProc(in thing.someProperty);대를 propType myProp = thing.someProperty; someProc(ref myProp);? 입니다 in같은 C # - 단지 개념은 out, 또는은 .NET 프레임 워크에 추가되었습니다?
supercat

@supercat,을 사용하여 속성을 전달할 수 없습니다. 는 실제로 특수 속성을 사용 in하기 때문 in입니다 ref. 따라서 첫 번째 스 니펫은 컴파일되지 않습니다. @VisualMelon, 맞습니다. 방어적인 복사는 메서드를 호출하거나 구조체를 인수로 가져 오는 메서드 내에서 구조체의 속성에 액세스 할 때 발생합니다.
dymanoid

@dymanoid 죄송 스팸을 위해, 당신이 쓴 거라고 이해하는 10 개 시도에 대해 걸렸다 (내가 그 네 잘못 생각하지 않습니다!)
VisualMelon

4
@dymanoid : in및 둘 다 out처음부터 프레임 워크에 있었어야하는 개념입니다 (메서드와 속성이 수정 여부를 나타낼 수있는 수단과 함께 this). 컴파일러는 속성을 in임시 보유에 대한 참조를 전달 하여 인수에 전달할 수 있습니다. 다른 언어로 작성된 함수가 해당 임시를 수정하면 의미 체계가 약간 엉망이되지만 서명과 일치하지 않는 함수 동작의 결함입니다.
supercat

1
@supercat, 여기에서 토론을 열지 마십시오 (저는 .NET Framework 개념 팀에 속해 있지 않습니다). 답변에서 참조 된 긴 토론이 있으며 GitHub 토론에서 다른 일부를 참조하는 흥미로운 읽기가 있습니다. BTW, VB.NET에서 속성을 참조로 전달할 수 있습니다. 컴파일러는 이러한 임시 파일을 생성합니다 (물론 모호한 문제로 이어질 수 있음).
dymanoid

41

in참조로 전달하는 것은 논리적으로 값으로 전달하는 것과 같습니다.

옳은.

성능상의 이점이 있습니까?

예.

사물의 백엔드 측면에서 ref매개 변수는 최소한 변수의 물리적 주소를 복사해야하며, 이는 일반적인 객체 참조와 동일한 크기 여야한다는 것이 제 생각입니다.

없다 요구 사항은 그 변수에 개체에 대한 참조와 참조 모두 같은 크기로, 그리고 거기에없는 요구 사항 중 하나는 컴퓨터 워드의 크기는 것을,하지만 네, 실제로는 32에서 32 비트입니다 비트 머신 및 64 비트 머신의 64 비트.

"물리적 주소"가 그것과 관련이 있다고 생각하는 것은 저에게 분명하지 않습니다. Windows 에서는 사용자 모드 코드의 실제 주소 가 아닌 가상 주소 를 사용합니다. 어떤 상황에서 실제 주소가 C # 프로그램에서 의미가 있다고 상상할 수 있는지 궁금합니다.

또한 모든 종류의 참조를 스토리지의 가상 주소로 구현할 필요 도 없습니다 . 참조는 CLI 사양을 준수하는 구현에서 GC 테이블에 대한 불투명 한 핸들 일 수 있습니다.

더 큰 구조체에서만 장점이 있습니까?

더 큰 구조체를 전달하는 비용을 줄이는 것이 기능에 대한 동기 부여 시나리오 입니다.

in어떤 프로그램을 실제로 더 빠르게 만든다는 보장은 없으며 프로그램을 느리게 만들 수 있습니다. 성능에 대한 모든 질문은 경험적 연구를 통해 답해야합니다 . 항상이기는 최적화는 거의 없습니다 . 이것은 "항상 승리"최적화가 아닙니다.

다른 곳에서 매력적으로 만드는 비하인드 씬 컴파일러 최적화가 있습니까?

런타임 컴파일러 및된다 허용 하고 그렇게는 C # 사양의 규칙을 위반하지 않는 경우 그들이 선택하는 어떤 최적화를 할 수 있습니다. 내가 아는 한 in매개 변수에 대한 최적화는 아직 없지만 이것이 향후 이러한 최적화를 배제하지는 않습니다.

모든 매개 변수를 입력하지 않는 이유는 무엇입니까?

글쎄, 당신이 int매개 변수 대신 매개 변수 를 만들었다 고 가정하자 in int. 어떤 비용이 부과됩니까?

  • 이제 호출 사이트 에 값이 아닌 변수 가 필요합니다.
  • 변수를 등록 할 수 없습니다. 지터의 신중하게 조정 된 레지스터 할당 체계에 렌치가 들어 있습니다.
  • 호출 사이트의 코드는 변수에 대한 참조를 가져와 스택에 넣어야하기 때문에 더 큽니다. 반면에 단순히 값을 호출 스택에 푸시 할 수 있기 전에
  • 더 큰 코드는 일부 짧은 점프 명령이 이제 긴 점프 명령이 될 수 있음을 의미하므로 코드가 이제 더 커졌습니다. 이것은 모든 종류의 일에 영향을 미칩니다. 캐시는 더 빨리 채워지고, 지터는 더 많은 작업을 수행하고, 지터는 더 큰 코드 크기에 대해 특정 최적화를 수행하지 않도록 선택할 수 있습니다.
  • 피 호출자 사이트에서 스택 (또는 레지스터)의 값에 대한 액세스를 포인터로의 간접 지정으로 전환했습니다. 이제 포인터가 캐시에있을 가능성이 높지만 여전히 값에 대한 단일 명령어 액세스를 두 명령어 액세스로 전환했습니다.
  • 등등.

a라고 가정 double하고 in double. 다시 말하지만, 이제 변수는 고성능 부동 소수점 레지스터에 등록 될 수 없습니다. 이것은 성능에 영향을 미칠 뿐만 아니라 프로그램 동작을 변경할 수도 있습니다! C #은 64 비트 이상의 정밀도로 부동 산술을 수행 할 수 있으며 일반적으로 부동 소수점을 등록 할 수있는 경우에만 수행합니다.

이것은 무료 최적화가 아닙니다. 대안에 대한 성능을 측정해야합니다. 가장 좋은 방법은 디자인 지침에서 제안하는 것처럼 애초에 큰 구조체를 만들지 않는 것입니다.


"어떤 [...] 가능한 상황에서 실제 주소가 C # 프로그램에서 의미가 있다고 상상할 수 있습니다. [...]." . C #은 일반적으로 메모리 매핑 된 I / O와 리셋 벡터 등과 같은 시스템에서 사용됩니다. 여기서는 평범한 오래된 윈텔 박스에 대해 생각하고 있습니다. x86 프로세서 및 VGA 프레임 버퍼 사용. 좋아요, 보통 우리는 이것들을 직접 조작하지 않지만 우리는 할 수 있습니다.
OmarL

5
전달 in은 모든 경우에 값을 전달하는 것과 실제로 동일합니까? 우리가 가지고 f(ref T x, in T y)있고 f수정 한다면 x, 그것은 y호출 될 때 와 같은 변화를 관찰해야합니다 f(ref a,a). 호출시 수정되는 델리게이트 및 델리게이트를 f취하는 경우에도 동일하게 적용됩니다 . 없이는 의미가 다를 것입니다. 그 값이 변경되지 않았기 때문에 복사본이 될 것이기 때문입니다. in T yyiny
카이

2
나는 OP가 "메모리 위치를 설명하는 비트 패턴"과 같이 비공식적 인 방법으로 "물리적 주소"를 의미한다고 생각한다. "백엔드가 객체를 찾을 위치를 설명하기 위해 사용하는 것을 선택하는 것"과 대조된다.
Sneftel

@Wilson : 당신이 말하는 것에 대한 예를보고 싶습니다. 나는 C #에서 그런 종류의 일을 시도하는 사람을 모른다. 저수준 시스템 구성 요소를 작성하는 데 상위 수준의 관리 언어를 사용할 수있는 "시스템 C #"에 대해 몇 년 동안 이야기가 있었지만 실제로 이러한 코드를 직접 본 적이 없습니다.
Eric Lippert

2
@chi : 맞습니다. 변수 앨리어싱으로 위험한 게임을하면 문제가 발생할 수 있습니다. 그렇게 할 때 아프면 그렇게하지 마십시오. 의 의도in"참조로 전달, 읽기 전용"이라는 개념을 나타 내기위한 것입니다. 이는 현명하게 행동 할 때 값으로 전달하는 것과 논리적으로 동일합니다 .
Eric Lippert

6

있습니다. 를 통과 할 때 structin키워드는 컴파일러는, 포인터를 전달해야하는 최적화 할 수 있습니다 내용을 변경하는 방법의 위험없이합니다. 마지막은 중요합니다. 복사 작업을 방지합니다. 큰 구조체에서 이것은 차이의 세계를 만들 수 있습니다.


2
이것은 질문이 이미 말한 것을 반복하고 실제 질문에 대답하지 않습니다.
Servy

읽기 전용 구조체에서만. 그렇지 않으면 컴파일러는 여전히 방어 복사본이 생성됩니다
파나지오티스 Kanavos

1
사실 아닙니다. 성능상의 이점이 있음을 확인하고 있습니다. IN이 langauge를 통해 실행되는 성능에서 만들어 졌다는 점을 감안할 때 그 이유입니다. (읽기 전용 구조체에서) 최적화를 허용합니다.
TomTom

3
@TomTom 다시, 질문은 이미 다루었습니다 . 그것이 묻는 것은 "그러면 더 큰 구조체에서만 이점이 있는가? 아니면 다른 곳에서 매력적으로 만드는 비하인드 씬 컴파일러 최적화가 있는가? 후자의 경우 모든 매개 변수를 입력하지 않는 이유는 무엇입니까?" 더 큰 구조체에 실제로 유익한 지, 아니면 전혀 유익한 지 묻는 것이 아닙니다. 그것은 작은 구조체에 유익한 묻는 것입니다 (그런 다음 그렇다면 후속 조치). 두 가지 질문에 모두 대답하지 않았습니다.
Servy

2

이것은 함수형 프로그래밍 접근 방식 때문에 수행됩니다. 주요 원칙 중 하나는 함수에 부작용이 없어야한다는 것입니다. 즉, 매개 변수 값을 변경해서는 안되며 일부 값을 반환해야합니다. C #에서는 값 변경을 허용하는 참조로만 복사하지 않고 구조체 (및 값 유형)를 전달할 방법이 없었습니다. 신속하게 메소드가 값을 변경하기 시작하는 한 구조체를 복사하는 해킹 알고리즘이 있습니다 (그들의 컬렉션은 구조체 BTW입니다). swift를 사용하는 사람들은 모두 복사 내용을 알지 못합니다. 이것은 메모리 효율적이고 명시 적이기 때문에 멋진 C # 기능입니다. 새로운 소식을 보면스택의 구조체와 배열에서 점점 더 많은 작업이 수행되는 것을 볼 수 있습니다. 그리고 진술은 이러한 기능에 필요합니다. 다른 답변에 언급 된 제한 사항이 있지만 .net이 어디로 향하고 있는지 이해하는 데 필수적인 것은 아닙니다.


2

에서 그것의 C # 7.2 읽기 전용 기준은

이것은 구조에 대한 참조 만 전달 하는 ref 케이스 와 유사한 함수 스택에 전체 객체를 전달하지 않음을 의미합니다.

그러나 객체의 값을 변경하려고하면 컴파일러 오류가 발생합니다.

그리고 예, 큰 구조를 사용하는 경우 코드 성능을 최적화 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.