2017-04-22 10 views
6

2 개의 유사한 프로그램의 성능을 보여주는 꽤 많은 프로그램이 있는데, 둘 다 계산을하기 위해 2 개의 스레드를 사용합니다. 아주 천천히, 나는 (나는 심지어 느린 것으로 예상) 동적으로 생성 할 수 OBJ를 수정 한 것전역 변수를 사용하는 C++은 pthread를 사용할 때 포인터보다 100 % 느립니다.

#include<pthread.h> 
#include<stdlib.h> 
struct M{ 
    long a; 
    long b; 
}obj; 
size_t count=2000000000; 
void* addx(void*args){ 
    long*pl=(long*)args; 
    for(size_t i=0;i<count;++i) 
     (*pl)*=i; 
    return NULL; 
} 
int main(int argc,char*argv[]){ 
    pthread_t tid[2]; 
    pthread_create(&tid[0],NULL,addx,&obj.a); 
    pthread_create(&tid[1],NULL,addx,&obj.b); 
    pthread_join(tid[0],NULL); 
    pthread_join(tid[1],NULL); 
    return 0; 
} 

clang++ test03_threads.cpp -o test03_threads -lpthread -O2 && time ./test03_threads 

real 0m3.626s 
user 0m6.595s 
sys 0m0.009s 

: 핵심 차이는 하나가 다른 아래로, "새로운"객체를 사용, 전역 변수를 사용한다는 것입니다 :

#include<pthread.h> 
#include<stdlib.h> 
struct M{ 
    long a; 
    long b; 
}*obj;//difference 1 
size_t count=2000000000; 
void* addx(void*args){ 
    long*pl=(long*)args; 
    for(size_t i=0;i<count;++i) 
     (*pl)*=i; 
    return NULL; 
} 
int main(int argc,char*argv[]){ 
    obj=new M;//difference 2 
    pthread_t tid[2]; 
    pthread_create(&tid[0],NULL,addx,&obj->a);//difference 3 
    pthread_create(&tid[1],NULL,addx,&obj->b);//difference 4 
    pthread_join(tid[0],NULL); 
    pthread_join(tid[1],NULL); 
    delete obj;//difference 5 
    return 0; 
} 

clang++ test03_threads_new.cpp -o test03_threads_new -lpthread -O2 && time ./test03_threads_new 

real 0m1.880s 
user 0m3.745s 
sys 0m0.007s 

놀랍도록 이전 버전보다 100 % 빠릅니다. 나는 또한 g ++을 리눅스에서 시도했다. 그러나 이것을 설명하는 방법? 나는 obj가 전역 변수라는 것을 알고 있지만 * obj는 여전히 전역 변수이며 동적으로 생성됩니다. 핵심 차이점은 무엇입니까?

+0

[test1] (http://ideone.com/jDOQ0b) (3.54s) 및 [test2] (http://ideone.com/C9R9ja) (3.55s)를 재현 할 수 없습니다. 또한 .cpp 경로 앞에'-O2'를두면 안됩니까? – VTT

+0

허위 공유와 관련이 있을지 모르지만 두 구현에서 허위 공유가 문제가 될 것으로 예상됩니다. – Unimportant

+0

실행 파일이 32 비트 또는 64 비트입니까? –

답변

1

저는 이것이 중요하지 않은 제안 때문에 거짓 공유 때문에 실제로 존재한다고 생각합니다.

왜 그런 차이가 있습니까?

변수가 count이기 때문에! 이것은 변수이고 size_t의 기본 유형은 long 일 수 있으므로 컴파일러에서는 plcount을 가리킬 수 있으므로이를 최적화 할 수 없습니다. countint이면 엄격한 별칭 지정 규칙 때문에 컴파일러에서 최적화 할 수 있습니다 (또는 간단히 const size_t 일 수 있음).

따라서 생성 된 코드는 매번 루프에서 count을 읽어야합니다.

첫 번째 예에서 두 글로벌 변수는 모두 countobj입니다. 따라서 링커가 이러한 변수를 동일한 캐시 행에 넣을 가능성이 높습니다. 따라서 obj.a 또는 obj.b으로 작성하면 count의 캐시 라인이 무효화됩니다. 따라서 CPU는 count의 읽기를 동기화해야합니다.

두 번째 예에서 obj은 힙에 할당되어 있으므로 주소는 count에서 충분히 떨어져 있으므로 동일한 캐시 라인을 차지하지 않습니다. count에 동기화가 필요하지 않습니다.