“while (1);”최적화 C ++ 0x에서


153

업데이트, 아래 참조!

C ++ 0x를 사용하면 컴파일러가 다음 스 니펫에 대해 "Hello"를 인쇄 할 수 있습니다.

#include <iostream>

int main() {
  while(1) 
    ;
  std::cout << "Hello" << std::endl;
}

분명히 스레드 및 최적화 기능과 관련이 있습니다. 이것은 많은 사람들을 놀라게 할 수 있다고 생각합니다.

누군가 이것이 왜 이것이 허용되어야했는지에 대해 잘 설명하고 있습니까? 참고로 가장 최근의 C ++ 0x 초안은6.5/5

for 문의 경우 for-init-statement 외부에있는 루프

  • 라이브러리 I / O 함수를 호출하지 않으며
  • 휘발성 개체에 액세스하거나 수정하지 않으며
  • 동기화 작업 (1.10) 또는 원 자성 작업 (원인 29)을 수행하지 않습니다

구현에 의해 종료 될 것으로 가정 될 수있다. [참고 : 이것은 종료를 증명할 수없는 경우에도 빈 루프 제거와 같은 컴파일러 변환을 허용하기위한 것입니다. — 끝 참고]

편집하다:

이 통찰력있는 기사 는 표준 텍스트에 대해 말합니다.

불행히도, "정의되지 않은 행동"이라는 단어는 사용되지 않습니다. 그러나 표준에서 "컴파일러가 P를 가정 할 수 있음"이라고 말하면 속성이 P가 아닌 프로그램에 정의되지 않은 의미가 있음을 의미합니다.

맞습니까? 컴파일러가 위 프로그램에 대해 "Bye"를 인쇄 할 수 있습니까?


여기에 더 많은 통찰력있는 스레드가 있습니다 .이 스레드 는 Guy가 위의 링크 된 기사를 시작한 C와 비슷한 변화에 관한 것입니다. 다른 유용한 사실 중에서도 C ++ 0x에도 적용되는 솔루션을 제시합니다 ( 업데이트 : n3225에서는 더 이상 작동하지 않습니다-아래 참조).

endless:
  goto endless;

컴파일러는 루프를 최적화하는 것이 아니라 루프가 아니기 때문에 최적화 할 수 없습니다. 다른 사람은 C ++ 0x 및 C201X의 제안 된 변경 사항을 요약합니다.

루프를 작성하여, 프로그래머는 주장한다 루프가 표시 동작에 무언가 (행한다 I / O, 휘발성 액세스 개체 또는 수행 동기 원자 또는 동작)을 수행하는 것이, 또는 그것이 결국 종료있다. 부작용없이 무한 루프를 작성하여 그 가정을 위반하면 컴파일러에 거짓말을하고 프로그램의 동작이 정의되지 않습니다. 운이 좋으면 컴파일러에서 경고를 표시 할 수 있습니다. 언어는 눈에 보이는 동작없이 무한 루프를 표현할 수있는 방법을 제공하지 않습니다 (더 이상 제공하지 않습니까?).


n3225로 3.1.2011 업데이트 :위원회에서 텍스트를 1.10 / 24로 이동하고

구현시 모든 스레드가 결국 다음 중 하나를 수행한다고 가정 할 수 있습니다.

  • 끝내다,
  • 라이브러리 I / O 함수를 호출하고
  • 휘발성 개체에 액세스하거나 수정하거나
  • 동기화 작업 또는 원자 작업을 수행합니다.

goto트릭은 것 없습니다 더 이상 작동!


4
while(1) { MyMysteriousFunction(); }신비한 기능의 정의를 모른 채 독립적으로 편집 할 수 있어야합니까? 그렇다면 라이브러리 I / O 함수를 호출하는지 어떻게 알 수 있습니까? 다시 말해서, 첫 번째 글 머리표를 사용할 수 있다는 것은 분명히 함수를 호출하지 않습니다 .
Daniel Earwicker

19
@Daniel : 함수의 정의에 접근 할 수 있다면 많은 것을 증명할 수 있습니다. 절차 적 최적화와 같은 것이 있습니다.
Potatoswatter

