실제로 개발 및 디버깅 단계에서 최적화를 비활성화하는 것이 좋은 방법입니까?


15

C에서 프로그래밍 16 비트 PIC 마이크로 컨트롤러를 읽었 으며이 책에 다음과 같은 확인이 있습니다.

그러나 프로젝트의 개발 및 디버깅 단계에서는 분석중인 코드의 구조를 수정하고 단일 스테핑 및 중단 점 배치에 문제가 발생할 수 있으므로 모든 최적화를 비활성화하는 것이 좋습니다.

나는 약간 혼란스러워 고백했다. 저자가 C30 평가 기간 때문에 또는 실제로 모범 사례 라고 말했는지 이해하지 못했습니다 .

이 연습을 실제로 사용하는지, 왜 그런지 알고 싶습니다.

답변:


16

이것은 소프트웨어 엔지니어링에서 전체적으로 표준입니다. 코드를 최적화 할 때 컴파일러는 연산의 차이점을 알 수없는 한 원하는대로 정렬 할 수 있습니다. 예를 들어 루프의 모든 반복 내에서 변수를 초기화하고 루프 내에서 변수를 변경하지 않으면 최적화 프로그램이 해당 초기화를 루프 밖으로 이동하여 시간을 낭비하지 않도록 할 수 있습니다.

또한 덮어 쓰기 전에 아무 것도하지 않는 숫자를 계산할 수도 있습니다. 이 경우 쓸모없는 계산이 제거 될 수 있습니다.

최적화 문제는 옵티마이 저가 이동하거나 제거한 일부 코드에 중단 점을 두어야한다는 것입니다. 이 경우 디버거는 원하는 작업을 수행 할 수 없습니다 (일반적으로 중단 점을 어딘가에 배치합니다). 따라서 생성 된 코드를 작성한 내용과 더 유사하게 만들려면 디버그 중에 최적화를 해제하십시오. 이렇게하면 중단하려는 코드가 실제로 존재합니다.

그러나 코드에 따라 최적화로 인해 문제가 발생할 수 있으므로이 점에주의해야합니다. 일반적으로 올바르게 작동하는 옵티 마이저에 의해 손상되는 코드는 실제로 무언가 버그가있는 버그 코드 일 뿐이므로 일반적으로 옵티마이 저가이를 깨뜨리는 이유를 파악하려고합니다.


5
이 주장에 대한 반론은 옵티마이 저가 일을 더 작거나 더 빠르게 만들 수 있다는 것입니다. 타이밍이나 크기가 제한된 코드가있는 경우 최적화를 비활성화하여 무언가를 깨뜨릴 수 있으며 문제가되는 문제를 디버깅하는 데 시간을 낭비 할 수 있습니다 실제로 존재하지 않습니다. 물론 디버거를 사용하면 코드가 느리고 커질 수 있습니다.
Kevin Vermeer

이것에 대해 더 배울 수있는 곳은 어디입니까?
Daniel Grillo

나는 C30 컴파일러와 함께 일하지 않았지만 C18 컴파일러의 경우 지원되는 최적화를 다루는 컴파일러에 대한 앱 노트 / 매뉴얼이있었습니다.
Mark

@O Engenheiro : 컴파일러 문서에서 지원하는 최적화에 대해 확인하십시오. 최적화는 컴파일러, 라이브러리 및 대상 아키텍처에 따라 크게 다릅니다.
Michael Kohne

다시, C30 컴파일러는 아니지만 gcc는 적용될 수있는 다양한 최적화의 LONG 목록을 게시합니다. 또한 특정 제어 구조를 그대로 유지하려는 경우이 목록을 사용하여 세분화 된 최적화를 얻을 수 있습니다. 목록은 여기에 있습니다 : gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
Kevin Vermeer

7

이 질문을 Jack Ganssle에게 보냈는데 이것이 그가 대답 한 것입니다.

다니엘,

릴리스 된 코드에서 최적화를 사용하여 디버깅하는 것을 선호합니다. NASA는 "무엇을 시험 해보고, 무엇을 시험해 보느냐"라고 말합니다. 다시 말해, 테스트를하지 말고 코드를 변경하십시오!

그러나 때로는 디버거가 작동하도록 최적화를 해제해야합니다. 작업중 인 모듈에서만 끄려고합니다. 이런 이유로 파일을 작게 유지한다고 생각합니다. 몇 백 줄 정도의 코드가 필요합니다.

최고야, 잭


