programing

컴파일러는 다른 유형의 루프에 비해 do-while 루프에 대해 더 나은 코드를 생성합니까?

prostudy 2022. 8. 28. 11:51
반응형

컴파일러는 다른 유형의 루프에 비해 do-while 루프에 대해 더 나은 코드를 생성합니까?

zlib 압축 라이브러리(다른 많은 컴파일러 중 크롬 프로젝트에서 사용됨)에 코멘트가 있습니다.이것은 C의 do-while 루프가 대부분의 컴파일러에서 "더 나은" 코드를 생성한다는 것을 의미합니다.여기에 코드 조각이 표시됩니다.

do {
} while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         scan < strend);
/* The funny "do {}" generates better code on most compilers */

https://code.google.com/p/chromium/codesearch#chromium/src/third_party/zlib/deflate.c&l=1225

대부분의 컴파일러(또는 모든 컴파일러)가 보다 효율적인 코드를 생성한다는 증거가 있습니까?

업데이트: 원작자 중 한 명인 Mark Adler는 코멘트에서 약간의 맥락을 설명했습니다.

우선:

A do-while가 른른른른른과 않다while- 또는 a - "루프" "루프"for- loop

  • while ★★★★★★★★★★★★★★★★★」for루프 본체가 전혀 실행되지 않을 수 있습니다.
  • A do-while은 항상 checkloop을 .초기 상태 체크를 건너뜁니다.

이치노그렇다고 해서 모두가 이것을 엄격하게 고수하는 것은 아니다. it it it it it it it it it it it it it it it 。while ★★★★★★★★★★★★★★★★★」for루프는 항상 1회 이상 루프되는 것이 보증되는 경우에도 사용됩니다.(특히 foreach 루프가 있는 언어에서는 더욱 그렇습니다.

따라서 사과와 오렌지를 비교하는 것을 피하기 위해 루프가 적어도 한 번은 작동한다고 가정합니다. 더군다나, 더군다나, ,for 한 번 그것은 본질적으로while설탕

그럼 제가 질문에 답하겠습니다.

「」의 'while수.「 1 」 「 1 」 「 1 」 「 1 」을 사용해 퍼포먼스가 되고 있지 않은가.이러한 루프를 사용함으로써 퍼포먼스가 향상됩니까?do-while대신 루프합니다.


A do-while첫 번째 조건 체크를 건너뜁니다.따라서 평가해야 할 브랜치 하나와 조건이 하나 줄어듭니다.

하는 데 이 많이 들고 수 것을 있는 , 는 한 번 이상 루프할 수 없습니다.do-while루프가 더 빠를 수 있습니다.

그리고 이것은 기껏해야 마이크로 최적화로 여겨지지만 컴파일러가 항상 할 수 있는 것은 아닙니다.특히 컴파일러가 루프가 적어도 한 번은 입력된다는 것을 증명할 수 없는 경우입니다.


즉, while-loop:

while (condition){
    body
}

사실상 다음과 같습니다.

if (condition){
    do{
        body
    }while (condition);
}

항상 1회 이상 루프하는 것을 알고 있는 경우, 그 if-statement는 관계가 없습니다.


어셈블리 레벨에서도 마찬가지로 다양한 루프가 다음과 같이 컴파일 됩니다.

do-while 루프:

start:
    body
    test
    conditional jump to start

while-loop:

    test
    conditional jump to end
start:
    body
    test
    conditional jump to start
end:

조건이 중복되어 있는 것에 주의해 주세요.대체 접근법은 다음과 같습니다.

    unconditional jump to end
start:
    body
end:
    test
    conditional jump to start

...복제된 코드를 다른 점프를 위해 교환합니다.

쪽이든, 여전히 보다 더 심하다do-whileloopsyslog.syslog..syslog.

즉, 컴파일러는 원하는 것을 할 수 있습니다.그리고 루프가 항상 한 번 들어간다는 것을 증명할 수 있다면 루프가 당신을 위해 작동한 것입니다.


그러나 이 질문의 특정 예제는 루프 본체가 비어 있기 때문에 상황이 좀 이상합니다.에 논리적으로 while ★★★★★★★★★★★★★★★★★」do-while.

WWIW, Visual Studio 2012에서 테스트했습니다.

  • 있으면 같은 코드를 할 수 .while ★★★★★★★★★★★★★★★★★」do-while그래서 그 부분은 컴파일러가 그다지 훌륭하지 않았던 옛날의 유물이 될 수 있습니다.

  • 그러나 VS2012는 빈 바디가 아니기 때문에 조건 코드의 중복을 피할 수 있지만 추가 조건부 점프가 발생합니다.

이 의 예에서는 왜 이 문제가 do-while일반적인 경우에는 루프가 더 빠를 수 있지만 예제 자체는 현대의 컴파일러에는 아무런 이점이 없는 것 같습니다.

그 댓글이 얼마나 오래됐는지 생각해보면, 우리는 그것이 왜 중요한지 추측할 수 있을 뿐이다.그 당시 컴파일러들은 본문이 비어 있다는 것을 인식하지 못했을 가능성이 매우 높습니다.(혹은 만약 비어 있다면 정보를 사용하지 않았을 수도 있습니다.)

대부분의 컴파일러(또는 모든 컴파일러)가 보다 효율적인 코드를 생성한다는 증거가 있습니까?

특정 플랫폼 상에서 실제로 생성된 특정 컴파일러의 어셈블리를 특정 최적화 설정으로 보는 경우를 제외하고 별로 없습니다.

이것은 아마 수십 년 전(ZLib가 작성되었을 때)에는 걱정할 만한 가치가 있었을 것입니다.그러나, 이것이 코드의 보틀 넥을 제거하는 것을, 실제 프로파일링을 통해서 발견하지 않는 한, 오늘날에는 확실히 그렇지 않습니다.

요약하면 (tl;dr):

OPS 코드의 코멘트를 조금 다르게 해석하고 있습니다만, 그들이 「더 좋은 코드」라고 주장하는 것은, 실제의 작업을 「조건」에 옮겼기 때문이라고 생각합니다.다만, 컴파일러에 특화되어 있기 때문에, 조금 다른 코드를 작성할 수 있지만, 그 비교는 거의 무의미하고, 아마 쓸모없다고 하는 것은, 이하와 같습니다.


세부사항:

원저자가 이것에 대해 말한 것이 무엇을 의미하는지 말하기 어렵다.do {} while더 나은 코드를 생산하고 있지만, 여기서 제기된 것과는 다른 방향으로 추측하고 싶습니다 - 우리는 그 차이가do {} while그리고.while {}루프는 매우 얇습니다(Ministry가 말한 것처럼 브랜치 1개 줄었습니다). 하지만 이 코드에는 더 재미있는 것이 있습니다.그 때문에, 모든 작업이 이 미친 상태에 들어가, 내부 부품을 비워 둡니다( ).do {}).