3
현재 C ++ 03에서 컴파일러는 ? 로 변경할 int x = 1; for(int i = 0; i < 10; ++i) do_something(&i); x++;for(int i = 0; i < 10; ++i) do_something(&i); int x = 2;있습니까? 또는 다른 방법으로 루프 전에 x초기화됩니다 2. 그것은 말할 수 do_something의 가치에 대해 상관하지 않는다 x는 완벽하게 안전하고 최적화하므로, 경우에 do_something 발생하지 않습니다의 값 i이 무한 루프에서 생을 마감하도록 변경할 수 있습니다.
Dennis Zickefoose

4
그렇다면 main() { start_daemon_thread(); while(1) { sleep(1000); } }백그라운드 스레드에서 데몬을 실행하는 대신 즉시 종료 할 수 있습니까?
Gabe

2
"이 통찰력있는 기사"는 명시 적이며 정의 된 동작이 없기 때문에 특정 동작이 정의되지 않은 동작이라고 가정합니다. 그것은 잘못된 가정입니다. 일반적으로 표준에서 유한 한 수의 동작을 열면 구현에서 그 중 하나를 선택해야합니다 ( 지정되지 않은 동작). 결정적이지 않아도됩니다. Do-nothing 루프가 종료되는지 여부는 부울 선택입니다. 또는 그렇지 않습니다. 다른 것을하는 것은 허용되지 않습니다.
MSalters

답변:


33

누군가 이것이 왜 이것이 허용되어야했는지에 대해 잘 설명하고 있습니까?

그렇습니다. Hans Boehm은 N1528 에서 이에 대한 이론적 근거를 제공합니다 . 왜 무한 루프에 대해 정의되지 않은 동작이 발생합니까? 이 문서는 WG14 문서이지만 이론적 근거는 C ++에도 적용되며 문서는 WG14와 WG21을 모두 참조합니다.

N1509가 올바르게 지적했듯이 현재 초안은 본질적으로 6.8.5p6의 무한 루프에 정의되지 않은 동작을 제공합니다. 그렇게하는 주요 문제는 코드가 종료되지 않는 루프를 통해 코드를 이동할 수 있다는 것입니다. 예를 들어, count와 count2가 전역 변수이거나 주소를 가지고 있고 p가 주소를 가져 오지 않은 지역 변수 인 다음과 같은 루프가 있다고 가정합니다.

for (p = q; p != 0; p = p -> next) {
    ++count;
}
for (p = q; p != 0; p = p -> next) {
    ++count2;
}

이 두 루프를 병합하여 다음 루프로 교체 할 수 있습니까?

for (p = q; p != 0; p = p -> next) {
        ++count;
        ++count2;
}

무한 루프에 대해 6.8.5p6에 특별한 경시가 없으면 이것이 허용되지 않습니다. q가 순환 목록을 가리켜 서 첫 번째 루프가 종료되지 않으면 원본은 count2에 쓰지 않습니다. 따라서 count2에 액세스하거나 업데이트하는 다른 스레드와 병렬로 실행될 수 있습니다. 무한 루프에도 불구하고 count2에 액세스하는 변환 된 버전에서는 더 이상 안전하지 않습니다. 따라서 변환으로 인해 데이터 경쟁이 발생할 수 있습니다.

이와 같은 경우 컴파일러가 루프 종료를 증명할 가능성은 거의 없습니다. q는 비순환 목록을 가리킨다는 것을 이해해야합니다. 비순환 목록은 대부분의 주류 컴파일러의 능력을 넘어서고 종종 전체 프로그램 정보가 없으면 불가능하다고 생각합니다.

비 종료 루프에 의해 부과되는 제한은 컴파일러가 종단을 증명할 수없는 종단 루프의 최적화와 실제로 비 종료 루프의 최적화에 대한 제한입니다. 전자는 후자보다 훨씬 흔하며 최적화하기가 더 흥미 롭습니다.

정수 루프 변수를 가진 for-loops도 분명히 있는데, 컴파일러가 종료를 증명하기 어렵 기 때문에 6.8.5p6없이 루프를 재구성하는 것이 어려울 것입니다. 심지어 같은

for (i = 1; i != 15; i += 2)

또는

for (i = 1; i <= 10; i += j)

