7

컴파일러가 수행 할 수있는 최적화에 관한 질문이 있습니다.컴파일러 최적화, 스레드 안전?

typedef struct test 
{ 
    short i; 
}    s_test; 

int function1(char *bin) 
{ 
    s_test foo; 

    lock(gmutex); 
    foo.i = *(int*)bin * 8; 
    unlock(gmutex); 

    sleep(5); 
    // 
    // Here anything can happen to *bin in another thread 
    // an inline example here could be: *(volatile int *)bin = 42; 
    // 

    int b = foo.i + sizeof(char*); 

    return (b > 1000); 
} 

컴파일러 이제까지

return ((*(int*)bin * 8 + sizeof(char*)) > 1000); 

와 마지막 라인을 대체 할 수 사용하는 경우 될 것 같지 않았다

아래의 코드 (이 예입니다) 자체에 대한 말 것 -O2 또는 -O3를 gcc 4.4와 함께 사용하지만 다른 컴파일러 및 다른 컴파일 플래그의 경우 일 수 있습니까?

+5

표현식으로 변수를 바꾸는 것이 정확히 최적화가 아닙니까? 그렇습니까? – EJP

+1

wrt 컴파일러 최적화 및 스레드 안전성에 대한 내용은 http : // stackoverflow를 참조하십시오.co.kr/questions/2001913/c0x-memory-model-and-speculative-loads-stores 및 특히 그 안에 링크 된 문서를 저장합니다. – janneb

+0

* bin은 gmutex를 가리킬 수있는 _ALL_ 메모리 오브젝트에 대한 모든 액세스를 보호하지 않는 한 뮤텍스 중에 발생할 수 있습니다. 뮤텍스에 의해 보호되는 유일한 것은 로컬 변수 foo인데 당신이 원하지 않았을 수도 있습니다. – slartibartfast

답변

5

나는 컴파일러가 그런 종류의 최적화를 할 것이라고 생각하지 않는다.

unlock(gmutex) 

이 빈 가리키는 그 값을 가정 할 수 컴파일러가 잠금 해제 기능 변경 여부됩니다 기능입니다.

예를 들어 bin은 지구본에서 가져온 것일 수 있습니다. 따라서 bin에 대한 최적화는 함수 호출을 넘을 수 없습니다. 당신이 선언 된 것보다 다른 유형을 통해 bin을 읽기 때문에

+1

컴파일러는 확실히이 최적화를 수행 할 수 없습니다. 그렇지 않으면 잠금 기능을 구현하는 안전한 방법이 없습니다. – ugoren

+2

@ugoren, 잠금 및 스레드 모델에는 C11 만 제공됩니다. 이러한 관점에서 동기 부여와 관련하여 C 표준 자체에 의존하기 전에 모든 배팅이 해제됩니다. C11 이전에 C로 스레드 프로그래밍을하면 특별한주의가 필요합니다. 이 예제에서 수행 된 것처럼 특히 앨리어싱은 좋은 생각이 아닙니다. 여기에'bin'이'char * '이래로 아마 괜찮을 것입니다. 그러나 제 생각에 Dpp는 C의이 부분을 아주 잘 제어하지 못할 것입니다. –

+0

"함수들 사이에 별칭이 없다"라고 설정되어 있지 않으면 (실제로 지역 변수를 변경할 수있는 함수가 있음). 다행히 MSDN에서이 옵션에 대해 더 이상 설명하지 않습니다. – selbie

4

귀하의 예는 불필요하게 복잡하다. 앨리어싱 규칙은 매우 복잡합니다. char도 특별합니다. 나는 그것에 대해 언급하지 않겠습니다.

선언문이 int* bin이라면 컴파일러는 함수 호출에서 명령문을 재정렬 할 권한이 없으므로 소위 시퀀스 포인트를 형성합니다. unlock에 대한 호출 전후에 *bin의 값이 다를 수 있으므로 호출 후 값을로드하려면 이 있어야합니다.

편집 : 슬라 티 바트 패스트 의해 명시된 바와 같이,이 인수는 unlock 인 (또는 포함 된) 함수 호출과 컴파일러에 의한 일련의 동작으로 해석 단지 매크로없는 것이 필수적이다.

+0

다시 가로 챌 수 미안 : unlock()이 단지 문장 일지라도 ";" 커튼 앞에서 잡아 당기는 함수 호출과 같은 시퀀스 포인트입니다.이 경우에는 도움이되지 않습니다. 최대 C99까지의 표준과 함께 다중 스레드 C의 복잡성에 대한 느낌을 얻으려면 comp.lang.c 및 comp.std.c 아카이브를 살펴보십시오. – slartibartfast

1

직접 답장에서 말한 것처럼 뮤텍스는 IMHO를 보호하지 않습니다. * bin이 가리키는 char 배열은 int 접근 중에 수정 될 수 있으므로 여러분의 컴퓨터에 따라서는 접근하고자하는 메모리에 일관된 뷰를 얻지 못할 수도 있습니다. 질문으로 돌아 가기 : 컴파일러는 소스 코드를 구상 한 시퀀스로 변환하지 않지만 실제로는 소스 코드의 방식대로 작동하는 기계어를 생성 할 수 있습니다. 함수 잠금, 잠금 해제 및 절전 (어쨌든 매크로 인 것처럼 보임)을 검사 할 수 있고 관련 메모리 위치에 부작용이 없다는 것을 추론 할 수 있고 구현 정의 된 정의가 없습니다. 임시 ("표준"이이 용어를 사용하지 않지만 "캐시 된") 값을 반환하는 sleep()은 유효하지 않은 값을 지정한 다음 명령과 같은 명령 시퀀스를 생성 할 수 있습니다. C (C99까지)는 본질적으로 단일 스레드이며 컴파일러는 이상적인 가상 C 머신에서 코드가 "있는 것처럼 동작"하는 한 원하는 최적화 전략을 적용 할 수 있습니다. Jens가 언급 한 시퀀스 포인트는 단일 스레드 조건에서 정확성에 영향을 미치지 않습니다. 컴파일러는 전체 함수 중에 foo를 레지스터에 유지하거나 foo.i에 * bin이 가리키는 메모리 위치를 별칭으로 지정할 수 있으므로이 코드는 본질적으로 위험합니다 (대부분의 컴파일러에서이 동작이 나타나지 않을 것이라고 생각합니다).

+0

'unlock' 함수가 함수 호출이 아니라 컴파일러가 알고있는 일련의 연산으로 확장되는 매크로라고 가정한다면 당신이 맞습니다. 나는 다른 케이스를 가정하고 있었는데, 이것은 사실 함수 호출이었다. –

+2

그럼에도 불구하고 이것은 당신을 구해주지 않을 것입니다. 컴파일러가 함수를 살펴볼 수 있다면 컴파일러는 함수를 매우 잘 최적화 할 수 있습니다. foo를 자동으로 사용하는 것은이 예제에서 다소 불행한 전제입니다. – slartibartfast

+0

'lock'과'unlock'이 버그가 없다면 문제가 없습니다. 그것들을 매크로 나 같은 범위에있는 함수로 구현하면 순서 재 지정을 어떻게 든 방지 할 수 없으므로 버그가있는 잠금 프리미티브를 작성하는 미묘한 방법 중 하나입니다. 또한이 메모리가 액세스 될 때마다 잠금이 유지되면 잠금은'* bin '에 대한 액세스를 보호합니다. 한 곳에서 잠그는 것은 쓸모가 없습니다. – ugoren