while (1) 또는 while (2) 중 어느 것이 더 빠릅니까?


587

이것은 선임 관리자가 묻는 인터뷰 질문이었습니다.

어느 것이 더 빠릅니까?

while(1) {
    // Some code
}

또는

while(2) {
    //Some code
}

내부 표현식 while이 최종적으로 true또는로 평가되어야 할 때 둘 다 동일한 실행 속도를 가지고 있다고 말했습니다 false. 이 경우 true조건 내부를 평가하고 추가 조건 지침이 없습니다 while. 따라서 둘 다 동일한 실행 속도를 가지며 나는 (1) 동안 선호합니다.

하지만 면접관은 자신있게 말했다 : "당신의 기초를 확인합니다. while(1)보다 더 빨리이다 while(2)." (그는 내 자신감을 테스트하지 않았다)

이것이 사실입니까?

참조 : 빨리 "동안 (TRUE)"이 아닌 "에 대한 (;;)"인가를? 그렇지 않은 경우 사람들은 왜 그것을 사용합니까?


202
반 괜찮은 컴파일러는 두 형식을 모두 최적화하지 않습니다.

64
while (n)마다 최적화 된 빌드에서, n! = 0 또는 for (;;)는 처음에는 레이블이 있고 끝은 goto 인 어셈블리 무한 루프로 변환됩니다. 정확히 동일한 코드, 동일한 성능.
Alex F

60
당연히, 주식 최적화는 스 니펫 모두에0x100000f90: jmp 0x100000f90 대해 주소를 가져옵니다 (주소가 다름) . 면접관은 아마도 간단한 깃발 점프 대 등록 테스트를 피할 수있었습니다. 질문과 그들의 가정은 절름발이입니다.
WhozCraig

50
면접관 의이 질문은 dilbert.com/strips/comic/1995-11-17 과 동일한 후원 에 해당합니다. 진술의 어리 석음의 몫에 관계없이 자신이 말하는 것을 진정으로 믿는 사람을 만날 것입니다. 다음 중 하나를 선택하십시오 : 깊은 자존심, 맹세, 웃음, 외침, 위의 조합 :)
GMasucci

3
@Mike W : 컴파일러가 무엇을해야하는지 궁금해 할 수 있습니다. Halt 문으로 변환하거나 무한 시간 후에 루프가 종료되고 무한 지연을 최적화한다고 생각하십니까?
Yves Daoust

답변:


681

두 루프는 모두 무한하지만 반복마다 더 많은 명령어 / 리소스가 필요한 루프를 확인할 수 있습니다.

gcc를 사용하여 다음 두 프로그램을 다양한 수준의 최적화로 어셈블리로 컴파일했습니다.

int main(void) {
    while(1) {}
    return 0;
}


int main(void) {
    while(2) {}
    return 0;
}

도없이 최적화 (함께 -O0), 생성 된 조립체를 모두 프로그램 동일 하였다 . 따라서 두 루프 사이에 속도 차이가 없습니다.

참고로, gcc main.c -S -masm=intel최적화 플래그와 함께 생성 된 어셈블리는 다음 과 같습니다.

-O0:

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    push    rbp
    .seh_pushreg    rbp
    mov rbp, rsp
    .seh_setframe   rbp, 0
    sub rsp, 32
    .seh_stackalloc 32
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

-O1:

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

부착 -O2하고 -O3(동일한 출력) :

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .section    .text.startup,"x"
    .p2align 4,,15
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

실제로 루프에 대해 생성 된 어셈블리는 모든 최적화 수준에서 동일합니다.

 .L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

중요한 부분은 다음과 같습니다.

.L2:
    jmp .L2

어셈블리를 잘 읽을 수는 없지만 무조건 루프입니다. 이 jmp명령 .L2은 값을 true와 비교하지 않고도 프로그램을 무조건 레이블로 다시 재설정하며 , 프로그램이 종료 될 때까지 즉시 다시 수행합니다. 이것은 C / C ++ 코드와 직접 일치합니다.

L2:
    goto L2;

편집하다:

흥미롭게 도 최적화없는 경우에도 다음 루프 jmp는 어셈블리에서 정확히 동일한 출력 (무조건 )을 생성했습니다 .

while(42) {}

while(1==1) {}

while(2==2) {}

while(4<7) {}

while(3==3 && 4==4) {}

while(8-9 < 0) {}

while(4.3 * 3e4 >= 2 << 6) {}

while(-0.1 + 02) {}

그리고 심지어 놀랍게도 :

#include<math.h>

while(sqrt(7)) {}

while(hypot(3,4)) {}

사용자 정의 함수는 조금 더 흥미로워집니다.

int x(void) {
    return 1;
}

while(x()) {}


#include<math.h>

double x(void) {
    return sqrt(7);
}

while(x()) {}

에서 -O0,이 두 가지 예는 실제로 호출 x하고 각 반복에 대한 비교를 수행합니다.

첫 번째 예 (1을 반환) :

.L4:
    call    x
    testl   %eax, %eax
    jne .L4
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

두 번째 예 ( sqrt(7)) :

.L4:
    call    x
    xorpd   %xmm1, %xmm1
    ucomisd %xmm1, %xmm0
    jp  .L4
    xorpd   %xmm1, %xmm1
    ucomisd %xmm1, %xmm0
    jne .L4
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

그러나 -O1둘 이상에서 이전 예제와 동일한 어셈블리를 생성합니다 ( jmp이전 레이블로 돌아가는 무조건 ).

TL; DR

GCC에서 다른 루프는 동일한 어셈블리로 컴파일됩니다. 컴파일러는 상수 값을 평가하고 실제 비교를 수행하지 않습니다.