다루기가 쉽지 않은 것 같습니다. 전자의 경우 종료를 증명하기 위해 몇 가지 기본 숫자 이론이 필요하며, 후자의 경우 j의 가능한 값에 대해 알아야합니다. 부호없는 정수에 대한 랩 어라운드는 이러한 추론 중 일부를 더 복잡하게 만들 수 있습니다. )

이 문제는 컴파일러 병렬화 및 캐시 최적화 변환을 포함하여 거의 모든 루프 구조 조정 변환에 적용되는 것으로 보입니다. 무한 루프를 가능한 가장 자연스러운 방식으로 작성할 수 있다는 이점 때문에 상당한 비용이 드는 것 같습니다. 특히 대부분의 사람들이 의도적으로 무한 루프를 거의 작성하지 않기 때문입니다.

C와의 한 가지 주요 차이점은 C11이 C ++과 다른 상수 표현식표현식을 제어하는 ​​예외를 제공하고 특정 예제를 C11에 잘 정의한다는 것입니다.


1
"루프의 종료가 객체의 상태에 의존하는 경우 루프를 실행하는 데 필요한 시간은 고려되지 않습니다. 그러한 시간이 무한한 경우에도 관찰 가능한 부작용 ". 주어 do { x = slowFunctionWithNoSideEffects(x);} while(x != 23);에 의존하지 것이다 루프 후 용이 한 코드 x안전하고 합리적인 것 같다,하지만 컴파일러 가정 할 수 있도록 x==23이러한 코드에 유용보다 더 위험한 것 같다.
supercat

47

나에게 관련 정당화는 다음과 같습니다

이는 종료를 입증 할 수없는 경우에도 빈 루프 제거와 같은 컴파일러 변환을 허용하기위한 것입니다.

아마도 종료를 기계적으로 증명하는 것이 어렵고 종료를 증명할 수 없기 때문에 컴파일러가 루프 전에서 후로 또는 그 반대로 비 종속 연산을 이동하거나 한 스레드에서 후 루프 연산을 수행하는 등 유용한 변환을 수행 할 수있는 컴파일러를 방해 할 수 있기 때문입니다. 루프는 다른 루프에서 실행됩니다. 이러한 변환이 없으면 루프가 다른 스레드를 모두 차단하면서 한 스레드가 해당 루프를 완료 할 때까지 기다릴 수 있습니다. (별도의 VLIW 명령 스트림을 포함하여 모든 형태의 병렬 처리를 의미하기 위해 "스레드"를 느슨하게 사용합니다.)

편집 : 바보 예 :

while (complicated_condition()) {
    x = complicated_but_externally_invisible_operation(x);
}
complex_io_operation();
cout << "Results:" << endl;
cout << x << endl;

여기서 한 스레드가 수행하는 것이 더 빠르며 다른 스레드 complex_io_operation는 루프에서 모든 복잡한 계산을 수행합니다. 그러나 인용 한 절이 없으면 컴파일러는 최적화를 수행하기 전에 두 가지를 증명해야합니다 .1) complex_io_operation()루프 결과에 의존하지 않는 루프와 2) 루프가 종료됩니다 . 1)을 증명하는 것은 매우 쉬우 며 2)가 멈추는 문제임을 증명합니다. 이 절을 사용하면 루프가 종료되고 병렬화 승리를 얻을 수 있습니다.

또한 설계자는 생산 코드에서 무한 루프가 발생하는 경우는 매우 드물며 일반적으로 어떤 방식으로 I / O에 액세스하는 이벤트 중심 루프와 같은 것이라고 생각합니다. 결과적으로, 그들은보다 일반적인 경우 (무한한이지만 기계적으로 비 한정적인 루프를 증명하기는 어렵다)를 최적화하기 위해 드문 경우 (무한 루프)를 비관했습니다.

그러나 학습 예제에 사용되는 무한 루프는 결과적으로 어려움을 겪고 초보자 코드에서 문제를 일으킬 것입니다. 나는 이것이 전적으로 좋은 것이라고 말할 수 없다.

편집 : 이제 당신이 연결하는 통찰력있는 기사와 관련하여, "컴파일러가 프로그램에 대해 X를 가정 할 수 있습니다"는 논리적으로 "프로그램이 X를 만족하지 않으면 동작이 정의되지 않았습니다"와 동일합니다. 우리는 이것을 다음과 같이 보여줄 수있다 : 속성 X를 만족시키지 않는 프로그램이 있다고 가정하자.이 프로그램의 행동은 어디에서 정의 될 것인가? 표준은 속성 X가 참이라고 가정하는 경우에만 동작을 정의합니다. 표준은 명시 적으로 행동을 미정도 선언하지 않지만, 생략에 의해 미정도 선언했다.