gcc 4.8.1(-O3)에서 다음 코드를 사용해 봤는데 흥미로운 차이가 있습니다.

#include "stdio.h" 
int main (){
    char buf[10];
    char *str = "hello";
    char *src = str, *dst = buf;

    char res;
    do {                            // loop 1
        res = (*dst++ = *src++);
    } while (res);
    printf ("%s\n", buf);

    src = str;
    dst = buf;
    do {                            // loop 2
    } while (*dst++ = *src++);
    printf ("%s\n", buf);

    return 0; 
}

컴파일 후 -

00000000004003f0 <main>:
  ... 
; loop 1  
  400400:       48 89 ce                mov    %rcx,%rsi
  400403:       48 83 c0 01             add    $0x1,%rax
  400407:       0f b6 50 ff             movzbl 0xffffffffffffffff(%rax),%edx
  40040b:       48 8d 4e 01             lea    0x1(%rsi),%rcx
  40040f:       84 d2                   test   %dl,%dl
  400411:       88 16                   mov    %dl,(%rsi)
  400413:       75 eb                   jne    400400 <main+0x10>
  ...
;loop 2
  400430:       48 83 c0 01             add    $0x1,%rax
  400434:       0f b6 48 ff             movzbl 0xffffffffffffffff(%rax),%ecx
  400438:       48 83 c2 01             add    $0x1,%rdx
  40043c:       84 c9                   test   %cl,%cl
  40043e:       88 4a ff                mov    %cl,0xffffffffffffffff(%rdx)
  400441:       75 ed                   jne    400430 <main+0x40>
  ...