이야기의 교훈은 다음과 같습니다.

  • C ++ 소스 코드와 CPU 명령어간에 변환 계층이 있으며이 계층은 성능에 중요한 영향을 미칩니다.
  • 따라서 소스 코드 만보고 성능을 평가할 수 없습니다.
  • 컴파일러 그러한 사소한 경우를 최적화하기에 충분히 똑똑 해야합니다 . 프로그래머 대부분의 경우에 대해 생각하면서 시간을 낭비 해서는 안됩니다 .

196
면접관이 gcc를 사용하지 않았을 수도 있습니다
MM

106
@Matt McNabb 좋은 지적입니다.하지만 면접관이 컴파일러 별 최적화에 의존하고 있다면 질문에 그 내용을 매우 명확하게 설명해야하며, "차이가 없습니다"라는 대답을 올바른 것으로 받아 들여야합니다. 일부 (가장?) 컴파일러.
Jonathan Hartley

109
의심을 없애기 위해 clang 3.4.2에서 이것을 테스트했으며 두 루프는 모든 -O수준 에서 동일한 어셈블리를 생성합니다 .
Martin Tournoij

16
루프의 조건부 섹션에 배치 한 모든 것이 컴파일 타임 상수이므로 놀랍지 않습니다. 따라서 컴파일러는 루프가 항상 참 또는 거짓이며 정중하게 단순히 jmp처음으로 돌아가거나 루프를 완전히 제거한다는 것을 컴파일러가 볼 것이라고 생각합니다 .
sherrellbc

10
@hippietrail 루프의 내용 (또는 내용이 없음)이 이러한 최적화에 어떤 영향을 미칠 수 있는지 알지 못하지만 (어떤 break문장 의 가능성을 제외하고 ) 루프 내부의 코드를 사용하더라도 루프를 테스트해도 안됩니다 . 절대 무조건 모두 while(1)while(2). 실제로 우려되는 경우 다른 사람을 직접 테스트하십시오.
ApproachingDarknessFish

282

예, while(1)보다 훨씬 빠르기 때문에 while(2), 인간이 읽을! while(1)익숙하지 않은 코드베이스에서 볼 때 저자가 의도 한 것을 즉시 알 수 있으며 내 시선은 다음 줄로 계속 진행될 수 있습니다.

내가 본다면 while(2)아마 내 트랙에서 멈추고 저자가 왜 쓰지 않았는지 알아 내려고 노력할 것이다 while(1). 저자의 손가락이 키보드에서 미끄러 졌습니까? 이 코드베이스의 관리자는 while(n)루프를 다르게 보이게하는 애매한 주석 처리 메커니즘으로 사용됩니까? 정적 분석 도구가 깨진 상태에서 가짜 경고에 대한 해결 방법이 있습니까? 아니면 이것이 생성 된 코드를 읽는 단서입니까? 잘못 권고 된 찾기 및 바꾸기, 또는 잘못된 병합 또는 우주 광선으로 인한 버그입니까? 이 코드 줄이 뭔가 다른 것을해야 할 수도 있습니다. 아마 읽었을 while(w)수도 while(x2)있습니다. 파일 기록에서 저자를 찾아서 "WTF"전자 메일을 보내는 것이 좋습니다. 이제 저는 정신적 인 맥락을 깨뜨 렸습니다.while(2)while(1) 잠깐 동안 걸렸을 것입니다!

나는 과장하지만 조금만. 코드 가독성이 정말 중요합니다. 그리고 그것은 인터뷰에서 언급 할 가치가 있습니다!


내 생각은 정확하고 정확하게 왜 (1) 더 빠르 냐 나는 그것이 코딩에 대해 알고 있다고 생각하지 않는 관리자의 오래된 사례라고 생각하지만, 모든 사람이 그것을 보지 못했다면 모두가 제다이.
ekerner

8
물론 이것은 전혀 과장된 것이 아닙니다. 확실히 svn-annotate 또는 git blame(또는 무엇이든)이 여기에서 사용되며 일반적으로 파일 비난 기록을로드하는 데 몇 분이 걸립니다. 그런 다음 마지막으로 "아 이해합니다, 저자는 고등학교에서 신선 할 때이 줄을 썼습니다", 10 분을 잃었습니다 ...
v.oddou

11
나는 컨벤션이 아프기 때문에 투표에 실패했습니다. 개발자는 자신이 좋아하는 숫자를 쓸 수있는 이런 기회를 갖게되는데, 지루한 누군가가 와서 왜 그렇지 않은지에 대해 불안해 1합니다.
Utkan Gezer

1
수사법으로 인해 하향 조정되었습니다. while (1)을 읽는 것은 while (2)와 정확히 같은 속도입니다.
hdante

4
while(2)코드에서 보았을 때 1 분 전에이 답변에 설명 된 것과 동일한 정신 과정을 겪었습니다. "이 코드베이스의 관리자는 while (n)을 모호한 주석 처리 메커니즘으로 사용하여 루프를 다르게 보이게합니까?" ). 아니, 당신은 과장하지 않습니다!
Hisham HM

151