"컴파일러는 변수 x가 시퀀스 포인트 사이에 최대 한 번만 할당된다고 가정 할 수 있습니다."는 "시퀀스 포인트 사이에 x를 두 번 이상 할당하는 것이 정의되지 않은"과 같습니다.


"1을 증명하는 것은 매우 쉽다"-실제로 Johannes가 요구하는 조항에 따라 루프 종료를 가정 할 수 있도록 컴파일러가 3 가지 조건을 즉시 따르지 않습니까? 나는 그것들이 "루프는 영원히 회전하는 것을 제외하고는 관찰 가능한 효과가 없다"고 생각하며, 그 조항은 "영원히 회전하는 것"이 ​​그러한 루프에 대한 동작을 보장하지는 않는다는 것을 보장한다.
Steve Jessop

@Steve : 루프가 종료되지 않으면 쉽습니다. 그러나 루프가 종료되면 complex_io_operation.
Philip Potter

예, IO op에 사용되는 비 휘발성 로컬 / 별칭 / 무엇을 수정할 수 있다는 것을 놓쳤습니다. 따라서 당신이 옳습니다 : 반드시 따르지는 않지만 컴파일러가 그러한 수정이 발생하지 않는다는 것을 증명할 수있는 많은 경우가 있습니다.
Steve Jessop

"그러나 이것은 학습 예제에 사용되는 무한 루프가 결과적으로 어려움을 겪고 초보자 코드에서 문제를 일으킬 것임을 의미합니다. 이것이 전적으로 좋은 것이라고 말할 수는 없습니다." 최적화를 끄고 컴파일
만해

1
@ supercat : 당신이 묘사하는 것은 실제로 일어날 일이지만, 표준 초안이 요구하는 것은 아닙니다. 우리는 컴파일러가 루프가 종료 될지 모른다고 가정 할 수 없습니다. 컴파일러 루프가 종료되지 않는다는 것을 알고 있다면 원하는대로 할 수 있습니다. DS9K는 것이다 비강 악마 만드는 어떤 없이 I / O 등과 함께 무한 루프 (따라서, DS9K 해결할 수있는 문제 중단 문제.)
필립 포터에게

15

올바른 해석은 편집 한 내용이라고 생각합니다. 빈 무한 루프는 정의되지 않은 동작입니다.

나는 그것이 직관적 인 행동이라고 말하지는 않지만,이 해석은 다른 해석보다 더 의미가 있습니다. 컴파일러는 UB를 호출하지 않고 무한 루프 를 임의로 무시할 수 있습니다.

무한 루프가 UB 인 경우 종료되지 않는 프로그램은 의미가없는 것으로 간주됩니다. C ++ 0x에 따르면 의미 없습니다.

그것은 어느 정도 의미가 있습니다. 그것들은 더 이상 많은 부작용이 더 이상 발생하지 않는 특별한 경우이며 (예 :에서 아무것도 반환되지 main않음) 무한 루프를 유지해야하여 많은 컴파일러 최적화가 방해받습니다. 예를 들어 루프에 부작용이없는 경우 루프를 가로 질러 계산을 이동하는 것은 완벽하게 유효합니다. 결국 어떤 경우에도 계산이 수행되기 때문입니다. 루프가 결코 종료하지 않는 경우 우리가 있기 때문에, 우리는 그것을 가로 질러하지 안전하게 다시 정렬 코드를 할 수 있습니다 단지 작업이 실제로 프로그램이 중단되기 전에 실행 얻을하는 변경 될 수있다. 교수형 프로그램을 UB로 취급하지 않는 한, 그렇습니다.


7
"빈 무한 루프는 정의되지 않은 동작입니다"? 앨런 튜링 (Alan Turing)은 달라지기를 간절히 바라고 있지만, 무덤에서 돌면서 넘어 질 때만 가능하다.
Donal Fellows

