2014-05-23 47 views
1

我有一個多線程套接字程序。我使用boost線程池(http://threadpool.sourceforge.net/)執行任務。我在threadpool中爲每個線程創建一個TCP客戶端套接字。每當我發送大量的數據,比如說500KB(消息大小),吞吐量就會大大降低。我檢查我的代碼爲:如何測量和修復上下文切換瓶頸?

1)等待可能導致上下文切換 2)鎖定/互斥

例如,500KB消息被劃分爲多個行和我發送通過使用插座的每一行: :send()。

typedef std::list<std::string> LinesListType; 
// now send the lines to the server 
for (LinesListType::const_iterator it = linesOut.begin(); 
     it!=linesOut.end(); 
     ++it) 
{ 
    std::string line = *it; 
    if (!line.empty() && '.' == line[0]) 
    { 
     line.insert(0, "."); 
    } 

    SendData(line + CRLF); 
} 

的SendData:

void SendData(const std::string& data) 
{ 
    try 
    { 
     uint32_t bytesToSendNo = data.length(); 
     uint32_t totalBytesSent = 0; 

     ASSERT(m_socketPtr.get() != NULL) 
     while (bytesToSendNo > 0) 
     { 
      try 
      { 
       int32_t ret = m_socketPtr->Send(data.data() + totalBytesSent, bytesToSendNo); 

       if (0 == ret) 
       { 
        throw; 
       } 

       bytesToSendNo -= ret; 
       totalBytesSent += ret; 
      } 
      catch() 
      { 
      } 
     } 
    } 
    catch() 
    { 

    } 
} 

發送方法在客戶端套接字:因爲::選擇

int Send(const char* buffer, int length) 
{ 
    try 
    { 
     int bytes = 0; 
     do 
     { 
      bytes = ::send(m_handle, buffer, length, MSG_NOSIGNAL); 
     } 
     while (bytes == -1 && errno == EINTR); 

     if (bytes == -1) 
     { 
      throw SocketSendFailed(); 
     } 

     return bytes; 

    } 
    catch() 
    { 

    } 
} 

調用::選擇()之前發送引起的上下文切換能阻斷。對共享互斥鎖持有鎖會導致並行線程等待並切換上下文。這影響了性能。

是否有避免上下文切換,特別是在網絡編程中的最佳做法?我花了至少一個星期的時間試圖找出各種不用運氣的工具(vmstat,valgrind中的callgrind)。 Linux上的任何工具都有助於衡量這些瓶頸?

+0

爲什麼你認爲上下文切換和/或鎖/互斥是問題?這500KB,是每秒?是來自一個客戶端的加載還是來自多個客戶端的聚合加載?另外,沒有代碼:( –

+0

> Linux上的任何工具都有助於測量這些瓶頸? 'perf記錄-e cs -g -p PID'。https://perf.wiki.kernel.org/index.php/Tutorial# Sampling_with_perf_record。但是,您提供的信息很少,以確保上下文切換對此負責。 –

+0

@MartinJames我已經更新了這個問題,使其更加清晰。我無法分享代碼,因爲它跨越了多個文件。 – ssk

回答

1

通常,與網絡無關,每個資源可以並行使用一個線程。換句話說,如果你有一個單一的網絡接口,一個線程就足以服務於網絡接口。由於您通常不會收到或發送數據,但也會對其執行某些操作,因此您的線程會切換爲使用其他資源,例如,用於計算的CPU或用於存儲或檢索硬盤的IO通道。這個任務需要在不同的線程中完成,而單個網絡線程不斷從網絡中檢索消息。因此,您爲每個連接創建一個線程的方法似乎是一種簡單的方法來保持事物的乾淨和獨立,但它不會縮放,因爲它涉及太多不必要的上下文切換。相反,如果可以的話,保持網絡在一個地方。另外,不要重新發明輪子。有像例如zeromq在那裏爲多個連接提供服務,從零散的網絡數據包中收集整個消息,並且只在一條消息完全接收時調用回調。它的確如此,所以我建議使用這個工具作爲溝通的基礎。此外,它提供了大量的語言綁定,因此您可以使用腳本語言快速爲節點建立原型,並在以後轉爲使用C++進行性能測試。

最後,恐怕您正在使用的庫(而不是似乎是Boost的一部分!)是放棄的,即它的開發已經停止。我不確定這一點,但看着更新日誌,他們聲稱他們使它與Boost 1.37兼容,這是非常古老的。確保你使用的是值得你的時間!

+0

好吧,您可以將第二段的開頭重寫爲'您正在避免使用每個連接的線程將您的內聯服務器代碼編寫爲複雜的,事件驅動的狀態機。從資源上阻塞的任何線程中刪除CPU並將CPU提供給另一個可以運行的線程需要一個上下文切換,這是這種設計中固有的必要條件。如果你確實需要處理大量的連接,你應該考慮將你的服務器重寫爲一堆雜亂的回調。 –