특정 옵션 세트를 가진 특정 대상에 대해 특정 컴파일러에 의해 생성 된 코드를 보여주는 기존 답변은 질문이 특정 상황에서 질문되지 않는 한 ( 'x86_64에 gcc 4.7.2를 사용하는 것이 더 빠름) 예를 들어 기본 옵션을 사용 하시겠습니까? ")

언어 정의에 관한 한 추상 기계 while (1) 에서는 정수 상수 1while (2)평가하고 정수 상수를 평가합니다 2. 두 경우 모두 결과는 0과 같은지 비교됩니다. 언어 표준은 두 구성의 상대적인 성능에 대해서는 전혀 언급하지 않습니다.

나는 매우 순진한 컴파일러가 적어도 최적화를 요청하지 않고 컴파일 할 때 두 형태에 대해 다른 기계 코드를 생성 할 수 있다고 상상할 수 있습니다.

반면에 C 컴파일러 는 상수 표현식이 필요한 컨텍스트에 나타날 때 컴파일 타임에 일부 상수 표현식을 평가해야합니다 . 예를 들면 다음과 같습니다.

int n = 4;
switch (n) {
    case 2+2: break;
    case 4:   break;
}

진단이 필요합니다. 게으른 컴파일러에는 2+2실행 시간까지 평가를 연기하는 옵션이 없습니다 . 컴파일러는 컴파일 타임에 상수 표현식을 평가할 수 있어야하므로 필요하지 않은 경우에도 해당 기능을 활용하지 않을 이유가 없습니다.

C 표준 ( N1570 6.8.5p4)에 따르면

반복문 은 제어 표현식이 0과 비교 될 때까지 루프 본문 이라는 명령문이 반복적으로 실행되도록합니다.

따라서 관련 상수 표현식은 1 == 02 == 0이며 둘 다 int값으로 평가됩니다 0. 이러한 비교는 while루프 의 의미에 내재되어 있으며 실제 C 표현식으로는 존재하지 않습니다.

순진한 컴파일러 는 두 구문에 대해 다른 코드를 생성 할 수 있습니다. 예를 들어 첫 번째 경우 무조건 무한 루프를 생성하고 ( 1특별한 경우로 처리 ) 두 번째 경우와 동등한 명시 적 런타임 비교를 생성 할 수 2 != 0있습니다. 그러나 실제로 그렇게 작동하는 C 컴파일러를 본 적이 없으며 그러한 컴파일러가 존재하는지 심각하게 의심합니다.

대부분의 컴파일러 (모든 프로덕션 품질의 컴파일러를 말하려는 유혹이 있습니다)에는 추가 최적화를 요청할 수있는 옵션이 있습니다. 이러한 옵션을 사용하면 컴파일러가 두 형식에 대해 다른 코드를 생성 할 가능성이 훨씬 줄어 듭니다.

컴파일러가 두 구문에 대해 다른 코드를 생성하는 경우 먼저 다른 코드 시퀀스가 ​​실제로 다른 성능을 갖는지 확인하십시오. 그렇다면 최적화 옵션을 사용하여 다시 컴파일하십시오 (가능한 경우). 여전히 다른 경우, 버그 보고서를 컴파일러 공급 업체에 제출하십시오. C 표준을 준수하지 못한다는 의미에서 (필요하게) 버그는 아니지만 거의 수정해야 할 문제입니다.

결론 : while (1)while(2) 거의 확실하게 동일한 성능을 가지고있다. 그것들은 정확히 같은 의미론을 가지고 있으며, 어떤 컴파일러도 동일한 코드를 생성하지 않을 이유가 없습니다.

컴파일러가 for while(1)보다 빠른 코드를 생성하는 것은 합법적이지만 while(2)컴파일러가 동일한 프로그램에서 while(1)다른 코드 보다 더 빠른 코드를 생성하는 것은 합법적입니다 while(1).

(당신이 물었던 또 다른 질문이 있습니다 : 당신은 잘못된 기술적 요점을 주장하는 면접관을 어떻게 다루나요? 아마도 직장 사이트에 좋은 질문 일 것입니다 ).


8
"이 경우 관련 (암시 적) 상수 표현식은 1! = 0 및 2! = 0이며, 둘 다 int 값 1로 평가됩니다."... 이것은 너무 복잡하고 정확하지 않습니다. 표준은 단순히 제어 식은 while스칼라 유형이어야하며 루프 본문은식이 0과 비교 될 때까지 반복된다고 말합니다. expr != 0평가 되는 암시성이 있다고 말하는 것은 아닙니다. 즉 0 또는 1은 0, 무한대와 비교됩니다. 아니요, 표현식은 0과 비교되지만 해당 비교는 값을 생성하지 않습니다. 추신 : 나는 공감했다.
Jim Balter

3
@ JimBalter : 요점을 보았고 답을 업데이트하기 위해 답변을 업데이트하겠습니다. 그러나 제가 의미하는 바는 "... 제어식이 0과 같아 질 때까지"라는 표준의 표현은 평가하는 것을 의미합니다 <expr> == 0. 그것이 "0과 비교하다" 가 C에서 의미하는 것 입니다. 그 비교는 while루프 의 의미론의 일부입니다 . 표준이나 내 대답에는 결과를 0다시 비교해야한다는 의미가 없습니다 . (나는 ==보다는 글을 써야했다 !=.) 그러나 내 대답의 그 부분은 불분명했으며 그것을 업데이트 할 것이다.
키이스 톰슨

1
""0과 비교하다 "는 C"를 의미합니다.하지만 그 언어는 C가 아니라 표준입니다 . 구현은 비교를 수행하고 C 언어 비교를 생성하지 않습니다. "둘 다 int 값 1로 평가"라고 쓰지만 그러한 평가는 발생하지 않습니다. 당신을 위해 생성 된 코드를 보면 while (2), while (pointer), while (9.67), 가장 순진, 최적화되지 않은 컴파일러에 의해, 당신은 수익률 것을 생성 된 코드를 볼 수 없습니다 0또는 1. "나는! ="보다는 ==를 써야만했다 – 아니, 그건 말이되지 않았다.
Jim Balter

2
@ JimBalter : 흠. "0과 같은 값"이 ... == 0C 표현식 의 존재를 암시한다고 말하는 것은 아닙니다 . 내 요점은 표준의 while루프 설명에 필요한 "compares equals 0" 과 명시 적 x == 0표현은 논리적으로 동일한 연산을 암시한다는 것입니다. 그리고 나는 고통스럽고 순진한 C 컴파일러가 어떤 루프 의 int0이나 루프 를 생성하는 코드를 생성 할 수 있다고 생각하지만 실제 컴파일러는 그렇게 순진하다고 생각하지 않습니다. 1while
키이스 톰슨

12
참고 : 이것은 이미 직장 문제입니다 : workspace.stackexchange.com/questions/4314/…
Joe

141

잠깐만 면접관, 그는이 사람처럼 보였습니까?

여기에 이미지 설명을 입력하십시오

면접관 자신 이이 면담에 실패한 것은 나쁘다. 만약이 회사의 다른 프로그래머가이 시험을 "통과"하면 어떨까?

제 명령문을 평가 1 == 0하고 2 == 0 있어야한다 동등하게 빨리. 우리 하나가 다른 것보다 빠를 수있는 좋지 않은 컴파일러 구현을 상상할 수 있습니다. 하지만 아무도 없다 좋은의 하나가 다른 하나보다 더 빠르게되어야하는 이유 이유는.

주장이 사실 일 때 모호한 상황이 있더라도 프로그래머는 모호한 (이 경우 소름 끼치는) 퀴즈에 대한 지식을 바탕으로 평가해서는 안됩니다. 이 인터뷰에 대해 걱정하지 마십시오. 가장 좋은 방법은 걸어가는 것입니다.

면책 조항 : 이것은 원래 Dilbert 만화가 아닙니다. 이것은 단지 매시업 입니다.


6
질문에 대한 답변 포함 한다면 재미있을 것 입니다.
코디 그레이

그러나 실제로는 심각한 회사가 작성한 모든 컴파일러가 합리적인 코드를 생성 할 것이라고 매우 쉽게 상상할 수 있습니다. "최적화되지 않은 사례"/ O0을 보자. 아마도 anatolyg가 게시 한 것처럼 끝날 것이다. 그렇다면 CPU에 대한 문제는 cmp피연산자가 1에서 0보다 2에서 0보다 더 적은 사이클로 실행됩니까? 일반적으로 cmp를 실행하는 데 얼마나 많은주기가 필요합니까? 비트 패턴에 따라 가변적입니까? 속도가 느려지는 "복잡한"비트 패턴 cmp입니까? 나는 개인적으로 모른다. 랭크 0에서 n까지 비트 단위로 수퍼 이디 오틱 구현을 검사한다고 상상할 수 있습니다 (예 : n = 31).
v.oddou

5
그것은 내 요점이기도합니다. cmp피연산자 1과 200에 대해 똑같이 빨라야합니다 . 이것이 사실이 아닌 곳에서 바보 같은 구현을 상상할 수있을 것 입니다. 그러나 비이 디오 구현이 어디 while(1)보다 빠르다고 상상할 수 while(200)있습니까? 마찬가지로, 어떤 선사 시대 시대에 유일하게 이용 가능한 구현이 그렇게 바보 같았다면, 오늘날 우리는 그것에 대해 혼란스러워해야합니까? 나는 그렇게 생각하지 않습니다, 이것은 뾰족한 머리 보스 이야기이며, 그에 대한 진정한 보석입니다!
janos

@ v.ouddou "cmp 피연산자가 2에서 0보다 1에서 0을 비교하는 더 적은 주기로 실행됩니다"– 아니오.주기가 무엇인지 알아야합니다. "나는 개인적으로 모른다. 당신은 0에서 n까지 비트 단위로 비트를 검사하는 슈퍼 바보적인 구현을 상상할 수있다." 그리고 비트 단위로 검사하는 것이 왜 걱정됩니까? 구현은 프로그램을 평가하는 동안 점심 시간을 갖기로 결정한 상자에 든 사람 일 수 있습니다.
Jim Balter

79

설명이 맞습니다. 이것은 기술적 지식뿐만 아니라 자신감을 테스트하는 질문 인 것 같습니다.

그런데 대답하면

두 코드를 모두 완료하는 데 무한 시간이 걸리기 때문에 두 코드 모두 동일하게 빠릅니다

면접관은

그러나 while (1)초당 더 많은 반복을 수행 할 수 있습니다. 이유를 설명 할 수 있습니까? (이것은 말도 안됩니다; 당신의 자신감을 다시 테스트하십시오)

그래서 당신이했던 것처럼 대답함으로써, 당신은 그렇지 않으면이 나쁜 질문에 대해 논의하는 데 낭비되는 시간을 절약 할 수 있습니다.


다음은 최적화가 해제 된 시스템 (MS Visual Studio 2012)에서 컴파일러가 생성 한 코드 예입니다.

yyy:
    xor eax, eax
    cmp eax, 1     (or 2, depending on your code)
    je xxx
    jmp yyy
xxx:
    ...

최적화가 설정된 경우 :

xxx:
    jmp xxx

따라서 생성 된 코드는 적어도 최적화 컴파일러와 정확히 동일합니다.


28
이 코드는 실제로 컴파일러가 내 시스템에서 출력하는 것입니다. 나는 그것을하지 않았다.
아나톨리 그

10
icepack "동안 피연산자는 부울 타입입니다." 당신은 면접관입니까? 그러한 주장을하기 전에 C 언어와 표준에 익숙해 지도록 제안합니다.
Jim Balter

29
"나는 그것을 만들지 않았다." -말도 안되는 아이스팩에주의를 기울이지 마십시오. C에는 부울 유형이 없으며 (stdbool.h에는 _Bool이 있지만 동일하지 않으며 while오래 전에 의미 가 있습니다) 피연산자 while는 부울 또는 _Bool 또는 다른 특정 유형이 아닙니다. 피연산자는 모든 식이 while될 수 있습니다 . while은 0에서 중단되고 0이 아닌 상태에서 계속됩니다.
Jim Balter

36
"둘 다 완료하는 데 무한한 시간이 걸리기 때문에 두 코드 조각 모두 똑같이 빠릅니다." 무한 루프를 종료하는 유일한 방법은 하전 된 입자 또는 하드웨어 고장으로 인해 비트가 뒤집히는 while (00000001) {}while (00000000) {}입니다. 실제 비트가 많을수록 값이 false로 전환 될 가능성이 줄어 듭니다. 안타깝게도 2에는 하나의 비트 만 있습니다. 그러나 3은 훨씬 더 오래 실행됩니다. 이것은 항상 이것을 멀리 최적화하지 않는 컴파일러에만 적용됩니다 (VC ++).
Jonathan Dickinson

8
@ mr5 nope. 실제로 이와 같은 결과를 얻기 위해 수만 년 동안 실행 시간에 대해 이야기하고 있습니다. 단지 생각 실험입니다. 불멸의 종족을 원한다면 비트 충돌이 프로그램에 영향을 미치지 않도록 -1을 사용할 수 있습니다.
Jonathan Dickinson

60

질문에 대한 가장 가능성있는 설명은 인터뷰자가 프로세서가 0이 아닌 값에 도달 할 때까지 숫자의 개별 비트를 하나씩 확인한다고 생각한다는 것입니다.

1 = 00000001
2 = 00000010

"0"인 경우 알고리즘은 숫자의 오른쪽에서 시작하여 0이 아닌 비트에 도달 할 때까지 각 비트 while(1) { }를 확인해야합니다 while(2) { }. 루프 는 루프 당 반복 당 두 배의 비트를 확인해야합니다 .

이를 위해서는 컴퓨터 작동 방식에 대한 매우 잘못된 정신 모델이 필요하지만 자체 내부 논리가 있습니다. 확인하는 방법 중 하나는 물어하는 것 while(-1) { }또는 while(3) { }경우에 똑같이 빠른 것, 또는 while(32) { }, 심지어 느린 .


39
나는 인터뷰어의 오해가 "2는 조건식으로 사용하기 위해 부울로 변환되어야하는 정수이고, 1은 이미 부울이다"라고 생각했다.
Russell Borogove

7
그리고 비교 알고리즘이 왼쪽에서 시작되면 반대의 방법입니다.
Paŭlo Ebermann

1
+1, 정확히 내가 생각한 것입니다. 기본적 cmp으로 CPU 의 알고리즘은 첫 번째 차이에서 초기 루프 종료를 사용하는 선형 비트 검사 라고 믿는 누군가를 완벽하게 상상할 수 있습니다.
v.oddou

1
@PeterCordes "gcc 나 clang의 -O0출력이 문자 그대로 충분하지 않다는 주장을 들어 본 적이 없습니다." 나는 그런 말을 한 적이 없으며 gcc 또는 clang을 전혀 염두에 두지 않았습니다. 당신은 나를 오독해야합니다. MSVC가 생산하는 것이 재미 있다는 의견은 아닙니다.
blubberdiblub

1
@PeterCordes 내가 틀렸다 .MSVC에서 중단 점 while(1)은 루프 전에 중단 되지 않지만 표현식 에서 중단된다 . 그러나 식을 최적화 할 때 여전히 루프 내부에서 축소됩니다. 가지고 이 코드를 휴식의 많은. 최적화되지 않은 디버깅 모드에서는 이것을습니다 . 최적화 할 때, 많은 브레이크 포인트는 함께 또는 가을 다음 기능에 이상 유출 - (IDE를 보여줍니다 디버깅하는 동안 결과 중단 점) 분해 대응 .
blubberdiblub

32

물론 나는이 관리자의 실제 의도를 알지 못하지만 완전히 다른 견해를 제안합니다. 새로운 회원을 팀에 채용 할 때, 그가 충돌 상황에 어떻게 대응하는지 아는 것이 유용합니다.

그들은 당신을 갈등으로 몰아 넣었습니다. 이것이 사실이라면, 그들은 영리하고 질문은 좋았습니다. 은행업과 같은 일부 산업의 경우 문제를 Stack Overflow에 게시하면 거부 사유가 될 수 있습니다.

그러나 물론 나는 모른다. 단지 하나의 옵션을 제안한다.


실제로 우수하지만 while (2) vs while (1)은 분명히 딜버트 만화에서 가져옵니다. 누군가 자신의 올바른 마음으로 발명 할 수는 없습니다 (어쨌든 누군가가 (2)를 쓸 수있는 방법으로 생각해 낸 방법은 무엇입니까?) 당신의 가설이 사실이라면, 당신은 그것을 독특하게 만들 수 있습니다. "while (0xf00b442)가 while (1)보다 느리다"와 같이, 은행은 다른 방법으로 조사관의 질문을 어떻게 찾습니까? 이들이 NSA이고 키스 코어에 액세스 할 수 있다고 가정합니까?
v.oddou

25

나는 그 단서가 "선임 관리자의 요청"에서 찾을 수 있다고 생각합니다. 이 사람은 관리자가되었을 때 프로그래밍을 중단 한 다음 수석 관리자가되기까지 몇 년이 걸렸습니다. 프로그래밍에 대한 관심을 잃지 않았지만 그 이후로 줄을 쓰지 않았습니다. 따라서 그의 답변은 일부 답변에서 언급했듯이 "저렴한 컴파일러"가 아니라 "이 사람이 20-30 년 전에 함께 일한 컴파일러"입니다.

당시 프로그래머들은 '중앙 미니 컴퓨터'의 CPU 시간이 매우 중요하기 때문에 코드를 더 빠르고 효율적으로 만들기 위해 다양한 방법을 시도하는 데 상당한 시간을 소비했습니다. 사람들이 컴파일러를 작성했듯이. 그 당시 그의 회사가 사용할 수있는 유일한 컴파일러는 '최적화 될 수있는 자주 발생하는 문'을 기반으로 최적화되었으며 while (1)을 만났을 때 약간의 지름길을 가졌고 모든 것을 평가했습니다. while (2)를 포함한 다른 것. 그러한 경험을했다면 그의 입장과 확신을 설명 할 수 있습니다.

당신을 고용하는 가장 좋은 방법은 아마도 선임 관리자가 다음 인터뷰 주제로 부드럽게 이끌 기 전에 "좋은 옛날 프로그래밍"에 대해 2-3 분 동안 강의하고 강의 할 수있게하는 방법 일 것입니다 . (좋은 타이밍은 여기서 너무 빠르며 이야기를 방해하고 있습니다-너무 느리고 초점이 불충분 한 사람으로 표시됩니다). 인터뷰 마지막에이 주제에 대해 더 많이 배우고 싶다고 말하십시오.


19

그가 어떻게 그 결론에 도달했는지 물었을 것입니다. 적절한 컴파일러에서 두 개는 동일한 asm 명령어로 컴파일됩니다. 그래서 그는 컴파일러에게 시작해야한다고 말했을 것입니다. 그럼에도 불구하고 이론적으로 교육받은 추측을하기 위해서는 컴파일러와 플랫폼을 잘 알아야합니다. 그리고 실제로 메모리 조각 화나 시스템로드와 같은 다른 외부 요소가이 세부 사항보다 루프에 더 영향을 미치기 때문에 실제로는 실제로 중요하지 않습니다.


14
@GKFX 만약 당신이 당신의 대답을했는데 그들이 당신이 틀렸다고 말하면 이유를 설명해 줄 이유가 없습니다. Anatolyg가 정확하고 자기 확신의 시험 인 경우, 왜 자신이 한 방식으로 대답했는지, 왜 똑같이 묻는 지 설명해야합니다.
P.Turpie

나는 당신이 그들에게 가장 먼저 말하는 것을 의미했습니다. "X가 더 빠른 이유는 무엇입니까?" "모르지만 x가 더 빠른 이유는 무엇입니까?" 분명히, 제대로 대답하면 물어볼 수 있습니다.
GKFX

18

이 질문을 위해, 나는이 최적화 통과하지 않고 일부 초기 C 컴파일러를위한 어셈블리 테스트를 생성 할 것이라고 C위원회 서면에서 더그 귄를 그 추가 기억해야한다 while(1)(에 비교 for(;;)하는 것이이없는 것입니다).

나는이 역사적인 메모를함으로써 면접관에게 대답하고 어떤 컴파일러가 이것을 놀라게하더라도 컴파일러는 다음과 같이 할 수 있다고 말한다.

  • 않고 최적화 컴파일러는 모두 시험을 통과 생성 while(1)while(2)
  • 옵티 마이저 패스를 사용하면 컴파일러는 while(1)관용으로 간주되기 때문에 무조건 점프로 최적화하도록 지시 받습니다. 이것은 while(2)테스트를 남길 수 있으므로 둘 사이의 성능 차이를 만듭니다.

나는 물론 고려하지 않는 것이 면접관에 추가 할 수 while(1)while(2)이 동등한 구조가 같은 구조는 낮은 품질의 최적화의 표시이다.


10

그러한 질문에 대한 또 다른 취지는 관리자에게 자신이 틀렸다고 말하는 용기가 있는지 확인하는 것입니다! 그리고 얼마나 부드럽게 의사 소통 할 수 있습니까?

내 첫 번째 본능은 괜찮은 컴파일러가 처리해야한다는 것을 관리자에게 보여주기 위해 어셈블리 출력을 생성하는 것이 었습니다. 그렇지 않으면 다음 패치를 제출합니다 :)


