인간은 제한 예선에서 무엇을 만들 수 있습니까?
-
19-09-2019 - |
문제
C99를 얻었다면 restrict
키워드 권리, 포인터 자격을 갖춘 자격을 갖춘 데이터는 aliasing을 통해 컴파일러의 백 뒤에 수정되지 않는다는 약속입니다.
대조적으로, 내가 이해하는 방식 const
예선은 주어진 객체가 코드를 쓰는 코드 뒤에서 수정되지 않는다는 컴파일러 강화 문서입니다. 컴파일러는 부작용으로 힌트를 얻을 수 있지만 프로그래머로서 나는 정말로 신경 쓰지 않습니다.
비슷한 방식으로 restrict
사용자가 통화 기간 동안 독점 액세스 ( "별명을 피하십시오"또는 더 강한 것)를 보장 해야하는 요구 사항으로 기능 프로토 타입의 예선? "문서"로 사용해야합니까?
또한, 사실에 이해할 것이 있습니까? restrict
( const
하다) ?
편집 : 나는 원래 그것을 믿었다 restrict
스레드 코드에 영향을 미칠 수 있지만, 이것은 잘못된 것처럼 보이므로 혼란스러운 독자를 피하기 위해 질문의 스레드에 대한 참조를 제거합니다.
해결책
제한 키워드에 대한 최선의 '직관'은 (컴파일러에 대한 프로그래머가) 포인터의 수명에 따라 해당 포인터를 통해 액세스 된 메모리가 다른 포인터를 통해서만 해당 포인터를 통해서만 액세스 할 것이라는 보증이라는 것입니다. 또는 참조 또는 글로벌 주소. 따라서 포인터가 포인터와 메모리의 속성으로 포인터에있어서 포인터가 범위를 벗어날 때까지 둘을 함께 묶는 것이 중요합니다.
다른 팁
Chris Dodd는 키워드에 대한 올바른 설명을 가지고 있습니다. 특정 플랫폼에서는 성능의 이유로 매우 중요 할 수 있습니다. 컴파일러가 해당 포인터를 통해 데이터를 레지스터에로드 한 후에는 다시 할 필요가 없다는 것을 알리기 때문입니다. 이 보증이 없으면 컴파일러는 매번 포인터를 통해 데이터를 다시로드해야합니다. 로드 하이터 스토어.
const
그리고 restrict
다른 개념이며, 그렇지 않습니다. const
암시합니다 restrict
. 모두 const
말한다 그 기능의 범위 내에서. ㅏ const
포인터는 여전히 별명 일 수 있습니다. 예를 들어 고려하십시오 :
int foo( const int *a, int * b )
{
*b *= 2;
return *a + *b; // induces LHS: *a must be read back immediately
// after write has cleared the store queue
}
직접 글을 쓸 수는 없습니다 a
이 기능에서는 Foo라고 부르는 것이 다음에는 완벽하게 합법적입니다.
int x = 3;
foo( &x, &x ); // returns 12
restrict
다른 보증입니다 : 약속 a != b
모든 전화에서 foo()
.
나는 에 대해 쓰여진 restrict
키워드 및 성능의 길이, 그리고 Mike Acton도 마찬가지입니다. 우리는 특정 인 주문 PowerPC에 대해 이야기하지만 X86에는로드 타격 스토어 문제가 존재하지만 X86의 주문 외의 실행은 스톨이 프로파일에서 분리하기가 더 어려워집니다.
그리고 그냥 강조하기 위해 : 이것은입니다 ~ 아니다 성능에 전혀 관심이 있다면 비전 또는 조기 최적화. restrict
올바르게 사용하면 상당한 속도를 높일 수 있습니다.
당신이 아는 대부분의 것이 잘못되었습니다!
Const ~ 아니다 컴파일러의 뒤에서 무언가가 바뀌지 않도록 보장하십시오. 그 일은 중지뿐입니다 너 글쓰기에서 그 자리까지. 다른 것이 여전히 그 위치에 쓸 수있을 수 있으므로 컴파일러는 일정하다고 가정 할 수 없습니다.
다른 사람들이 말했듯이, 제한 예선은 별명에 관한 것입니다. 실제로 C 표준화의 첫 번째 라운드 동안 "Noalias"키워드에 대한 제안이있었습니다. 불행히도, 제안은 상당히 제대로 쓰여졌다. Dennis Ritchie가 그 과정에서 참여했을 때의 유일한 시간을 자극했다. 그는 "Noalias가 가야한다는 효과에 대해 무언가를 말한 편지를 썼을 때, 이것은 협상에 열려 있지 않다. "
말할 것도없이, '노리아'는 C의 일부가되지 않았다. 그것을 위해, 그 이름은 너무 오염되어 그것을 사용하려고 노력하는 사람도 의심했습니다.
어쨌든, 제한의 주요 의도는이 항목의 별칭이 없을 것이라고 컴파일러에게 알리는 것입니다. 이에 대한 한 가지 이유는 일시적으로 레지스터에 물건을 저장할 수 있기 때문입니다. 예를 들어 다음과 같은 것을 고려하십시오.
void f(int *a, int *b, int *c) {
for (int i=0; i<*a; i++)
*b += c[i];
}
컴파일러는 실제로 I를 레지스터에 넣고 *a를 레지스터에로드하려고하므로 루프의 다른 반복을 실행할지 여부를 결정할 때마다 서로 등록하는 값의 값을 비교합니다. 불행히도,이 기능을 사용한 사람이 완전히 미쳤고 A == B로 불렀다면, 루프 내부에 *b에 쓸 때마다 새로운 값도 *a의 값입니다. - 루프의 반복마다 메모리에서 *A를 읽어야합니다. 만일을 대비하여 그것을 부르는 사람은 완전히 미쳤다. 제한을 사용하면 컴파일러에 A와 B가 항상 뚜렷하다고 가정 할 때 코드를 생성 할 수 있으므로 *A에 쓰는 것은 *B (또는 그 반대)를 절대 변경하지 않습니다.
당신의 이해는 크게 정확합니다. 그만큼 restrict
한정자는 단순히 소위 포인터가 액세스 한 데이터가 뿐 그 정확한 포인터로 액세스합니다. 그것은 쓴대로 읽기 웰에 적용됩니다.
컴파일러는 동시 스레드를 신경 쓰지 않으며 코드를 다르게 생성하지 않으며 원하는대로 자신의 데이터를 깎을 수 있습니다. 그러나 어떤 포인터 작업이 글로벌 메모리를 바꿀 수 있는지 알아야합니다.
Restrict
또한 인간에게 주어진 기능이 비 알리 아스 매개 변수의 가정으로 구현된다는 API 경고를 전달합니다.
컴파일러에 관한 한 사용자의 잠금은 필요하지 않습니다. 그것은 단지 그것을 올바르게 읽었는지 확인하고 싶어합니다. 추정된 코드별로 클로버링해야합니다 컴파일러가 생성되어야했습니다, 아무도없는 경우 restrict
예선. 첨가 restrict
그 우려에서 해방됩니다.
마지막으로, 컴파일러는 더 높은 최적화 수준에서 데이터 유형을 기반으로 가능한 앨리어싱을 이미 분석 할 가능성이 높습니다. restrict
동일한 유형의 데이터에 대한 여러 포인터가있는 기능에 주로 중요합니다. 이 주제로부터 교훈을 얻고 당신이하는 의도적 인 별칭이 union
.
우리는 볼 수있다 restrict
행동 :
void move(int *a, int *b) { void move(int *__restrict a, int *__restrict b) {
a[0] = b[0]; a[0] = b[0];
a[1] = b[0]; a[1] = b[0];
} }
movl (%edx), %eax movl (%edx), %edx
movl %eax, (%ecx) movl %edx, (%eax)
movl (%edx), %eax movl %edx, 4(%eax)
movl %eax, 4(%ecx)
오른쪽 열에서 restrict
, 컴파일러는 다시 읽을 필요가 없었습니다 b[0]
기억으로부터 . 읽을 수있었습니다 b[0]
그리고 등록에 보관하십시오 %edx
, 그리고 레지스터를 메모리에 두 번 저장하십시오. 왼쪽 열에서 매장이 a
변경되었을 수 있습니다 b
.
표준에 더 친숙한 사람은 아마도 더 나은 대답을 할 수 있지만, 나는 그것을 한 번 줄 것입니다.
"컴파일러 뒷면 뒤에서 데이터가 수정되지 않음"은 "휘발성"의 반대와 비슷합니다.
"Const"는 프로그래머 앞에서 데이터가 수정되지 않음을 의미합니다. 즉, 그녀는 "const"로 표시된 서명자를 통해 데이터를 수정할 수 없습니다 ( int const *pi
, 이름 pi
Const는 아니지만 *pi
이다). 데이터는 다른 서명자를 통해 수정 가능할 수 있습니다 (결국 비 초보 데이터는 Const 데이터로서 함수로 전달 될 수 있음).
"제한"이 포인터를 자격으로하는 것이 핵심입니다. 포인터는 C의 별칭 데이터를위한 유일한 방법이므로 두 개의 다른 이름을 통해 일부 데이터에 액세스 할 수있는 유일한 방법입니다. "제한"은 하나의 액세스 경로로 데이터 액세스를 제한하는 것입니다.
이것은 An의 예일 수 있습니다 극도로 좁은 도메인이지만 Altera의 NIOS II 플랫폼은 FPGA 내에서 사용자 정의 할 수있는 소프트 코어 마이크로 컨트롤러입니다. 그런 다음 해당 마이크로의 C 소스 코드 내에서 C-to-Hardware 도구를 사용하여 소프트웨어가 아닌 사용자 정의 하드웨어를 사용하여 내부 루프 속도를 높일 수 있습니다.
거기에서 __restrict__
키워드 (C99와 동일합니다 restrict
) C2H 도구가 포인터 작동의 하드웨어 가속도를 올바르게 최적화 할 수 있도록합니다. 병행하여 순차적으로 대신. 적어도이 경우에 restrict
간단합니다 ~ 아니다 인간 소비를위한 것입니다. 또한보십시오 태양 페이지 on restrict
, 첫 번째 줄이 말하는 곳
사용
restrict
C 프로그램의 적절한 예선은 컴파일러가 상당히 빠른 실행 파이브를 생성 할 수 있습니다.
C2H에서 더 많은 것을 읽는 데 관심이 있다면 이 PDF C2H 결과 최적화에 대해 논의합니다. 섹션 __restrict__
20 페이지에 있습니다.