11
@ Donal : Turing 머신에서 그 의미에 대해 아무 말도하지 않았습니다. 우리는 C ++에서 부작용이없는 무한 루프의 의미를 논의하고 있습니다. 그리고 그것을 읽었을 때 C ++ 0x는 그러한 루프가 정의되지 않았다고 말합니다.
jalf

빈 무한 루프는 어리석은 것이며 특별한 규칙을 가질 이유가 없습니다. 이 규칙은 미래에 필요하지만 즉시는 아닌 것을 계산하는 무한한 (길이가 무한한) 지속 기간의 유용한 루프를 처리하도록 설계되었습니다.
supercat

1
이것은 C ++ 0x가 임베디드 장치에 적합하지 않다는 것을 의미합니까? 거의 모든 임베디드 장치는 종료되지 않으며 큰 지방 안에서 작업을 수행합니다 while(1){...}. 그들은 심지어 while(1);워치 독 지원 리셋을 호출하기 위해 일상적으로 사용 합니다.
vsz

1
@vsz : 첫 번째 형태는 괜찮습니다. 무한 루프는 일종의 관찰 가능한 동작이있는 한 완벽하게 정의됩니다. 두 번째 형태는 더 까다 롭지 만 두 가지 매우 쉬운 방법을 생각할 수 있습니다. (1) 임베디드 장치를 대상으로하는 컴파일러는이 경우 더 엄격한 동작을 정의하도록 선택할 수 있거나 (2) 일부 더미 라이브러리 함수를 호출하는 본문을 만듭니다. . 컴파일러가 해당 함수의 기능을 모르는 경우 부작용이있을 수 있으므로 루프를 망칠 수 없다고 가정해야합니다.
jalf

8

나는 이것이 다른 유형의 쓰레드 를 참조 하는 이런 유형의 질문 의 행을 따른다고 생각한다 . 최적화는 때때로 빈 루프를 제거 할 수 있습니다.


3
좋은 질문. 그 사람 이이 단락이 컴파일러가 일으킬 수있는 문제가있는 것처럼 보입니다. 답변 중 하나의 관련 토론에서 "불행히도 정의되지 않은 동작"이라는 단어는 사용되지 않습니다. 그러나 표준에 '컴파일러는 P를 가정 할 수 있습니다'라고 표시 될 때마다 not-P 속성에 정의되지 않은 의미가 있습니다. " . 이것은 나를 놀라게한다. 이것은 위의 예제 프로그램에 정의되지 않은 동작이 있으며 아무 데서도 segfault가 될 수 있습니까?
Johannes Schaub-litb

@Johannes : "임시로 가정 할 수있다"라는 텍스트는 내가 제출해야 할 초안의 다른 곳에서는 발생하지 않으며 "가정 할 수도있다"는 몇 번만 발생합니다. 줄 바꿈에서 일치하지 않는 검색 기능으로 이것을 확인했지만 일부를 놓쳤을 수 있습니다. 필자는 저자의 일반화가 증거에 대해 보증 된 것이 확실하지 않지만 , 수학자로서 논쟁의 논리를 인정해야한다. 만약 컴파일러가 잘못된 것을 가정하면 일반적으로 무언가를 추론 할 수있다.
Steve Jessop

... 프로그램에 대한 컴파일러의 추론에서 모순을 허용하면 특히 UB에서 컴파일러가 매우 강력하게 암시합니다. 특히 모든 X의 경우 컴파일러가 프로그램이 X와 동등한 것으로 추론 할 수 있기 때문입니다. 그것을 허용하는하는 것을. 필자는 또한 UB가 의도 된 경우 명시 적으로 언급되어야하며, 의도하지 않은 경우 사양 텍스트가 잘못되어 수정되어야한다는 점에 저자의 의견에 동의합니다. 효과가없는 코드로 루프하십시오 ", 확실하지 않습니다).
Steve Jessop

@SteveJessop : 무한 루프를 포함한 모든 코드의 실행이 코드 조각이 관찰 가능한 프로그램 동작에 영향을 미칠 때까지 연기 될 수 있다고 단순히 어떻게 생각하십니까? 규칙적으로, 코드를 실행하는 데 필요한 시간은 (무한한 경우에도) "관측 가능한 부작용"이 아닙니다. 컴파일러가 특정 값을 보유한 변수없이 루프가 종료 될 수 없음을 입증 할 수 있으면 해당 값을 보유한 것으로 간주 될 수 있지만 루프가 해당 값을 보유한 상태에서 루프를 종료 할 수없는 것으로 표시 될 수도 있습니다.
supercat

