GCC가 비지 대기 루프를 최적화하지 않도록 하려면 어떻게 해야 합니까?
Atmel AVR 마이크로컨트롤러용 C코드 펌웨어를 작성하고 싶습니다.GCC의 컴파일러의 「 」, 「 」)를 하게 하고 .-Os
★★★★★★★★★★★★★★★★★」-O2
를 유효하게 하지 않을 이유가 없습니다.또한 수동으로 어셈블리를 쓰는 것보다 훨씬 빠른 속도로 어셈블리를 생성할 수 있습니다.
하지만 최적화되지 않은 작은 코드를 원합니다.기능 실행을 조금 늦추고 싶기 때문에 do-nothing 루프를 작성하여 시간을 낭비하고 싶었습니다.정확할 필요 없이 조금만 기다려 주세요.
/* How to NOT optimize this, while optimizing other code? */
unsigned char i, j;
j = 0;
while(--j) {
i = 0;
while(--i);
}
느리기 에, AVR 를 .i
★★★★★★★★★★★★★★★★★」j
CPU ★★★★★★★★★★★★★★★★★★★★★★★★★★★」
업데이트: AVR Libc에서 util/delay.h 및 util/delay_basic.h를 찾았습니다.대부분의 경우 이러한 기능을 사용하는 것이 더 나을 수 있지만, 이 질문은 여전히 유효하고 흥미롭습니다.
관련 질문:
- C에서 gcc가 일부 스테이트먼트를 최적화하지 않도록 하려면 어떻게 해야 합니까?
- GCC에 특정 코드를 최적화하지 않도록 지시하는 방법이 있습니까?
- 최적화하지 않는 방법 - 어리석음 함수의 메커니즘
dmckee의 답변 링크에 따라 이 답변을 개발했지만, 답변과는 다른 접근 방식을 취합니다.
GCC의 Function Attributes 문서에는 다음 사항이 기재되어 있습니다.
noinline
이 함수 Atribut은 함수를 인라인용으로 고려하지 않도록 합니다.함수에 부작용이 없는 경우, 함수 호출이 라이브인 경우에도 함수 호출이 최적화되는 인라인 이외의 최적화가 있습니다. , 「」를 붙입니다.asm ("");
이것은 나에게 흥미로운 아이디어를 주었다.「 」를 추가하는 에, 「 」를 추가해 주세요.nop
내부 루프의 명령어를 입력하기 위해 빈 어셈블리 코드를 추가해 보았습니다.
unsigned char i, j;
j = 0;
while(--j) {
i = 0;
while(--i)
asm("");
}
리고고 그과효효 !었!!!! 않으며, 의 루프는 .nop
이치노
게다가, 만약 당신이volatile
하고 gcc의 RAM 합니다.ldd
★★★★★★★★★★★★★★★★★」std
임시 레지스터에 복사할 수 있습니다. 이 어프로치에서는, 「」는 사용하지 .volatile
이치노
업데이트: 코드를 컴파일하는 경우-ansi
★★★★★★★★★★★★★★★★★」-std
경우, 을 치환해야 합니다.asm
( 「」)__asm__
를 참조해 주세요.
에도 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 이런 것도 쓸 수 있어요.__asm__ __volatile__("")
조립문을 우리가 배치한 위치에서 실행해야 하는 경우(즉, 최적화로 루프에서 이동해서는 안 됩니다).
i
★★★★★★★★★★★★★★★★★」j
(예: 서 variables변 variables variables variables variables variables variables ) 。volatile
이렇게 하면 컴파일러가 이러한 변수와 관련된 코드를 최적화할 수 없습니다.
unsigned volatile char i, j;
»__asm__
하다: data dependencies: "데이터 "
다음과 같이 합니다.
메인
int main(void) {
unsigned i;
for (i = 0; i < 10; i++) {
__asm__ volatile("" : "+g" (i) : :);
}
}
컴파일 및 분해:
gcc -O3 -ggdb3 -o main.out main.c
gdb -batch -ex 'disas main' main.out
출력:
0x0000000000001040 <+0>: xor %eax,%eax
0x0000000000001042 <+2>: nopw 0x0(%rax,%rax,1)
0x0000000000001048 <+8>: add $0x1,%eax
0x000000000000104b <+11>: cmp $0x9,%eax
0x000000000000104e <+14>: jbe 0x1048 <main+8>
0x0000000000001050 <+16>: xor %eax,%eax
0x0000000000001052 <+18>: retq
은, 변수 「Loop variable」에 있기 합니다.i
에서 제시된 바와 같이 C++에서 스테이트먼트 순서를 적용하여 원하는 루프를 생성합니다.
마크는 ★★★i
인라인 어셈블리의 입력 및 출력으로 사용됩니다.로서 GCC의 을 알 수.i
그래서 최적화가 안 될 것 같아요.
으로 __asm__
불량.c
int main(void) {
unsigned i;
for (i = 0; i < 10; i++) {
__asm__ volatile("");
}
}
루프가 완전히 제거되어 다음과 같이 출력됩니다.
0x0000000000001040 <+0>: xor %eax,%eax
0x0000000000001042 <+2>: retq
, 「 」, 「 」라고 하는 도 주의해 주세요.__asm__("")
★★★★★★★★★★★★★★★★★」__asm__ volatile("")
출력 오퍼랜드가 없기 때문에 다음과 같아야 합니다.asm, asm volatile 및 clobbering 메모리의 차이
다음과 같이 대체하면 무슨 일이 일어나고 있는지 알 수 있습니다.
__asm__ volatile("nop");
그 결과, 다음과 같이 됩니다.
0x0000000000001040 <+0>: nop
0x0000000000001041 <+1>: nop
0x0000000000001042 <+2>: nop
0x0000000000001043 <+3>: nop
0x0000000000001044 <+4>: nop
0x0000000000001045 <+5>: nop
0x0000000000001046 <+6>: nop
0x0000000000001047 <+7>: nop
0x0000000000001048 <+8>: nop
0x0000000000001049 <+9>: nop
0x000000000000104a <+10>: xor %eax,%eax
0x000000000000104c <+12>: retq
즉, GCC가 루프로 전개된 것을 알 수 있습니다.nop
이 경우 루프가 충분히 작았기 때문에 루프가 발생합니다.
빈칸에 하면 빈칸에 의존하게 됩니다.__asm__
트레이드오프를 빈가 항상 됩니다.__asm__ volatile("");
0으로 하다
noinline
루프
컴파일 시에 루프 사이즈를 알 수 없는 경우 풀 롤링은 할 수 없지만, GCC는 여전히 청크로 롤을 풀기로 결정할 수 있기 때문에 지연이 일관되지 않게 됩니다.
Denilson의 답변과 함께 비지 루프 함수는 다음과 같이 기술할 수 있습니다.
void __attribute__ ((noinline)) busy_loop(unsigned max) {
for (unsigned i = 0; i < max; i++) {
__asm__ volatile("" : "+g" (i) : :);
}
}
int main(void) {
busy_loop(10);
}
분해 위치:
Dump of assembler code for function busy_loop:
0x0000000000001140 <+0>: test %edi,%edi
0x0000000000001142 <+2>: je 0x1157 <busy_loop+23>
0x0000000000001144 <+4>: xor %eax,%eax
0x0000000000001146 <+6>: nopw %cs:0x0(%rax,%rax,1)
0x0000000000001150 <+16>: add $0x1,%eax
0x0000000000001153 <+19>: cmp %eax,%edi
0x0000000000001155 <+21>: ja 0x1150 <busy_loop+16>
0x0000000000001157 <+23>: retq
End of assembler dump.
Dump of assembler code for function main:
0x0000000000001040 <+0>: mov $0xa,%edi
0x0000000000001045 <+5>: callq 0x1140 <busy_loop>
0x000000000000104a <+10>: xor %eax,%eax
0x000000000000104c <+12>: retq
End of assembler dump.
★★★★★★★★★★★★★★★★★★.volatile
이 경우 출력 변수가 있기 때문에 어셈블리에 부작용이 있을 수 있음을 표시하기 위해 필요했습니다.
이중 루프 버전은 다음과 같습니다.
void __attribute__ ((noinline)) busy_loop(unsigned max, unsigned max2) {
for (unsigned i = 0; i < max2; i++) {
for (unsigned j = 0; j < max; j++) {
__asm__ volatile ("" : "+g" (i), "+g" (j) : :);
}
}
}
int main(void) {
busy_loop(10, 10);
}
GitHub 업스트림
관련 스레드:
Ubuntu 19.04, GCC 8.3.0에서 테스트 완료.
컴파일러 업그레이드 등에 의해 이 접근법이 완전히 잘못되어 쉽게 고장난다는 것은 왜 아직 언급되지 않았는지 모르겠습니다.대기할 시간 값을 결정하고 원하는 값을 초과할 때까지 현재 시간을 폴링하는 것이 훨씬 더 합리적입니다.에서는 x86을 사용할 수 .rdtsc
위해서라면, 이 좋은 은, 「아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 네.clock_gettime
(POSIX OS).에서는 "x86_64 Linux"의 됩니다.clock_gettime
를 사용합니다.rdtsc
할 수 있는 시스콜을 clock_nanosleep
★★★★★★★★★★★…
avr 버전의 컴파일러가 풀셋을 지원하는지(링크에 있는 흥미로운 것은 모두 gcc 버전 4.4부터) 즉석에서 알 수 없지만 보통 여기서부터 시작합니다.
GCC 4.7.0에서는 빈 ASM이 O3(-O2에서는 시도하지 않음)로 최적화되어 레지스터 또는 휘발성으로 i++를 사용하면 (내 경우) 퍼포먼스가 크게 저하됩니다.
내가 한 것은 컴파일러가 "메인 프로그램"을 컴파일할 때 볼 수 없었던 다른 빈 함수와 링크하는 것이었다.
기본적으로 다음과 같습니다.
이 함수가 선언된 "helper.c"가 생성되었습니다(빈 함수).
void donotoptimize(){}
후 컴파일 되었습니다.gcc helper.c -c -o helper.o
에.
while (...) { donotoptimize();}
연결하면 .gcc my_benchmark.cc helper.o
.
이것은 나에게 최고의 결과를 가져다 주었다(그리고 내 생각에 오버헤드는 전혀 없지만, 프로그램이 없으면 작동하지 않기 때문에 테스트할 수 없다:).
나는 그것이 icc와도 작동해야 한다고 생각한다.링크 최적화를 이노블로 하면 안 될 수도 있지만 gcc를 사용하면 그렇게 됩니다.
휘발성 ASM을 넣으면 도움이 될 거예요.자세한 내용은 이쪽에서 보실 수 있습니다.
http://www.nongnu.org/avr-libc/user-manual/optimization.html
Windows 로 작업하고 있는 경우는, 이하에 자세하게 설명하듯이, 코드를 플러그마로 설정해 볼 수도 있습니다.
이게 도움이 됐으면 좋겠다.
이 루프를 별도의 .c 파일에 저장하고 하나의 파일을 최적화하지 마십시오.게다가 그 루틴을 어셈블러에 기입해, 어느 쪽이든 옵티마이저가 관여하지 않는 C 로부터 호출하는 것이 좋습니다.
가끔 휘발성 있는 일을 하지만 보통 asm 함수를 만듭니다.이 함수는 최적기가 for/while 루프를 조여주는 함수에 대한 호출을 되돌립니다.다만, 더미 함수에 대한 모든 호출을 실시할 필요가 있기 때문에 최적화되지 않습니다.Denilson Sa의 nop 답변도 마찬가지지만 더 빡빡하게...
register 키워드를 사용할 수도 있습니다.레지스터로 선언된 변수는 CPU 레지스터에 저장됩니다.
고객님의 경우:
register unsigned char i, j;
j = 0;
while(--j) {
i = 0;
while(--i);
}
언급URL : https://stackoverflow.com/questions/7083482/how-to-prevent-gcc-from-optimizing-out-a-busy-wait-loop
'IT이야기' 카테고리의 다른 글
라우터 뷰 콘텐츠가 렌더링되지 않음 (0) | 2022.06.04 |
---|---|
null 포인터와 void 포인터의 차이점은 무엇입니까? (0) | 2022.06.04 |
Jest에서 VueJ(Nuxt) 스토어를 테스트하는 방법 (0) | 2022.06.04 |
Vuex Module Getter에 액세스할 수 없음 - 정의되지 않은 속성 'getters'를 읽을 수 없음" (0) | 2022.06.04 |
Linux에서 PATH_MAX는 어디에 정의되어 있습니까? (0) | 2022.06.04 |