8

많은 사람들이이 문제를 파고 들기 위해, 이것이 왜 당신이 사물 을 미세 최적화 하기를 원하는지 확인하는 시험이 될 수있는 이유를 정확하게 보여줍니다 .

내 대답은 그다지 중요하지 않습니다. 오히려 우리가 해결하고있는 비즈니스 문제에 중점을 둡니다. 결국, 그것이 내가 지불 할 것입니다.

또한, 나는 while(1) {}더 일반적이기 때문에 선택 하고 다른 팀원들은 왜 누군가가 1보다 높은 숫자를 찾는 지 알아 내기 위해 시간을 소비 할 필요가 없습니다.

이제 코드를 작성하십시오. ;-)


1
실시간 코드를 최적화하기 위해 비용을 지불하지 않는 한, 실행 시간 요구에 맞게 1 또는 2 밀리 초 만 면도하면됩니다. 물론 그것은 옵티마이 저의 일입니다. 아키텍처에 대한 옵티마이 저가있는 경우도 있습니다.
Neowizard

6

최적화가 걱정된다면

for (;;)

테스트가 없기 때문입니다. (사이 닉 모드)


2
그렇기 때문에을 사용하는 것이 현명하고 while(2)최적화의 여지를 남겨두고 for (;;)성능 향상을위한 준비가되면 간단히 전환하면 됩니다.
Groo