@ supercat : 당신이 그 결론을 말했듯이, 나는 그것이 그것을 개선한다고 생각하지 않습니다. 루프가 객체 X와 비트 패턴 x에 대해 종료되지 않는 경우 컴파일러는 X비트 패턴 을 유지 하지 않고 루프가 종료되지 않음을 보여줄 수 있습니다 x. 그것은 사실이다. 그래서 X의미에서 UB 나쁜 같은 비트 패턴, 그리고 그의를 개최하는 것으로 간주 될 수있는 잘못에 대한 X그리고 x그것을 것입니다 신속하게 원인을 몇 가지. 그래서 나는 당신이 당신의 표준에서 더 정확해야한다고 생각합니다. 무한 루프의 "종료"에 어떤 일이 일어나는지 이야기하기가 어렵고, 유한 한 연산과 동등한 것을 보여주기는 어렵습니다.
Steve Jessop

8

관련된 문제는 컴파일러가 부작용이 충돌하지 않는 코드를 다시 정렬 할 수 있다는 것입니다. 컴파일러가 무한 루프에 대해 종료되지 않는 기계 코드를 생성하더라도 놀라운 실행 순서가 발생할 수 있습니다.

이것이 올바른 접근 방법이라고 생각합니다. 언어 사양은 실행 순서를 시행하는 방법을 정의합니다. 순서를 바꿀 수없는 무한 루프를 원하면 다음과 같이 작성하십시오.

volatile int dummy_side_effect;

while (1) {
    dummy_side_effect = 0;
}

printf("Never prints.\n");

2
@ JohannesSchaub-litb : 루프가 끝없는 지 여부에 관계없이 실행 중에 휘발성 변수를 읽거나 쓰지 않고 그렇게 할 수있는 함수를 호출하지 않으면 컴파일러는 루프의 일부를 자유롭게 연기 할 때까지 그 안에 계산 된 것에 접근하려는 첫 번째 노력. 주어진 unsigned int dummy; while(1){dummy++;} fprintf(stderror,"Hey\r\n"); fprintf(stderror,"Result was %u\r\n",dummy);첫 번째 fprintf는 실행할 수 있지만 두 번째 는 실행할 수 없습니다 (컴파일러 dummy는 두 사이의 계산을 이동할 수는 fprintf있지만 그 값을 인쇄하는 것을 지나칠 수는 없습니다).
supercat

1

"나중의 코드가 이전 코드에 의존하지 않고 이전 코드가 시스템의 다른 부분에 부작용이 없다면 컴파일러의 출력으로 문제를 가장 잘 설명 할 수 있다고 생각합니다. 전자가 루프를 포함하더라도 전자의 코드가 실제로 언제 완료되는지 여부에 관계없이 전자의 실행 이전, 이후 또는 혼합과 함께 이후 코드를 실행할 수 있습니다. 예를 들어, 컴파일러는 수 있습니다.

void testfermat (int n)
{
  int a = 1, b = 1, c = 1;
  while (pow (a, n) + pow (b, n)! = pow (c, n))
  {
    if (b> a) a ++; 그렇지 않으면 (c> b) {a = 1; b ++}; 그렇지 않으면 {a = 1; b = 1; c ++};
  }
  printf ( "결과는");
  printf ( "% d / % d / % d", a, b, c);
}

같이

void testfermat (int n)
{
  if (fork_is_first_thread ())
  {
    int a = 1, b = 1, c = 1;
    while (pow (a, n) + pow (b, n)! = pow (c, n))
    {
      if (b> a) a ++; 그렇지 않으면 (c> b) {a = 1; b ++}; 그렇지 않으면 {a = 1; b = 1; c ++};
    }
    signal_other_thread_and_die ();
  }
  else // 두번째 스레드
  {
    printf ( "결과는");
    wait_for_other_thread ();
  }
  printf ( "% d / % d / % d", a, b, c);
}