이 답변에서 두 단락 사이에 명시되지 않은 차이가 있다고 생각합니다. 테스트는 소프트, 회사 및 / 또는 하드웨어가 올바르게 작동 함을 보여주기위한 절차를 말합니다. 디버깅은 코드가 명령별로 단계별로 진행되어 왜 작동하지 않는지 확인하는 프로세스입니다 [아직].
케빈 베르메르

선택하는 것이 좋습니다. 따라서 테스트는 최적화 유무에 관계없이 더 많은 품종 / 순열을 포함 할 수 있습니다. 더 적용 범위, 더 나은 테스트하고있다

@reemrevnivek, 디버깅 할 때 테스트하지 않습니까?
Daniel Grillo

@O Engenheiro-아니요. 테스트에 실패한 경우에만 디버깅합니다.
케빈 베르메르

6

의존하며, 이는 일반적으로 C30뿐만 아니라 모든 도구에 적용됩니다.

최적화는 종종 다양한 방법으로 코드를 제거 및 / 또는 재구성합니다. switch 문은 if / else 구문으로 다시 구현되거나 경우에 따라 함께 제거 될 수 있습니다. y = x * 16은 일련의 왼쪽 시프트 등으로 대체 될 수 있습니다. 비록이 마지막 최적화 유형은 일반적으로 여전히 단계적으로 진행될 수 있습니다.

C에서 정의한 구조가 더 이상 존재하지 않고 컴파일러에서 컴파일러가 더 빠르거나 공간을 덜 사용한다고 생각하는 것으로 대체되거나 다시 정렬되기 때문에 C 코드를 통해 디버거를 단계적으로 실행할 수 없습니다. 또한 중단 된 명령어가 더 이상 존재하지 않을 수 있으므로 C 목록에서 중단 점을 설정하는 것이 불가능할 수도 있습니다. 예를 들어 if 문 안에 중단 점을 설정하려고 할 수 있지만 컴파일러는 if 문을 제거했을 수 있습니다. while 또는 for 루프 내에서 중단 점을 설정하려고 시도 할 수 있지만 컴파일러는 해당 루프를 더 이상 존재하지 않도록 풀기를 결정했습니다.

이러한 이유로 최적화를 해제 한 상태에서 디버깅 할 수 있으면 일반적으로 더 쉽습니다. 항상 최적화를 설정 한 상태에서 다시 테스트해야합니다. 이것은 당신이 중요한 것을 놓치고 volatile간헐적 실패 (또는 다른 이상한)를 일으키는 것을 알 수있는 유일한 방법 입니다.

임베디드 개발의 경우 어쨌든 최적화에주의해야합니다. 특히 타이밍이 중요한 코드 섹션에서 일부 인터럽트가 발생합니다. 이 경우 어셈블리에서 중요한 비트를 코딩하거나 컴파일러 지시문을 사용하여 이러한 섹션이 최적화되지 않았는지 확인하여 실행 시간이 고정되어 있거나 최악의 경우 런타임이 고정되어 있는지 확인해야합니다.

다른 하나는 코드를 uC에 넣을 수 있으므로 코드에 칩을 맞추기 위해 코드 밀도 최적화가 필요할 수 있습니다. 이것이 일반적으로 제품군에서 가장 큰 ROM 용량 uC로 시작하고 코드가 잠긴 후에 제조를 위해 더 작은 것을 선택하는 것이 좋은 이유입니다.


5

일반적으로 릴리스하려는 설정으로 디버깅합니다. 최적화 된 코드를 출시하려고한다면 최적화 된 코드로 디버깅합니다. 최적화되지 않은 코드를 릴리스하려는 경우 최적화되지 않은 코드로 디버깅합니다. 나는 두 가지 이유로 이것을한다. 첫째, 옵티마이 저는 최종 제품이 최적화되지 않은 코드와 다르게 동작하도록 충분히 타이밍 차이를 만들 수 있습니다. 둘째, 대부분의 경우는 좋지만 컴파일러 공급 업체는 실수를하고 최적화 된 코드는 최적화되지 않은 코드와 다른 결과를 생성 할 수 있습니다. 결과적으로 릴리스하려는 모든 설정으로 가능한 한 많은 테스트 시간을 얻고 싶습니다.

즉, 옵티마이 저는 이전 답변에서 언급했듯이 디버깅을 어렵게 만들 수 있습니다. 디버깅하기 어려운 특정 코드 섹션을 찾으면 옵티 마이저를 일시적으로 끄고 디버깅을 수행하여 코드가 작동하도록 한 다음 옵티 마이저를 다시 켜서 한 번 더 테스트합니다.