5

이것은 기술적 인 질문으로 가려진 행동 인터뷰 질문 중 하나 인 것 같습니다. 어떤 회사들은 이것을한다-그들은 어떤 유능한 프로그래머라도 대답하기 쉬운 기술적 인 질문을 할 것이지만, 면담자가 정답을 주면 면접관은 그들이 틀렸다고 말할 것이다.

회사는이 상황에서 어떻게 대응할 것인지 알고 싶어합니다. 자기 의심이나 면접관을 화나게하는 두려움 때문에 조용히 앉아 대답이 정확하다고 강요하지 않습니까? 아니면 자신이 잘못 알고있는 권위있는 사람에게 도전 할 의향이 있습니까? 그들은 당신이 당신의 유죄 판결을 기꺼이지지하는지, 당신이 그것을 재치 있고 존중하는 방식으로 할 수 있는지 알고 싶어합니다.


3

이런 종류의 말도 안되는 차이가 생겼을 때 C 및 어셈블리 코드를 다시 프로그래밍하는 데 사용했습니다. 그것이 차이를 만들었을 때 우리는 그것을 의회에 썼습니다.

내가 그 질문을 받았다면 나는 도널드 크 누스의 유명한 1974 년 조기 최적화에 대한 인용문을 반복하고 면접관이 웃지 않고 계속 움직이면 걸었다.


