2012-10-14 2 views
3

이것은 이상한 리눅스 버릇이 될 수도 있지만 매우 이상한 행동을하고 있습니다.비동기의 수수께끼 같은 행동

다음 코드는 동기화 된 버전의 합계를 비동기 버전과 비교해야합니다. 하나의 스레드 (하나의 코어 만 사용됨)로 프로그램을 관찰하면서 성능 향상 (캐싱이 아니라 코드를 두 개의 개별 프로그램으로 분할하는 경우에도 발생합니다)이 표시됩니다.

strace에는 스레드 활동이 표시되지만 top 복제본과 같은 모니터링 도구에는 사용 된 코어가 하나만 표시됩니다.

두 번째 문제점 저는 스폰 비율을 증가 시키면 메모리 사용량이 폭발한다는 것입니다. 스레드의 메모리 오버 헤드는 얼마입니까? 5000 스레드로 ~ 10GB 메모리 사용량을 얻습니다.

#include <iostream> 
#include <random> 
#include <chrono> 
#include <future> 
using namespace std; 


long long sum2(const vector<int>& v, size_t from, size_t to) 
{ 
    const size_t boundary = 5*1000*1000; 

    if (to-from <= boundary) 
    { 
     long long rsum = 0; 
     for (;from < to; from++) 
     { 
      rsum += v[from]; 
     } 
     return rsum; 
    } 
    else 
    { 
     size_t mid = from + (to-from)/2; 
     auto s2 = async(launch::async,sum2,cref(v),mid,to); 

     long long rsum = sum2(v,from,mid); 
     rsum += s2.get(); 
     return rsum; 
    } 
} 

long long sum2(const vector<int>& v) 
{ 
    return sum2(v,0,v.size()); 
} 

long long sum(const vector<int>& v) 
{ 
    long long rsum = 0; 
    for (auto i : v) 
    { 
     rsum += i; 
    } 

    return rsum; 
} 

int main() 
{ 
    const size_t vsize = 100*1000*1000; 

    vector<int> x; 
    x.reserve(vsize); 

    mt19937 rng; 
    rng.seed(chrono::system_clock::to_time_t(chrono::system_clock::now())); 

    uniform_int_distribution<uint32_t> dist(0,10); 

    for (auto i = 0; i < vsize; i++) 
    { 
     x.push_back(dist(rng)); 
    } 

    auto start = chrono::high_resolution_clock::now(); 
    long long suma = sum(x); 
    auto end = chrono::high_resolution_clock::now(); 

    cout << "Sum is " << suma << endl; 
    cout << "Duration " << chrono::duration_cast<chrono::nanoseconds>(end - start).count() << " nanoseconds." << endl; 

    start = chrono::high_resolution_clock::now(); 
    suma = sum2(x); 
    end = chrono::high_resolution_clock::now(); 

    cout << "Async sum is " << suma << endl; 
    cout << "Async duration " << chrono::duration_cast<chrono::nanoseconds>(end - start).count() << " nanoseconds." << endl; 

    return 0; 
} 
+1

10GB 가상 또는 실제? 가상 메모리 사용은 쉽게 폭발 할 수 있지만 실제 메모리 사용량은 그다지 높지 않아야합니다. – nneonneo

+0

@nneonneo 예, 가상은 50GB와 같습니다 .-D –

+0

64 비트 운영 체제에서는 가상 메모리가 부족하지 않습니다. 따라서 사용 빈도를 줄이거 나 사용량을 추적하는 데 아무런 요점이 없습니다. (적어도, 당신이 결핵에 빠지기 전까지는.) –

답변

1

동시에 작업을 수행하는 스레드 간의 겹침이 너무 짧아 눈에 띄기 때문에 하나의 코어가 사용되는 것을 볼 수 있습니다. 메모리의 연속 영역에서 5mln 값을 합산하면 현대 하드웨어에서 매우 빨라야하므로 부모가 합산을 마칠 때까지 아동이 간신히 시작되었을 수 있으며 부모는 자녀의 결과를 기다리는 시간의 대부분 또는 모든 시간을 보낼 수 있습니다. 오버랩이 눈에 띄는 지 확인하기 위해 작업 단위를 늘리려고 했습니까?

향상된 성능 : 너무 작은 작업 단위로 인해 스레드간에 0 겹침이 있더라도 다중 스레드 버전은 추가 L1 캐시 메모리의 이점을 여전히 볼 수 있습니다. 이러한 테스트에서 메모리는 병목 현상이 될 수 있으며 순차 버전은 하나의 L1 캐시 만 사용하고 멀티 스레드 버전은 코어가있는만큼 사용할 것입니다.

+0

코드를 실행 해 보셨습니까? 이것은 실제로 이것이 문제가되지 않습니다. 스레드가 너무 많으면 문제가 될 수 있지만 실제로 4로 줄이면 실제로 성능이 향상되지 않습니다. 심지어 사전에 블록을 최적으로 스케쥴하기 위해 코드를 다시 작성했습니다. – sehe

+0

하지만 이러한 숫자를 살펴보십시오. http://www.quora.com/What-are-the-numbers-that-every-computer-engineer-should-know-according-to-Jeff-Dean 이런 종류의 문제에 대해서는, 프로그램은 순차적 인 주 메모리에서 입력을 읽는 데 대부분의 시간을 소비합니다 (DRAM은 동시 액세스를 허용하지 않습니다). –

1

인쇄되는 시간을 확인 했습니까? 내 컴퓨터에서 직렬 시간은 -O2에서 1 초 미만이지만 병렬 총 시간은 몇 배 더 빠릅니다. 따라서 일반적으로 초당 한 번만 새로 고침하기 때문에 CPU 사용량이 "최고"와 같은 항목을 등록하기에 충분하지 않을 수 있습니다.

스레드 당 횟수를 줄임으로써 스레드 수를 늘리면 스레드 관리의 오버 헤드가 효과적으로 증가합니다. 5000 개의 쓰레드가 활성화되어 있다면, 당신의 작업은 추가 메모리에서 5000 * min-thread-stack-size를 차지하게됩니다. 내 컴퓨터에서 20Gb!

원본 컨테이너의 크기를 늘리면 어떨까요? 병렬 섹션의 시간이 오래 걸리면 해당 병렬 CPU 사용이 표시됩니다. 그러나 합계 수는 이며, 난수를 생성하는 데 걸리는 시간은 숫자를 함께 더하는 데 걸리는 시간보다 한 두 배 정도 길 수 있습니다.