1
그러나 최적화를 켜면 코드를 한 단계 씩 실행하는 것이 거의 불가능합니다. 최적화를 끄고 디버그하고 릴리스 코드를 사용하여 단위 테스트를 실행하십시오.
Rocketmagnet

3

내 일반적인 전략은 최종 최적화 (최대 크기 또는 속도에 대한 최대)로 개발하는 것이지만 추적 추적을 디버그 해야하는 경우 최적화를 일시적으로 해제하십시오. 최적화 수준을 변경하면 버그가 떠오를 위험이 줄어 듭니다.

일반적인 실패 모드는 최적화를 늘리면 필요한 경우 변수를 휘발성으로 선언하지 않았기 때문에 이전에 보지 못한 버그가 발생하는 경우입니다. 이는 컴파일러에게 '최적화'해서는 안되는 것을 알려주는 데 필수적입니다.


2

디버거 및 디버깅을 위해 릴리스 할 형식을 사용하여 릴리스 컴파일 할 때까지 볼 수없는 많은 버그를 숨 깁니다. 그때까지는 디버깅 할 때보 다 버그를 찾기가 훨씬 어렵습니다. 20 년 전 지금은 gdb 또는 다른 디버거와 같은 용도를 사용하지 않았으므로 변수 나 단일 단계를 볼 필요가 없습니다. 하루에 수백 줄이 있습니다. 따라서 가능합니다. 다르게 생각하지 마십시오.

디버그를위한 컴파일과 나중에 릴리즈를위한 컴파일은 두 배에서 두 배 이상의 노력이 필요합니다. 바인드에 들어가서 디버거와 같은 도구를 사용해야하는 경우 디버거가 특정 문제를 해결하도록 컴파일 한 다음 다시 정상 작동으로 되 돌리십시오.

옵티마이 저가 코드를 더 빠르게 만들어 특히 컴파일러 옵션으로 타이밍을 변경하고 프로그램의 기능에 영향을 줄 수있는 다른 문제도 마찬가지입니다. 여기에서 전체 단계에서 제공 가능한 컴파일 선택을 다시 사용하십시오. 컴파일러도 프로그램이며 버그가 있으며 최적화 프로그램은 실수를 저지르고 일부는 그에 대한 믿음을 갖지 못합니다. 그렇다면 최적화없이 컴파일하는 데 아무런 문제가 없다면 항상 그렇게하십시오. 내가 선호하는 경로는 최적화를 위해 컴파일하는 것입니다. 컴파일러 문제로 인해 최적화를 비활성화하면 의심되는 경우 일반적으로 앞뒤로 때때로 어셈블러 출력을 검사하여 이유를 알아냅니다.


1
+1은 좋은 대답을 정교하게하기위한 것입니다. 종종 "디버그"모드에서 컴파일하면 할당되지 않은 공간이있는 변수 주위의 스택 / 힙을 채워서 작은 쓰기-쓰기 및 형식 문자열 오류를 완화합니다. 릴리스에서 컴파일하면 더 자주 런타임 충돌이 발생합니다.
Morten Jensen

2

나는 항상 -O0 (최적화를 끄는 gcc 옵션)으로 코드를 개발합니다. 내가 더 많은 것을 릴리스하기 위해 시작하고 싶다고 생각할 때, 일반적으로 캐시에 더 많은 코드를 보관할수록 더 나은 코드로 -Os (크기 최적화)로 시작합니다. 슈퍼 듀퍼 최적화되지 않은 경우에도 마찬가지입니다.

나는 gdb가 -O0 코드로 훨씬 잘 작동한다는 것을 알았으며 어셈블리를 시작 해야하는 경우 훨씬 쉽게 따라 할 수 있습니다. -O0과 -Os 간을 전환하면 컴파일러가 코드에서 수행하는 작업을 볼 수도 있습니다. 때로는 흥미로운 교육이며 컴파일러 버그도 발견 할 수 있습니다. 코드에 어떤 문제가 있는지 알아 내려고하는 불쾌한 것들!

실제로 필요한 경우 링커가 실제로 사용되지 않는 전체 함수와 데이터 세그먼트를 제거 할 수 있도록 --gc-sections를 사용하여 -fdata-sections 및 -fcode-sections에 추가를 시작합니다. 사물을 줄이거 나 더 빨리 만들려고 노력할 수있는 많은 것들이 있지만, 전반적으로 이것들은 내가 사용하는 유일한 트릭이며, 작거나 빠를 수있는 모든 것들입니다. -모으다.


