GCC 앨리어싱 확인/제한 포인터 포함
다음 두 가지 토막글을 생각해 보십시오.
#define ALIGN_BYTES 32
#define ASSUME_ALIGNED(x) x = __builtin_assume_aligned(x, ALIGN_BYTES)
void fn0(const float *restrict a0, const float *restrict a1,
float *restrict b, int n)
{
ASSUME_ALIGNED(a0); ASSUME_ALIGNED(a1); ASSUME_ALIGNED(b);
for (int i = 0; i < n; ++i)
b[i] = a0[i] + a1[i];
}
void fn1(const float *restrict *restrict a, float *restrict b, int n)
{
ASSUME_ALIGNED(a[0]); ASSUME_ALIGNED(a[1]); ASSUME_ALIGNED(b);
for (int i = 0; i < n; ++i)
b[i] = a[0][i] + a[1][i];
}
함수를 컴파일하면 다음과 같이 됩니다.gcc-4.7.2 -Ofast -march=native -std=c99 -ftree-vectorizer-verbose=5 -S test.c -Wall
나는 GCC가 두 번째 함수에 대해 앨리어싱 체크를 삽입한다는 것을 발견했습니다.
이를 방지하려면 어떻게 해야 하나요?fn1
에 대한 것과 같습니다.fn0
? (파라미터의 수가 3개에서 30개로 증가할 때 인수 통과 접근법(fn0
)가 번거로워지고 에일리어싱 체크의 수가 증가합니다.fn1
어프로치가 우스꽝스러워집니다.)
어셈블리(x86-64, AVX 지원 칩); .LFB10의 앨리어싱 크루프트
fn0:
.LFB9:
.cfi_startproc
testl %ecx, %ecx
jle .L1
movl %ecx, %r10d
shrl $3, %r10d
leal 0(,%r10,8), %r9d
testl %r9d, %r9d
je .L8
cmpl $7, %ecx
jbe .L8
xorl %eax, %eax
xorl %r8d, %r8d
.p2align 4,,10
.p2align 3
.L4:
vmovaps (%rsi,%rax), %ymm0
addl $1, %r8d
vaddps (%rdi,%rax), %ymm0, %ymm0
vmovaps %ymm0, (%rdx,%rax)
addq $32, %rax
cmpl %r8d, %r10d
ja .L4
cmpl %r9d, %ecx
je .L1
.L3:
movslq %r9d, %rax
salq $2, %rax
addq %rax, %rdi
addq %rax, %rsi
addq %rax, %rdx
xorl %eax, %eax
.p2align 4,,10
.p2align 3
.L6:
vmovss (%rsi,%rax,4), %xmm0
vaddss (%rdi,%rax,4), %xmm0, %xmm0
vmovss %xmm0, (%rdx,%rax,4)
addq $1, %rax
leal (%r9,%rax), %r8d
cmpl %r8d, %ecx
jg .L6
.L1:
vzeroupper
ret
.L8:
xorl %r9d, %r9d
jmp .L3
.cfi_endproc
.LFE9:
.size fn0, .-fn0
.p2align 4,,15
.globl fn1
.type fn1, @function
fn1:
.LFB10:
.cfi_startproc
testq %rdx, %rdx
movq (%rdi), %r8
movq 8(%rdi), %r9
je .L12
leaq 32(%rsi), %rdi
movq %rdx, %r10
leaq 32(%r8), %r11
shrq $3, %r10
cmpq %rdi, %r8
leaq 0(,%r10,8), %rax
setae %cl
cmpq %r11, %rsi
setae %r11b
orl %r11d, %ecx
cmpq %rdi, %r9
leaq 32(%r9), %r11
setae %dil
cmpq %r11, %rsi
setae %r11b
orl %r11d, %edi
andl %edi, %ecx
cmpq $7, %rdx
seta %dil
testb %dil, %cl
je .L19
testq %rax, %rax
je .L19
xorl %ecx, %ecx
xorl %edi, %edi
.p2align 4,,10
.p2align 3
.L15:
vmovaps (%r9,%rcx), %ymm0
addq $1, %rdi
vaddps (%r8,%rcx), %ymm0, %ymm0
vmovaps %ymm0, (%rsi,%rcx)
addq $32, %rcx
cmpq %rdi, %r10
ja .L15
cmpq %rax, %rdx
je .L12
.p2align 4,,10
.p2align 3
.L20:
vmovss (%r9,%rax,4), %xmm0
vaddss (%r8,%rax,4), %xmm0, %xmm0
vmovss %xmm0, (%rsi,%rax,4)
addq $1, %rax
cmpq %rax, %rdx
ja .L20
.L12:
vzeroupper
ret
.L19:
xorl %eax, %eax
jmp .L20
.cfi_endproc
컴파일러에게 앨리어싱 검사를 중지하도록 지시하는 방법이 있습니다.
줄을 추가하십시오.
#pragma GCC ivdep
벡터화하고 싶은 루프의 바로 앞에, 더 많은 정보가 필요하다면 읽어주시기 바랍니다:
https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Loop-Specific-Pragmas.html
이게 도움이 될까요?
void fn1(const float **restrict a, float *restrict b, int n)
{
const float * restrict a0 = a[0];
const float * restrict a1 = a[1];
ASSUME_ALIGNED(a0); ASSUME_ALIGNED(a1); ASSUME_ALIGNED(b);
for (int i = 0; i < n; ++i)
b[i] = a0[i] + a1[i];
}
편집: 두 번째 시도 :).http://locklessinc.com/articles/vectorize/ 의 정보와 함께
gcc --fast-math ...
그럼 깃발은?
-fno-strict-aliasing
?
제가 이해한 대로 이 체크를 어떻게 끄는지 알고 싶으신가요?그게 전부라면, gcc 명령줄에 대한 이 매개 변수가 당신에게 도움이 될 것입니다.
편집:
귀하의 의견 외에, const type 제한 포인터를 사용하는 것은 금지되어 있지 않습니까?
이는 ISO/IEC 9899 (6.7.3.1 제한의 공식적인 정의)에서 나온 것입니다.
1.
D를 T형에 대한 제한 자격 포인터로서 객체 P를 지정하는 수단을 제공하는 일반 식별자의 선언이라고 가정합니다.
4.
B를 실행할 때마다 L을 P를 기준으로 &L을 갖는 임의의 l 값이라 하자. L을 사용하여 자신이 지정한 개체 X의 값에 접근하고 X도 (어떤 방법으로든) 수정된다면 다음과 같은 요구 사항이 적용됩니다.T는 일정한 자격이 없어야 합니다.X 값에 접근하기 위해 사용되는 다른 모든 l 값은 P를 기준으로 주소를 가져야 합니다.X를 수정하는 모든 접근은 이 하위 절의 목적을 위해 P를 수정하는 것도 고려해야 합니다.블록 B2와 관련된 다른 제한된 포인터 객체 P2를 기반으로 하는 포인터 식 E의 값이 P에 할당된 경우, B2의 실행은 B의 실행 전에 시작되거나 B2의 실행은 할당 전에 종료됩니다.이러한 요구 사항이 충족되지 않으면 동작이 정의되지 않습니다.
그리고 레지스터와 마찬가지로 훨씬 더 흥미로운 점은 다음과 같습니다.
6.
번역자는 제한을 사용할 때 어떤 또는 모든 앨리어싱의 의미를 무시할 수 있습니다.
따라서 gcc가 그렇게 하도록 강제하는 명령 파라미터를 찾을 수 없다면, 표준에서 그렇게 하는 옵션을 줄 필요가 없기 때문에 아마도 불가능할 것입니다.
GCC 4.7로 결과를 기계에 재현할 수 없기 때문에 미리 사과드립니다만, 두 가지 가능한 해결책이 있습니다.
typedef를 사용하여 a를 작성합니다.
* restrict * restrict
적절히.LLVM 컴파일러를 개발하는 전 동료에 따르면, 이것은 다음과 같은 단 하나의 예외입니다.typedef
C의 전처리기처럼 동작하며 원하는 안티 앨리어싱 동작을 허용하기 위해 존재합니다.저는 아래에서 이것을 시도해 보았지만 성공했는지 확신할 수 없습니다.저의 시도를 잘 확인해 주시기 바랍니다.
C99 VLA(Variable Length Array)와 함께 제한 한정자를 사용하는 것에 대한 답변에 설명된 구문을 사용합니다.
저는 아래에서 이것을 시도해 보았지만 성공했는지 확신할 수 없습니다.저의 시도를 잘 확인해 주시기 바랍니다.
여기 제가 실험을 수행할 때 사용한 코드가 있습니다만, 제 제안 중 어느 것이든 원하는 대로 작동하는지 단정적으로 판단하지 못했습니다.
#define ALIGN_BYTES 32
#define ASSUME_ALIGNED(x) x = __builtin_assume_aligned(x, ALIGN_BYTES)
void fn0(const float *restrict a0, const float *restrict a1,
float *restrict b, int n)
{
ASSUME_ALIGNED(a0); ASSUME_ALIGNED(a1); ASSUME_ALIGNED(b);
for (int i = 0; i < n; ++i)
b[i] = a0[i] + a1[i];
}
#if defined(ARRAY_RESTRICT)
void fn1(const float *restrict a[restrict], float * restrict b, int n)
#elif defined(TYPEDEF_SOLUTION)
typedef float * restrict frp;
void fn1(const frp *restrict a, float *restrict b, int n)
#else
void fn1(const float *restrict *restrict a, float *restrict b, int n)
#endif
{
//ASSUME_ALIGNED(a[0]); ASSUME_ALIGNED(a[1]); ASSUME_ALIGNED(b);
for (int i = 0; i < n; ++i)
b[i] = a[0][i] + a[1][i];
}
제한을 사용하여 얻을 수 있는 거의 모든 성능상의 이점에는 다음과 같은 두 가지 사용 패턴 중 하나가 포함됩니다.
지정된 함수 인수에 직접 적용되는 제한 한정자(여기서 가리키는 것과 반대)
이니셜라이저가 있는 명명된 자동 개체에 직접 적용되는 제한 한정자입니다.
이 두 가지 맥락에서 한정자는 명명된 객체의 초기 값을 기반으로 포인터에 의해 액세스되는 저장소를 "보호"하고 이러한 보호의 용어는 객체가 초기화된 시점부터 수명이 끝날 때까지 확장된다는 것이 분명합니다.
제한 한정자가 다른 환경에서 사용되는 경우 의미론이 무엇인지 훨씬 명확하지 않습니다.Standard는 다른 유형이 어떻게 작동해야 하는지 지정하려고 하지만, 적용하려는 컴파일러에 대해서는 알지 못합니다.
예를 들면 다음과 같습니다.
extern int x,y;
int *xx = &x, *yy = &y;
int *restrict *restrict pp;
pp = &xx;
int *q = *pp;
*q = 1;
pp = &yy;
... other code
한다면q
다음에는 사용되지 않습니다.*q=1;
위와 같이 "restrict" 한정자가 켜져 있는 경우*pp
경계를 계속함x
그 후에도pp
그 자체가 다음을 가리키도록 바뀝니다.yy
. 위원회가 그러한 문제를 고려하여 합의에 도달했거나 컴파일러 작성자가 그러한 사례를 의미 있게 처리하려고 시도했다는 증거가 있습니까?
의미있는 처리.restrict
한정자는 이렇게 설정된 "guarded 포인터 값"이 잘 정의된 수명을 가져야 합니다.위에서 설명한 두 가지 이상의 사례를 처리하기 위해서는 상당한 노력이 필요하지만 상대적으로 최소의 혜택을 제공할 수 있습니다.
예제 코드가 선언을 사용하도록 변경된 경우int *restrict q = *pp;
, 다음의 가치는 분명할 것입니다.x
다음의 범위 내에 있다면 "기타 코드"로 보호될 것입니다.q
, 그러나 그것은 컴파일러가 외부 레벨을 인식했는지 여부와 관계없이 사실일 것입니다.restrict
에 대한 예선전pp
. 그렇다면 왜 그런 문제를 해결해야 합니까?
언급URL : https://stackoverflow.com/questions/15613670/gcc-aliasing-checks-w-restrict-pointers
'sourcecode' 카테고리의 다른 글
MySQL - 이전 행에서 값 빼기, 그룹화 기준 (0) | 2023.10.25 |
---|---|
바퀴의 재창조: 노드.JS/이벤트 기반 프로그래밍 대.기능적 프로그래밍? (0) | 2023.10.25 |
WooCommerce 관리자 -> 주문 | 각 상품에 추가 확인란 (0) | 2023.10.25 |
수식 값의 범위를 복사하여 다른 시트의 특정 범위에 붙여넣는 방법은 무엇입니까? (0) | 2023.10.25 |
Angular CLI를 로컬로 설치하는 방법(-g 플래그 없이) (0) | 2023.10.25 |