1
그는 또한“기존 엔지니어링 분야에서 쉽게 얻을 수있는 12 % 향상은 결코 한계로 간주되지 않으며 소프트웨어 엔지니어링에서도 동일한 관점이 우선해야한다고 생각합니다.
Alice

3

면접관이 그런 멍청한 질문을 의도적으로 제기하고 3 점을 원했을 수도 있습니다.

  1. 기본 추론. 두 루프 모두 무한하며 성능에 대해 말하기가 어렵습니다.
  2. 최적화 수준에 대한 지식 그는 컴파일러가 당신을 위해 어떤 최적화를하게한다면 당신의 의견을 듣고 싶었다.
  3. 마이크로 프로세서 아키텍처에 대한 지식 대부분의 아키텍처에는 0과 비교하기위한 특별한 CPU 명령어가 있습니다 (반드시 더 빠를 필요는 없음).

2

문제가 있습니다. 실제로 프로그램을 작성하고 속도를 측정하면 두 루프의 속도가 다를 수 있습니다! 합리적인 비교를 위해 :

unsigned long i = 0;
while (1) { if (++i == 1000000000) break; }

unsigned long i = 0;
while (2) { if (++i == 1000000000) break; }

시간을 인쇄하는 코드가 추가되면 루프가 하나 또는 두 개의 캐시 라인 내에 배치되는 방식과 같은 임의의 효과가 차이를 만들 수 있습니다. 하나의 루프는 순전히 하나의 캐시 라인 내에 있거나 캐시 라인의 시작 부분에 있거나 두 개의 캐시 라인을 걸 수 있습니다. 결과적으로, 면접관이 주장하는 것이 가장 빠른 것은 실제로 우연의 일치 일 수 있습니다.

