2017-05-24 2 views
1

golang 채널과 C++ tbb 동시 대기열 성능을 비교하기위한 테스트를 수행했습니다. 설치 프로그램이 8 개이고 판독기가 서로 다릅니다. 결과 golang이 C++ 버전보다 훨씬 빠르다는 것을 보여줍니다 (대기 시간과 전체 send/recv 속도). 또는 내 코드의 실수? , ++ 218,622왜 golang 채널이 인텔보다 훨씬 빠름 tbb concurrent_queue 8 생산자 1 소비자와 테스트 할 때

package main 

import (
    "flag" 
    "time" 
    "fmt" 
    "sync" 
    "runtime" 
) 

var (
    producer = flag.Int("producer", 8, "producer") 
    consumer = flag.Int("consumer", 1, "consumer") 
    start_signal sync.WaitGroup 
) 

const (
    TEST_NUM = 1000000 
) 

type Item struct { 
    id int 
    sendtime int64 
    recvtime int64 
} 

var g_vec[TEST_NUM] Item 

func sender(out chan int, begin int, end int) { 
    start_signal.Wait() 
    runtime.LockOSThread() 
    println("i am in sender", begin, end) 
    for i:=begin; i < end; i++ { 
     item := &g_vec[i] 
     item.id = i 
     item.sendtime = time.Now().UnixNano()/1000 
     out<- i 
    } 
    println("sender finish") 
} 

func reader(out chan int, total int) { 
    //runtime.LockOSThread() 
    start_signal.Done() 
    for i:=0; i<total;i++ { 
     tmp :=<- out 
     item := &g_vec[tmp] 
     item.recvtime = time.Now().UnixNano()/1000 
    } 
    var lsum int64 = 0 
    var lavg int64 = 0 
    var lmax int64 = 0 
    var lstart int64 = 0 
    var lend int64 = 0 
    for _, item:= range g_vec { 
     if lstart > item.sendtime || lstart == 0 { 
      lstart = item.sendtime 
     } 
     if lend < item.recvtime { 
      lend = item.recvtime 
     } 

     ltmp := item.recvtime - item.sendtime 
     lsum += ltmp 
     if ltmp > lmax { 
      lmax = ltmp 
     } 
    } 
    lavg = lsum/TEST_NUM 
    fmt.Printf("latency max:%v,avg:%v\n", lmax, lavg) 
    fmt.Printf("send begin:%v,recv end:%v, time:%v", lstart, lend, lend-lstart) 
} 

func main() { 
    runtime.GOMAXPROCS(10) 
    out := make (chan int,5000) 

    start_signal.Add(1) 
    for i:=0 ;i<*producer;i++ { 
     go sender(out,i*TEST_NUM/(*producer), (i+1)*TEST_NUM/(*producer)) 
    } 
    reader(out, TEST_NUM) 
} 

C 만 주요부

: 시간 1,495,593,677,901,854 : 1,505, 평균 : 1073 보내기 시작 : 1495593677683232, RECV 단부

golang 결과, 유닛

레이턴시 맥스 마이크로

concurrent_bounded_queue g_queue; 최대 : 558,301, 최소 3, 평균 : 403,741 (단위는 마이크로 초이다) 시작 : 1495594232068580 최종 : 1495594233497618 길이 : 1,429,038

static void sender(int start, int end) 
{ 
    for (int i=start; i < end; i++) 
    { 
     using namespace std::chrono; 
     auto now = system_clock::now(); 
     auto now_ms = time_point_cast<microseconds>(now); 
     auto value = now_ms.time_since_epoch(); 
     int64_t duration = value.count(); 

     Item &item = g_pvec->at(i); 
     item.id = i; 
     item.sendTime = duration; 
     //std::cout << "sending " << i << "\n"; 
     g_queue.push(i); 
    } 
} 


