C99 'restrict' 키워드를 실제로 사용하시겠습니까?
몇 가지 문서와 질문/답변을 훑어보던 중 언급되어 있는 것을 보았습니다.저는 포인터를 다른 곳을 가리키는 데 사용하지 않겠다는 프로그래머의 약속이라는 간단한 설명을 읽었습니다.
이것을 실제로 사용할 가치가 있는 현실적인 사례를 제안할 수 있는 사람이 있습니까?
restrict
기본 오브젝트에 액세스하는 것은 포인터뿐이라고 합니다.포인터 에일리어스의 가능성을 배제하고 컴파일러에 의한 최적화를 개선합니다.
예를 들어 메모리 내에 숫자의 벡터를 곱할 수 있는 특수한 명령어를 가진 기계가 있으며, 다음과 같은 코드를 가지고 있다고 가정합니다.
void MultiplyArrays(int* dest, int* src1, int* src2, int n)
{
for(int i = 0; i < n; i++)
{
dest[i] = src1[i]*src2[i];
}
}
의 경우에 .dest
,src1
, , , , 입니다.src2
overlap즉한 에 한 가지다restrict
컴파일러는 벡터 명령을 사용하여 이 코드를 자유롭게 최적화할 수 있습니다.
에 대한 항목이 있습니다.restrict
, 여기에 다른 예를 제시하겠습니다.
위키피디아의 예는 매우 밝다.
하나의 어셈블리 명령을 저장할 수 있는 방법을 명확하게 보여줍니다.
제한 없음:
void f(int *a, int *b, int *x) {
*a += *x;
*b += *x;
}
유사 어셈블리:
load R1 ← *x ; Load the value of x pointer
load R2 ← *a ; Load the value of a pointer
add R2 += R1 ; Perform Addition
set R2 → *a ; Update the value of a pointer
; Similarly for b, note that x is loaded twice,
; because x may point to a (a aliased by x) thus
; the value of x will change when the value of a
; changes.
load R1 ← *x
load R2 ← *b
add R2 += R1
set R2 → *b
제한 있음:
void fr(int *restrict a, int *restrict b, int *restrict x);
유사 어셈블리:
load R1 ← *x
load R2 ← *a
add R2 += R1
set R2 → *a
; Note that x is not reloaded,
; because the compiler knows it is unchanged
; "load R1 ← *x" is no longer needed.
load R2 ← *b
add R2 += R1
set R2 → *b
GCC가 정말 그렇게 하나요?
GCC 4.8 Linux x86-64:
gcc -g -std=c99 -O0 -c main.c
objdump -S main.o
★★★★★★★★★★★★★★★★ -O0
똑같아요.
★★★★★★★★★★★★★★★★ -O3
:
void f(int *a, int *b, int *x) {
*a += *x;
0: 8b 02 mov (%rdx),%eax
2: 01 07 add %eax,(%rdi)
*b += *x;
4: 8b 02 mov (%rdx),%eax
6: 01 06 add %eax,(%rsi)
void fr(int *restrict a, int *restrict b, int *restrict x) {
*a += *x;
10: 8b 02 mov (%rdx),%eax
12: 01 07 add %eax,(%rdi)
*b += *x;
14: 01 06 add %eax,(%rsi)
미개시의 경우, 발신자 규약은 다음과 같습니다.
rdi
= parameter = 첫 번째 파라미터rsi
= parameter = 세컨드 파라미터rdx
= 번째 파라미터 = 세 번째 파라미터
GCC의 출력은 Wiki 기사보다 더 명확했다: 4개의 명령과 3개의 명령.
어레이
지금까지는 단일 명령어를 절약할 수 있었지만 포인터가 루프오버할 어레이를 나타내는 일반적인 사용 사례인 경우 슈퍼캣에서 언급한 것처럼 여러 명령을 저장할 수 있습니다.
예를 들어 다음과 같습니다.
void f(char *restrict p1, char *restrict p2) {
for (int i = 0; i < 50; i++) {
p1[i] = 4;
p2[i] = 9;
}
}
에 에.restrict
스마트 컴파일러(또는 인간)는 다음을 위해 이를 최적화할 수 있습니다.
memset(p1, 4, 50);
memset(p2, 9, 50);
적절한 libc 구현(glibc 등)에서 어셈블리가 최적화되어 있기 때문에 훨씬 효율적일 수 있습니다.퍼포먼스에 관해서는 std::memcpy() 또는 std::copy() 중 어느 쪽을 사용하는 것이 좋습니까?
GCC가 정말 그렇게 하나요?
GCC 5.2.1Linux x86-64 Ubuntu 15.10:
gcc -g -std=c99 -O0 -c main.c
objdump -dr main.o
★★★★★★★★★★★★★★★★ -O0
다 다 똑같아요.
★★★★★★★★★★★★★★★★ -O3
:
제한 있음:
3f0: 48 85 d2 test %rdx,%rdx 3f3: 74 33 je 428 <fr+0x38> 3f5: 55 push %rbp 3f6: 53 push %rbx 3f7: 48 89 f5 mov %rsi,%rbp 3fa: be 04 00 00 00 mov $0x4,%esi 3ff: 48 89 d3 mov %rdx,%rbx 402: 48 83 ec 08 sub $0x8,%rsp 406: e8 00 00 00 00 callq 40b <fr+0x1b> 407: R_X86_64_PC32 memset-0x4 40b: 48 83 c4 08 add $0x8,%rsp 40f: 48 89 da mov %rbx,%rdx 412: 48 89 ef mov %rbp,%rdi 415: 5b pop %rbx 416: 5d pop %rbp 417: be 09 00 00 00 mov $0x9,%esi 41c: e9 00 00 00 00 jmpq 421 <fr+0x31> 41d: R_X86_64_PC32 memset-0x4 421: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 428: f3 c3 repz retq
memset
이치노제한 없음: stdlib 콜은 없습니다.16회 반복 와이드 루프 언롤링뿐입니다.여기서 재현할 생각은 없습니다:-)
벤치마킹할 만한 인내심은 없었지만, 제한 버전이 더 빠를 것 같습니다.
C99
완성도를 높이기 위해 기준을 살펴보자.
restrict
는, 2개의 포인터가 중복하는 메모리 영역을 가리킬 수 없다고 합니다.가장 일반적인 용도는 함수 인수입니다.
이는 함수를 호출하는 방법을 제한하지만 더 많은 컴파일 시간 최적화를 허용합니다.
발신자가 에 따르지 않는 경우restrict
계약, 정의되지 않은 동작
C99 N1256 드래프트 6.7.3/7 "유형 적격자"에는 다음과 같이 기술되어 있습니다.
제한 한정자(레지스터 스토리지 클래스 등)의 목적은 최적화를 촉진하는 것이며, 적합 프로그램을 구성하는 모든 전처리 변환 단위에서 한정자의 모든 인스턴스를 삭제해도 그 의미는 변경되지 않습니다(즉, 관찰 가능한 동작).
6.7.3.1 "제한의 공식 정의"는 뼈아픈 세부사항을 제공한다.
엄밀한 에일리어스 규칙
그restrict
키워드는 호환되는 유형의 포인터에만 영향을 줍니다(예: 2개).int*
엄밀한 에일리어스 규칙에서는 비호환 타입의 에일리어스는 디폴트로 정의되지 않은 동작으로 되어 있기 때문에 컴파일러는 이 동작이 발생하지 않는다고 가정하고 최적화할 수 있습니다.
「 」를 참조해 주세요.
- C++14의 아날로그는 아직 없습니다.
restrict
단, GCC에는__restrict__
내선번호:C++에서 restrict 키워드는 무엇을 의미합니까? - 많은 질문을 던진다: 이 코드는 UB인가 아닌가?
- "사용 시기" 질문:제한을 사용할 때와 사용하지 않을 때
- 관련 GCC
__attribute__((malloc))
함수의 반환값에는 에일리어스가 없음을 나타냅니다.GCC : _ _ _ attribute _ _ ( malloc )
언급URL : https://stackoverflow.com/questions/745870/realistic-usage-of-the-c99-restrict-keyword
'programing' 카테고리의 다른 글
Vue + Webpack 스테이징/프리프로덕션 구성 (0) | 2022.08.13 |
---|---|
Vue 유닛 테스트:소품, vuex 스토어, 워처, 게터 등을 사용하여 복잡한 컴포넌트를 테스트하는 방법 (0) | 2022.08.13 |
#include in .h or .c / .cpp? (0) | 2022.08.11 |
Laravel : Vue js가 로드되기 전에 보간 코드를 숨기려면 어떻게 해야 합니까? (0) | 2022.08.11 |
Kotlin-Android: 확인되지 않은 참조 데이터 바인딩 (0) | 2022.08.11 |