2010-07-26 4 views
7

작지만 자주 사용되는 함수 객체가 있습니다. 각 스레드는 자체 복사본을 가져옵니다. 모든 것이 정적으로 할당됩니다. 사본은 전역 또는 정적 데이터를 공유하지 않습니다. 이 개체를 허위 공유로부터 보호해야합니까?거짓 공유 및 스택 변수

감사합니다. EDIT : Boost.Threads를 사용하는 장난감 프로그램입니다. 필드 데이터에 대해 허위 공유가 가능합니까?

#include <boost/thread/thread.hpp> 

struct Work { 
    void operator()() { 
     ++data; 
    } 

    int data; 
}; 

int main() { 
    boost::thread_group threads; 
    for (int i = 0; i < 10; ++i) 
     threads.create_thread(Work()); 
    threads.join_all(); 
} 
+0

코드가 더 잘 작동합니다. 함수 객체가'정적'데이터를 가지고 있다면, 모든 쓰레드는 그 데이터를 공유 할 것입니다. – GManNickG

+0

"각 스레드는 자신의 사본을 얻습니다"및 "정적으로 할당 됨"이 무엇을 의미하는지 정확하게 말해야한다고 생각하십시오. 스레드가 서로의 사본을 사용합니까? – Elemental

+0

@ Elemental : 일부 컴파일러는 TLS- 스레드 로컬 저장소를 사용할 수 있습니다. 이는 느리지 만 정적으로 스레드를 안전하게 할당 할 수 있음을 의미합니다. – Puppy

답변

6

스레드 간 거짓 공유는 2 개 이상의 스레드가 동일한 캐시 라인을 사용하는 경우입니다.

예. :

struct Work { 
    Work(int& d) : data(d) {} 
    void operator()() { 
     ++data; 
    } 

    int& data; 
}; 

int main() { 
    int false_sharing[10] = { 0 }; 
    boost::thread_group threads; 
    for (int i = 0; i < 10; ++i) 
     threads.create_thread(Work(false_sharing[i])); 
    threads.join_all(); 

    int no_false_sharing[10 * CACHELINE_SIZE_INTS] = { 0 }; 
    for (int i = 0; i < 10; ++i) 
     threads.create_thread(Work(no_false_sharing[i * CACHELINE_SIZE_INTS])); 
    threads.join_all(); 
} 

첫 번째 블록의 스레드는 거짓 공유로 고통받습니다. 두 번째 블록의 스레드는 없습니다 (덕분에 CACHELINE_SIZE).

스택의 데이터는 다른 스레드와 항상 멀리 떨어져 있습니다. (예 : 창 아래, 최소 두 페이지 이상).

함수 개체의 정의에서 Work의 인스턴스가 힙에 만들어지고이 힙 공간이 스레드 내부에서 사용되므로 false 공유가 나타날 수 있습니다.

이렇게하면 여러 개의 Work 인스턴스가 인접 해져 캐시 라인을 공유 할 수 있습니다.

하지만 데이터가 외부로 절대 전달되지 않아 잘못된 공유가 불필요하게 유도되므로 샘플이 이해가되지 않습니다.

가장 쉬운 방법은 이와 같은 문제를 방지하기 위해 '공유'데이터를 스택에 로컬로 복사 한 다음 스택 복사본에서 작업하는 것입니다. 작업이 끝나면 출력 var로 다시 복사하십시오.

예컨대 :

struct Work { 
    Work(int& d) : data(d) {} 
    void operator()() 
    { 
     int tmp = data; 
     for(int i = 0; i < lengthy_op; ++i) 
      ++tmp; 
     data = tmp; 
    } 

    int& data; 
}; 

이 공유로 모든 문제를 방지 할 수 있습니다.

+0

데이터가 허위 공유의 영향을받을 수 있다고 말하고 있습니까? 필자의 경우 함수의 스택에 복사하는 것은 도움이되지 않습니다. 왜냐하면 함수 자체를 자주 호출해야하고 호출 당 한 번만 데이터를 사용해야하기 때문입니다. – user401947

+0

함수를 자주 호출해야하는 경우 매번 스레드를 만드는 것이 의미가 없습니다. 새로운 스레드에서 많은 작업을 수행하거나 스레드 생성/제거를위한 사이클을 구울 수 있습니다. 그리고 나중의 경우 스레드의 엄청난 비용으로 허위 공유 비용을 줄입니다. – Christopher

+0

그럼에도 불구하고. 작업을 위해 스택에 데이터를 복사 할 수없는 경우 '작업'을 최소한 CACHLINE_SIZE만큼 길게 만드십시오. 2 바이트를 잃어 버리지 만 허위 공유 문제가 발생하지 않도록 할 수 있습니다. – Christopher

0

나는 세부 사항을 완전히 안전 느끼지 않는다 ', 그러나 여기에서 필자의 :

부스트 create_thread이 참조를 기대하고 있기 때문에 (1) 귀하의 간단한 예는 고장, 임시을 통과은.

(2) vector<Work>을 각 스레드마다 하나씩 사용하거나 순차적으로 메모리에 저장하는 경우 잘못된 공유가 발생합니다.

+0

(1) 아니오, 깨지지 않았습니다. create_thread는 값으로 인수를 승인합니다. 당신이 나를 믿지 않는다면 선언을 확인하십시오. (2) 나는 각 스레드가 자신의 사본을 얻는다는 것을 분명히 말했습니다. 코드를 확인하십시오. 함수 객체는 값에 의해 전달됩니다. – user401947

+0

입니다. 작업이 대상 스택에 복사되지 않습니다. 'create_thread'의 컨텍스트에서 'newed'되고 대상 스택으로 전송되는 (공유 -) 포인터 만입니다. 데이터는 포인터에 의해서만 참조됩니다. (나는 이것을 데이터 멤버에게 thread_id를 할당하고 operator() 호출의 값을보고 테스트했다.) – Christopher

+0

(1) 우리는 다음과 같이 이야기했다 :'thread * create_thread (const boost :: function0 & threadfunc); '? 그건 내가 참조를 확인하려고 할 때 찾은 것입니다 – peterchen

2

나는 약간의 연구를했는데 거짓 공유에 은색 총알 해결책이없는 것 같습니다. 여기 제가 생각해내는 것이 있습니다 (크리스토퍼 덕분에) : 1) 사용하지 않거나 자주 사용하지 않는 재료로 양쪽면의 데이터를 채 웁니다. 2) 데이터를 스택에 복사하고 모든 노력이 끝나면 다시 복사하십시오. 3) 캐시 정렬 메모리 할당을 사용하십시오.