일반적으로 불합리하지는 않지만 걱정할 수도 있습니다.

  int total = 0;
  (i = 0; num_reps> i; i ++)
  {
    update_progress_bar (i);
    total + = do_something_slow_with_no_side_effects (i);
  }
  show_result (총);

될 것이다

  int total = 0;
  if (fork_is_first_thread ())
  {
    (i = 0; num_reps> i; i ++)
      total + = do_something_slow_with_no_side_effects (i);
    signal_other_thread_and_die ();
  }
  그밖에
  {
    (i = 0; num_reps> i; i ++)
      update_progress_bar (i);
    wait_for_other_thread ();
  }
  show_result (총);

한 CPU가 계산을 처리하고 다른 CPU가 진행률 표시 줄을 처리하게하면 재 작성이 효율성을 향상시킵니다. 불행하게도, 진행률 표시 줄 업데이트가 필요 이상으로 유용하지 않게됩니다.


진행률 표시 줄을 표시하는 것이 라이브러리 I / O 호출이기 때문에 진행률 표시 줄을 분리 할 수 ​​없다고 생각합니다. 최적화는 이러한 방식으로 가시적 인 동작을 변경해서는 안됩니다.
Philip Potter

@ 필립 포터 (Philip Potter) : 느린 일과에 부작용이 있다면 그것은 사실 일 것입니다. 이전 예제에서는 그렇지 않은 경우 의미가 없으므로 변경했습니다. 스펙에 대한 나의 해석은 시스템이 느린 코드의 실행이 그 효과 (실행하는 데 걸리는 시간이 아닌)가 보일 때까지, 즉 show_result () 호출까지 연기 할 수 있다는 것입니다. 진행률 바코드가 누계를 사용하거나 최소한 척하는 경우 느린 코드와 동기화됩니다.
supercat

1
이것은 0에서 100까지 빠르게 진행 한 다음 나이가
걸리는

0

무한 루프 인 경우 사소한 경우에 컴파일러에서 결정할 수 없습니다.

다른 경우에는 옵티마이 저가 코드에 대한 더 복잡한 클래스에 도달 할 수 있습니다 (예 : O (n ^ 2)이고 최적화 후 O (n) 또는 O (1)을 얻음).

따라서 무한 루프를 C ++ 표준으로 제거하는 것을 허용하지 않는 규칙을 포함하면 많은 최적화가 불가능합니다. 그리고 대부분의 사람들은 이것을 원하지 않습니다. 나는 이것이 당신의 질문에 대한 답이라고 생각합니다.


다른 것 : 나는 당신이 아무것도하지 않는 무한 루프가 필요한 유효한 예를 본 적이 없습니다.

내가 들었던 한 가지 예는 실제로 해결해야 할 추한 해킹입니다. 리셋을 트리거하는 유일한 방법은 장치를 정지시켜 워치 독이 자동으로 다시 시작하는 임베디드 시스템에 관한 것입니다.

아무것도하지 않는 무한 루프가 필요한 유효하고 좋은 예를 알고 있다면 알려주십시오.


1
무한 루프를 원할 수있는 위치의 예 : 성능상의 이유로 잠들기를 원하지 않고 모든 코드가 한두 번 중단 된 임베디드 시스템?
JCx

표준 C에서 @JCx 인터럽트는 메인 루프가 점검하는 플래그를 설정해야하므로 플래그가 설정되는 경우 메인 루프는 관찰 가능한 동작을 갖습니다. 인터럽트에서 상당한 코드를 실행하는 것은 이식 불가능합니다.
MM

-1

비 휘발성, 동기화되지 않은 변수를 통해 다른 스레드와 상호 작용한다는 사실을 제외하고는 무한한 루프를 지적 할 가치가 있다고 생각합니다. 이제 새로운 컴파일러에서 잘못된 동작을 일으킬 수 있습니다.

다른 말로하면, 당신의 전역을 휘발성으로 만들고 인수는 포인터 / 참조를 통해 그러한 루프에 전달됩니다.


그들이 다른 스레드와 상호 작용하는 경우, 스레드를 휘발성으로 만들거나 원자로 만들거나 잠금으로 보호해서는 안됩니다.
BCoates

1
Thjs는 끔찍한 조언입니다. 그것들을 만드는 volatile것도 필요하거나 충분하지 않으며 성능을 크게 저하시킵니다.
David Schwartz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.