최악의 시나리오 : 최적화 컴파일러는 루프가 수행하는 작업을 파악하지 않지만 두 번째 루프가 실행될 때 생성 된 값이 첫 번째 루프에서 생성 된 값과 동일하다는 것을 알아냅니다. 그리고 첫 번째 루프에 대한 전체 코드를 생성하지만 두 번째 루프에 대해서는 전체 코드를 생성하지 않습니다.


1
"캐시 라인"추론은 캐시가 매우 작은 칩에서만 작동합니다.
sharptooth

10
이것은 요점 옆에 있습니다. 속도에 대한 질문은 "다른 모든 것이 같다"고 가정합니다.
Jim Balter

6
@ JimBalter : 이것은 속도 문제가 아니라 면접관과의 논쟁에 관한 문제였습니다. 그리고 실제 테스트를 수행 할 가능성은 면접관이 "올바른"것을 증명할 수 있습니다. 그의 주장과는 관련이 없지만 순수한 우연의 일치 때문입니다. 당신이 그가 틀렸다는 것을 증명하려고하면 당황스러운 상황에 처하게 될 것입니다.
gnasher729

1
@ gnasher729 모든 논리적 주장은 제쳐두고, 당신이 이야기하는 우연의 경우는 볼 가치가 있습니다. 그러나 여전히 맹목적으로 그러한 경우가 있다고 믿는 것은 순진한 것이 아니라 어리석은 일입니다. 당신이 무엇을 설명하지 않는 한,이 답변이 어떻게, 왜 그렇게 될지는 쓸모가 없습니다.
user568109

4
@ gnasher729 "이것은 속도의 문제가 아니었다"-나는 부정직 한 수사법을 사용하는 사람들을 토론하지 않을 것이다.
Jim Balter

2

둘 다 동일합니다.

사양에 따르면 0이 아닌 것은 사실로 간주되므로 최적화가 없어도 우수한 컴파일러는 while (1) 또는 while (2)에 대한 코드를 생성하지 않습니다. 컴파일러는 간단한 검사를 생성합니다 != 0.


@djechlin-둘 다 1 CPU 사이클을 사용하기 때문입니다.
UncleKing

컴파일러는 상수를 접으므로 런타임에도 평가되지 않습니다.
PaulHK

2

사람들이이 매우 간단한 질문을 테스트하고, 증명하고, 답변하는 데 걸린 시간과 노력의 양으로 판단하면 둘 다 질문을함으로써 매우 느려 졌다고 말합니다.

그리고 그것에 더 많은 시간을 보내려면 ...

"(2)"는 말도 안됩니다.

"while (1)"및 "while (true)"은 역사적으로 확실히 발생하는 조건에 따라 루프 내부의 어떤 단계에서 "break"가 호출 될 것으로 예상되는 무한 루프를 만드는 데 사용됩니다.

"1"은 항상 참으로 평가하기 위해 존재하기 때문에 "while (2)"는 "while (1 + 1 == 2)"라고 말하는 것만 큼 어리 석다는 말입니다.

그리고 당신이 완전히 바보가되고 싶다면 :

while (1 + 5 - 2 - (1 * 3) == 0.5 - 4 + ((9 * 2) / 4)) {
    if (succeed())
        break;
}

나는 코더가 코드 실행에 영향을 미치지 않는 오타를 만들었다 고 생각하고 싶지만 의도적으로 "2"를 이상하게 사용했다면 코드를 통해 이상한 sh를 넣기 전에 해체하십시오! 읽고 협력하십시오.


롤백 한 롤백은 매우 높은 사용자가 편집 한 내용을 되돌립니다. 이 사람들은 일반적으로 정책을 잘 알고 있으며 여러분에게 정책을 가르치기 위해 여기 있습니다. 게시물의 마지막 줄에 게시물에 유용한 내용이 없습니다. 내가 농담을 했음에도 불구하고 분명히 누락되었지만 추가 단락의 가치가없는 너무 수다하다고 생각합니다.
Palec

