Nice programing

GCC가 바쁜 대기 루프를 최적화하지 못하게하는 방법은 무엇입니까?

nicepro 2020. 12. 4. 20:37
반응형

GCC가 바쁜 대기 루프를 최적화하지 못하게하는 방법은 무엇입니까?


Atmel AVR 마이크로 컨트롤러 용 C 코드 펌웨어를 작성하고 싶습니다. GCC를 사용하여 컴파일하겠습니다. 또한 컴파일러 최적화 ( -Os또는 -O2) 를 활성화하고 싶습니다. 활성화 하지 않을 이유가 없으며 어셈블리를 수동으로 작성하는 것보다 더 빠른 어셈블리 방식을 생성 할 것입니다.

하지만 최적화되지 않은 작은 코드를 원합니다. 함수 실행을 일정 시간 지연시키고 싶기 때문에 시간을 낭비하기 위해 아무것도하지 않는 루프를 작성하고 싶었습니다. 정확할 필요는 없습니다. 잠시만 기다리십시오.

/* How to NOT optimize this, while optimizing other code? */
unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i);
}

AVR에서 메모리 액세스가 많이 느린이기 때문에, 내가 원하는 ijCPU 레지스터에 보관 할 수 있습니다.


업데이트 : 방금 AVR Libc 에서 util / delay.hutil / delay_basic.h찾았습니다 . 대부분의 경우 이러한 기능을 사용하는 것이 더 나은 아이디어 일 수 있지만이 질문은 여전히 ​​유효하고 흥미 롭습니다.


관련 질문 :


나는 dmckee의 답변 에서 링크를 따라이 답변을 개발 했지만 답변 과는 다른 접근 방식을 취합니다.

GCC에서 언급 한 기능 속성 문서 :

noinline이 함수 속성은 함수가 인라인으로 간주되지 않도록합니다. 함수에 부작용이없는 경우 함수 호출이 라이브이지만 함수 호출이 최적화되도록하는 인라인 이외의 최적화가 있습니다. 이러한 통화가 최적화되지 않도록하려면asm ("");

이것은 나에게 흥미로운 아이디어를 주었다 ... nop내부 루프에 명령 을 추가하는 대신 다음과 같이 빈 어셈블리 코드를 추가해 보았습니다.

unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i)
        asm("");
}

그리고 작동했습니다! 해당 루프는 최적화되지 않았으며 추가 nop명령이 삽입 되지 않았습니다 .

당신이 사용하는 경우 무엇보다, volatile, GCC는 RAM에서 그 변수를 저장하고의 무리를 추가합니다 lddstd임시 레지스터에 복사 할 수 있습니다. 반면에이 접근 방식은 volatile이러한 오버 헤드를 사용하지 않으며 생성 하지도 않습니다 .


업데이트 : 당신이 사용하여 코드를 컴파일하는 경우 -ansi또는 -std, 당신은 대체해야 asm와 키워드 __asm__로, GCC 설명서에 설명 .

또한 어셈블리 문이 우리가 넣은 곳에서 실행해야하는__asm__ __volatile__("") 경우 (즉, 최적화로 루프 밖으로 이동해서는 안 됨) 사용할 수도 있습니다 .


선언 ij변수로 volatile. 이렇게하면 컴파일러가 이러한 변수를 포함하는 코드를 최적화하지 못합니다.

unsigned volatile char i, j;

이 접근 방식이 컴파일러 업그레이드 등으로 인해 완전히 오도되고 쉽게 깨지는 이유가 아직 언급되지 않은 이유를 잘 모르겠습니다. 기다릴 시간 값을 결정하고 전류를 폴링하는 것이 훨씬 더 합리적입니다. 원하는 값이 초과 될 때까지의 시간. x86 rdtsc에서는이 목적으로 사용할 수 있지만 더 이식 가능한 방법은 clock_gettime시간을 확보하기 위해 호출 (또는 비 POSIX OS의 변형) 을 호출 하는 것입니다. 현재 x86_64 Linux는 syscall을 피하고 내부적으로 clock_gettime사용 rdtsc합니다. 또는 시스템 호출 비용을 처리 할 수있는 경우 다음 clock_nanosleep으로 시작하십시오.


컴파일러의 avr 버전이 s전체 세트#pragma (링크의 흥미로운 것들은 모두 gcc 버전 4.4에서 시작됨)를 지원하는지 내 머리 꼭대기에서 알 수 없지만 일반적으로 시작하는 곳입니다.


나를 위해 GCC 4.7.0에서 빈 asm은 어쨌든 -O3로 최적화되었습니다 (-O2로 시도하지 않았습니다). 레지스터 또는 휘발성에서 i ++를 사용하면 성능이 크게 저하되었습니다 (제 경우).

내가 한 것은 "주 프로그램"을 컴파일 할 때 컴파일러가 볼 수 없었던 다른 빈 함수와 연결하는 것이었다.

기본적으로 이것은 :

이 함수를 선언하여 "helper.c"를 생성했습니다 (빈 함수).

void donotoptimize(){}

그런 다음 "gcc helper.c -c -o helper.o"를 컴파일 한 다음

while (...) { donotoptimize();}

이것은 나에게 최고의 결과를 주었다 (그리고 내 생각으로는 오버 헤드가 전혀 없지만 내 프로그램이 그것 없이는 작동하지 않기 때문에 테스트 할 수 없습니다 :))

I think it should work with icc too. Maybe not if you enable linking optimizations, but with gcc it does.


put that loop in a separate .c file and do not optimize that one file. Even better write that routine in assembler and call it from C, either way the optimizer wont get involved.

I sometimes do the volatile thing but normally create an asm function that simply returns put a call to that function the optimizer will make the for/while loop tight but it wont optimize it out because it has to make all the calls to the dummy function. The nop answer from Denilson Sá does the same thing but even tighter...


Putting volatile asm should help. You can read more on this here:-

http://www.nongnu.org/avr-libc/user-manual/optimization.html

If you are working on Windows, you can even try putting the code under pragmas, as explained in detail below:-

https://www.securecoding.cert.org/confluence/display/cplusplus/MSC06-CPP.+Be+aware+of+compiler+optimization+when+dealing+with+sensitive+data

Hope this helps.


You can also use the register keyword. Variables declared with register are stored in CPU registers.

In your case:

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

반응형