2

예, 디버깅하는 동안 최적화를 비활성화하는 것이 세 가지 이유로 한동안 모범 사례였습니다.

  • (a) 높은 수준의 디버거를 사용하여 프로그램을 한 단계 씩 진행하려는 경우 약간 덜 혼란 스럽습니다.
  • (a) (구식) 어셈블리 언어 디버거를 사용하여 프로그램을 단일 단계 디버깅하려는 경우 훨씬 덜 혼란 스럽습니다. (그러나 높은 수준의 디버거를 사용할 수 있는데 왜 이것을 귀찮게하겠습니까?)
  • (b) (오래 사용되지 않음) 아마도이 특정 실행 파일을 한 번만 실행 한 다음 변경하고 다시 컴파일해야합니다. 10 분 미만의 런타임을 절약 할 때 컴파일러가이 특정 실행 파일을 "최적화"하는 동안 추가 10 분을 기다리는 것은 시간 낭비입니다. (이는 더 이상 2 초 이내에 전체 최적화를 통해 일반적인 마이크로 컨트롤러 실행 파일을 컴파일 할 수있는 최신 PC와 관련이 없습니다).

많은 사람들이이 방향으로 나아가고 어설 션이 설정된 상태로 제공됩니다 .


어셈블리 코드를 통한 단일 스테핑은 소스 코드가 실제로 보이는 것과 다른 것 (예 : "longvar1 & = ~ 0x40000000; longvar2 & = ~ 0x80000000;")을 지정하거나 컴파일러에서 버그가있는 코드를 생성하는 경우를 진단하는 데 매우 유용 할 수 있습니다. . 나는 다른 방법으로 추적 할 수 없다고 생각하는 기계 코드 디버거를 사용하여 몇 가지 문제를 추적했습니다.
supercat

2

단순성 : 최적화는 시간이 많이 걸리며 나중에 개발 단계에서 해당 코드를 변경해야하는 경우 쓸모가 없을 수 있습니다. 따라서 시간과 돈을 낭비 할 수 있습니다.
그러나 완성 된 모듈에는 유용합니다. 더 이상 변경이 필요없는 코드 부분.


2

컴파일러는 실제로 메모리에 영향을 미치지 않는 많은 명령문을 제거 할 수 있기 때문에 브레이크 포인트의 경우 확실히 의미가 있습니다.

다음과 같은 것을 고려하십시오 :

int i =0;

for (int j=0; j < 10; j++)
{
 i+=j;
}
return 0;

i읽지 않기 때문에 완전히 최적화 할 수 있습니다 . 그것은 기본적으로 단지 거기에 없었을 때 모든 코드를 건너 뛴다는 점에서 브레이크 포인트의 관점에서 볼 것입니다.

for (int j=delay; j != 0; j--)
{
    asm( " nop " );
    asm( " nop " );
}
return 0;

1

디버거를 사용하는 경우 최적화를 비활성화하고 디버그를 활성화합니다.

개인적으로 PIC 디버거가 수정하는 데 도움이되는 것보다 더 많은 문제를 일으키는 것으로 나타났습니다.
C18로 작성된 내 프로그램을 디버그하기 위해 USART에 printf ()를 사용합니다.


1

컴파일에서 최적화를 활성화하는 것에 대한 대부분의 주장은 다음과 같습니다.

  1. 디버깅 문제 (JTAG 연결, 중단 점 등)
  2. 잘못된 소프트웨어 타이밍
  3. sh * t가 제대로 작동하지 않습니다

IMHO 첫 두 개는 합법적이며 세 번째는 그리 많지 않습니다. 그것은 종종 당신이 나쁜 코드를 가지고 있거나 언어 / 구현에 대한 안전하지 않은 착취에 의존하고 있거나 아마도 저자가 정의되지 않은 삼촌 행동의 팬이라는 것을 의미합니다.

Embedded in Academia 블로그에는 정의되지 않은 동작에 대해 언급 할 내용이 있으며이 게시물은 컴파일러가이를 활용하는 방법에 대한 것입니다. http://blog.regehr.org/archives/761


또 다른 가능한 이유는 최적화가 켜져있을 때 컴파일러가 성가 시게 느려질 수 있기 때문입니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.