@Palec : 댓글 주셔서 감사합니다. 나는 같은 사람이 내 게시물에서 많은 부분을 편집했으며 대부분 서명을 제거한다는 것을 알았습니다. 그래서 나는 그가 평판이 좋은 트롤이었고, 따라서 그의 명성이라고 생각했습니다. 온라인 포럼이 더 이상 우리의 저술에 서명하지 않기 위해 언어와 일반 예의를 고갈 시켰습니까? 어쩌면 나는 조금 오래된 학교이지만 안녕하세요와 작별 인사를 생략하는 것은 무례한 것 같습니다. 우리는 앱을 사용하고 있지만 여전히 인간입니다.
ekerner

1
스택 교환 , 인사, 슬로건, 감사 등 환영하지 않습니다 . 도움말 센터 , 특히 예상되는 동작 에서 동일하게 언급됩니다 . Stack Overflow 뿐만 아니라 Stack Exchange의 일부 도 포럼이 아니며 Q & A 사이트입니다. 편집 은 차이점 중 하나입니다. 또한 의견은 토론에 대한 것이 아니라 게시물에 대한 피드백을 제공하는 데만 사용되어야합니다 ( 스택 오버 플로우 채팅 ). Q & A가 포럼과 다른 많은 방법이 있습니다. 도움말 센터메타 스택 오버플로 에 대한 자세한 내용 .
Palec

2000 개 이상의 담당자 (편집 권한)가 있으면 편집에서 더 이상 담당자를 얻지 못합니다. 동의하지 않는 이유와 수정 사항이 게시물에 적용되었거나 누군가의 오작동을 발견 한 경우 메타 스택 오버플로 에 문의하십시오 . 편집자가 잘못된 일을했다면, 그들은 mod (알지 못했을 수도 있음)에 의해 통지를 받거나 어떤 식 으로든 (행동이 악의적 일 경우) 처벌을받을 수 있습니다. 그렇지 않으면 편집 / 동작이 올바른 이유를 설명하는 포인터가 나타납니다. 불필요한 롤백으로 인해 개정 내역이 복잡해져 롤백 전쟁에 빠질 수 있습니다. 편집 내용이 정확하지 않은 경우에만 롤백합니다.
Palec

마지막으로, 서명, 인사말에 대해… : 이러한 형식을 포함하지 않는 것은 무례한 것처럼 보이지만 신호 대 잡음비를 증가 시키며 정보를 잃지 않습니다. 원하는 별명, 즉 서명을 선택할 수 있습니다. 대부분의 장소에는 아바타도 있습니다.
Palec

1

컴파일러에 따라 다릅니다.

코드를 최적화하거나 특정 명령어 세트에 대해 동일한 수의 명령어로 1과 2를 true로 평가하면 실행 속도는 동일합니다.

실제로는 항상 똑같이 빠르지 만, 다르게 평가 될 때 특정 컴파일러와 특정 시스템을 상상할 수 있습니다.

내 말은 : 이것은 실제로 언어 (C) 관련 질문이 아닙니다.


0

이 질문에 대답하고자하는 사람들은 가장 빠른 루프를 원하기 때문에 다른 답변에서 언급 한 것처럼 둘 다 동일한 어셈블리 코드로 똑같이 컴파일되고 있다고 대답했을 것입니다. 그럼에도 불구하고 '루프 언 롤링'을 사용하여 면접관에게 제안 할 수 있습니다 . while 루프 대신 {} while 루프 를 수행하십시오.

주의 : 루프가 항상 한 번만 실행되도록해야합니다 .

루프 내부에 파손 상태가 있어야합니다.

또한 이러한 종류의 루프의 경우 개인적으로 do {} while (42) 사용을 선호합니다 .0을 제외한 모든 정수가 작동하기 때문입니다.


그는 왜 {} while 루프를 제안합니까? While (1) (또는 while (2))도 같은 일을합니다.
Catsunami

한 번의 추가 점프를 제거하여 성능을 향상시킵니다. 물론 루프가 최소한 한 번만 실행될 것이라는 것을 알고있는 경우
Antonin GAVREL

0

명백한 대답은 다음과 같습니다. 게시 된대로 두 프래그먼트 모두 동일하게 사용중인 무한 루프를 실행하여 프로그램이 무한히 느려집니다 .

C 키워드를 매크로로 재정의하면 기술적으로 정의되지 않은 동작이 발생하지만 코드 조각을 전혀 빠르게 만들 수있는 유일한 방법입니다.이 조각을 두 조각 위에 추가 할 수 있습니다.

#define while(x) sleep(x);

실제로 while(1)두 배 빠르거나 느리게 while(2)만듭니다.


@MaxB : 실제로 매크로 트릭은 훨씬 더 왜곡되어야합니다. C 표준에서 보장하지는 않지만 새 버전이 작동해야한다고 생각합니다.
chqrlie

-4

내가 왜 while(2)더 느릴 지 생각할 수있는 유일한 이유 는 다음과 같습니다.

  1. 코드는 루프를 최적화합니다

    cmp eax, 2

  2. 빼기가 발생하면 본질적으로 빼기

    ㅏ. 00000000 - 00000010 cmp eax, 2

    대신에

    비. 00000000 - 00000001 cmp eax, 1

cmp플래그 만 설정하고 결과를 설정하지 않습니다. 따라서 최하위 비트에서 b 로 빌려야하는지 아닌지를 알고 있습니다. 반면 당신은 빌리기 전에 두 개의 빼기를 수행해야합니다.


15
cmp는 두 경우에 관계없이 1 CPU 사이클을 사용합니다.
UncleKing

8
해당 코드가 올바르지 않습니다. 올바른 코드는 레지스터 eax에 2 또는 1을로드 한 다음 eax를 0과 비교합니다.
gnasher729
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.