첫 번째 루프는 7개의 명령을 수행하며 두 번째 루프는 6개의 명령을 수행하지만 같은 작업을 수행해야 합니다.이 배후에 컴파일러의 스마트함이 있는지 어떤지는 알 수 없습니다.아마도 우연의 일치일 것입니다만, 이 프로젝트가 사용하고 있는 다른 컴파일러 옵션과 어떻게 상호작용하는지는 확인하지 못했습니다.


한편 Clang 3.3(-O3)에서는 양쪽 루프가 다음 5개의 명령 코드를 생성합니다.

  400520:       8a 88 a0 06 40 00       mov    0x4006a0(%rax),%cl
  400526:       88 4c 04 10             mov    %cl,0x10(%rsp,%rax,1)
  40052a:       48 ff c0                inc    %rax
  40052d:       48 83 f8 05             cmp    $0x5,%rax
  400531:       75 ed                   jne    400520 <main+0x20>

이는 컴파일러가 상당히 다르며, 몇 년 전에 일부 프로그래머가 예상했던 것보다 훨씬 빠른 속도로 발전한다는 것을 보여줍니다.그것은 또한 이 논평이 매우 무의미하고 아마도 아무도 그것이 여전히 말이 되는지 확인하지 않았기 때문에 거기 있을 것이라는 것을 의미한다.


결론 - 가능한 한 최선의 코드로 최적화하고 싶은 경우(및 그 방법을 알고 있는 경우), 어셈블리에서 직접 실시해, 「중간자」(컴파일러)를 등식에서 잘라냅니다만, 새로운 컴파일러나 새로운 하드웨어가 이 최적화를 불필요하게 하는 경우가 있습니다.대부분의 경우 컴파일러에게 그 수준의 작업을 맡기고 큰 작업을 최적화하는 데 주력하는 것이 훨씬 좋습니다.

명령 카운트(원래 OPs의 코드가 이 코드 뒤에 있었다고 가정)를 실행해야 하는 또 다른 포인트는 코드 효율성에 대한 좋은 측정이 결코 아닙니다.모든 명령어가 동일한 것은 아니며 CPU에 의해 최적화되기 때문에 일부 명령어(예를 들어 단순한 reg-to-reg 이동)는 매우 저렴합니다.다른 최적화는 실제로 CPU 내부 최적화에 악영향을 미칠 수 있으므로 결국 적절한 벤치마킹만 고려됩니다.

A while 프는는 loop a a a a a a a a a로 .do-while "")으로 루프합니다.

    bra $1    ; unconditional branch to the condition
$2:
    ; loop body
$1:
    tst <condition> ; the condition
    brt $2    ; branch if condition true

, 「」, 「」의 컴파일에는, 「」이 포함되어 있습니다.do-while은 첫 branchloop이 없어도 .을 알 수 while()브런치 , 첫 브런치 비용1회밖에 되지 실행의 ]while,따라서 반복마다 조건부 브랜치와 무조건 브랜치가 모두 필요합니다.]

그렇다고는 해도, 실제로는 비교가 되지 않는 대체 수단이라고는 할 수 없습니다..while를 틀다do-while루프 반대의 경우도 마찬가지입니다.그들은 다른 일을 한다.그리고 이 경우 몇 가지 메서드 호출이 컴파일러가 무엇을 하든 완전히 지배하게 됩니다.whiledo-while.

이 코멘트는 컨트롤 스테이트먼트의 선택(do vs. while)이 아니라 루프 언롤링에 관한 것입니다.

보시는 바와 같이 이것은 문자열 비교 함수(문자열 요소는 2바이트 길이일 가능성이 있음)로, 단축키와 식에 4개가 아닌 단일 비교를 사용하여 기술할 수 있습니다.

이 후자의 실장은 4개의 요소 비교 후 스트링 종료 조건을 1회 체크하기 때문에 확실히 고속입니다.반면 표준 코딩은 비교당 1회의 체크를 수반합니다.다르게 말하면, 4요소당 5개의 테스트와 4요소당 8개의 테스트입니다.

가 있는 두이 Sentinel 합니다).strend 위험합니다!

while vs. do efficiency에 대한 이러한 논의는 본문이 없기 때문에 이 경우 전혀 의미가 없습니다.

while (Condition)
{
}

그리고.

do
{
}
while (Condition);

완전히 동등합니다.

언급URL : https://stackoverflow.com/questions/20172402/do-compilers-produce-better-code-for-do-while-loops-versus-other-types-of-loops

반응형