static void reader(int num) 
{ 
    barrier.set_value();  
    for (int i=0;i<num;i++) 
    { 
     int v; 
     g_queue.pop(v); 
     Item &el = g_pvec->at(v); 

     using namespace std::chrono; 
     auto now = system_clock::now(); 
     auto now_ms = time_point_cast<microseconds>(now); 
     auto value = now_ms.time_since_epoch(); 
     int64_t duration = value.count(); 

     el.recvTime = duration; 
     //std::cout << "recv " << item.id << ":" << duration << "\n"; 
    } 
    // caculate the result. 
    int64_t lmax = 0; 
    int64_t lmin = 100000000; 
    int64_t lavg = 0; 
    int64_t lsum = 0; 
    int64_t lbegin = 0; 
    int64_t lend = 0; 
    for (auto &item : *g_pvec) 
    { 
     if (item.sendTime<lbegin || lbegin==0) 
     { 
      lbegin = item.sendTime; 
     } 
     if (item.recvTime>lend) 
     { 
      lend = item.recvTime; 
     } 

     lsum += item.recvTime - item.sendTime; 
     lmax = max(item.recvTime - item.sendTime, lmax); 
     lmin = min(item.recvTime - item.sendTime, lmin); 
    } 
    lavg = lsum/num; 
    std::cout << "max:" << lmax << ",min:" << lmin << ",avg:" << lavg << "\n"; 
    std::cout << "start:" << lbegin << ",end:" << lend << ",length:" << lend-lbegin << "\n"; 
} 

DEFINE_CODE_TEST(plain_queue_test) 
{ 
    g_pvec = new std::vector<Item>(); 
    g_pvec->resize(TEST_NUM);  

    auto sf = barrier.get_future().share(); 

    std::vector<std::thread> vt; 
    for (int i = 0; i < SENDER_NUM; i++) 
    { 
     vt.emplace_back([sf, i]{  
      sf.wait(); 
      sender(i*TEST_NUM/SENDER_NUM, (i + 1)*TEST_NUM/SENDER_NUM); 
     }); 
    } 


    std::cout << "create reader\n"; 
    std::thread rt(bind(reader, TEST_NUM)); 
    for (auto& t : vt) 
    { 
     t.join(); 
    }  
    rt.join(); 
} 

golang cpu usage

cpp cpu usage

(적색 수단 cpu 스핀/오버 헤드, 녹색 유휴) vtune cpu 그래프에서 나는 골란 채널이 더 효율적인 뮤텍스를 가지고 있다고 느꼈다. (예 : goroutine 대 C++ 뮤텍스를 자고 자하는 시스템 호출이 필요합니까?) enter image description here

많은 시간 회전 지출 내가 TBB 큐가 잠을하지 않는 것으로 결론을 내릴 수의 VTune 트레이스에서

enter image description here

+0

먼저 실제와 같은 실제 벤치마킹 도구를 사용하는 것이 좋습니다. –

+0

검사를 통해 두 프로그램 모두에서 동일한 작업을 수행하지 않을 것입니다. 몇 가지 예를 들자면, C++ 버전은 지속 기간 캐스트의 정수 나눗셈을 가지고 있으며, 변수는 동일한 수준의 간접 참조를 가지고 있지 않습니다. 이는 궁극적으로는 관련성이없는 것으로 판명 될 수도 있지만 실제로 벤치마킹하기 전까지는 알 수 없습니다. –

+0

생산자/소비자에게 실제/시뮬레이션 된 계산 작업을 추가해보십시오. goroutines 때문에 단일 스레드에서 실행되므로 Go 버전에서는 실제 동기화가 수행되지 않을 수 있습니다. 또한 TBB의 대기열은 MPMC이고 Go는 MPSC 모델에 대해이를 최적화 할 수 있습니다. – Anton

답변

2

, 이동 버전 OS 동기화에 잠자는 스레드를 나타내는 밝은 녹색 영역이있다. 왜 더 나은가요? 대개는 시스템에 초과 가입이 있음을 나타내므로 OS를 통한 통신은 비용을 지불합니다. 그래서 과다 가입합니까? 그렇다면, 이것은 대응 라이브러리의 철학에 맞는 오히려 예상되는 행동이라고 말하고 싶습니다. TBB는 계산 병렬 처리를 위해 설계되었으므로 과다 신청을 방지하면서 IO 작업을 잘 처리하지 못합니다. Go는 IO 작업을 위해 정확하게 설계되었으므로 병렬 번호 처리에 비우호적 인 스케줄러의 FIFO 정책과 함께 내장 된 동시성을 구현합니다. Oversubscription은 IO 병렬 작업에 영향을 미치거나 심지어는 종료하는 동안 IO 작업에 권장됩니다.

관련 문제