2012-07-06 101 views
11

我想知道是否有一種很好的方法來監視我的應用程序內部,最好是以現有庫的形式進行監視。如何實現高效的C++運行時間統計信息

我的應用程序是多線程的,它使用消息傳遞系統在線程和外部世界之間進行通信。我的目標是監視發送什麼樣的消息,以什麼頻率發送等等。

也可以用其他的統計方式,比如每分鐘產生多少個線程,調用多少新的/刪除或應用程序的更具體方面;你的名字。

什麼是令人敬畏的就像你有谷歌瀏覽器的「內部網頁」,如net或chrome://跟蹤,但以命令行方式。

如果有一個通用的庫足以容納我的應用程序的特性,那就太棒了。
否則我準備實施一個能夠完成這項工作的小班,但我不知道從哪裏開始。我認爲最重要的是代碼不應該太多幹涉,以便不影響性能。

你們對這件事有一些指點嗎?

編輯:我的應用程序運行在Linux,在嵌入式環境中,可惜不是由Valgrind的支持:(

+0

將GPROF支持? gcc編譯器上的-pg? – pyCthon 2012-07-06 16:08:05

+0

是的,這是我們的一件事。雖然我的問題是運行很長時間的程序(服務),所以應該在運行時訪問統計信息:-) – Gui13 2012-07-06 19:04:13

+1

太糟糕了,您無法添加另一個標記「嵌入」。 – 2012-07-06 19:47:38

回答

3

我建議在你的代碼,您認爲,獲得增加的計數器,計數器可以static類成員。或全局變量。如果你使用一個類來定義你的櫃檯,你可以構造一個名一起註冊一個信息庫的計數器。然後,您可以查詢和通過查詢資料庫重置計數器。

struct Counter { 
    unsigned long c_; 
    unsigned long operator++() { return ++c_; } 
    operator unsigned long() const { return c_; } 
    void reset() { unsigned long c = c_; ATOMIC_DECREMENT(c_, c); } 
    Counter (std::string name); 
}; 

struct CounterAtomic : public Counter { 
    unsigned long operator++() { return ATOMIC_INCREMENT(c_, 1); } 
    CounterAtomic (std::string name) : Counter(name) {} 
}; 

ATOMIC_INCREMENT將是一個平臺特定的機制來增加櫃檯原子地。 GCC爲此提供了一個內置的__sync_add_and_fetchATOMIC_DECREMENT與GCC內置的__sync_sub_and_fetch類似。

struct CounterRepository { 
    typedef std::map<std::string, Counter *> MapType; 
    mutable Mutex lock_; 
    MapType map_; 
    void add (std::string n, Counter &c) { 
     ScopedLock<Mutex> sl(lock_); 
     if (map_.find(n) != map_.end()) throw n; 
     map_[n] = &c; 
    } 
    Counter & get (std::string n) const { 
     ScopedLock<Mutex> sl(lock_); 
     MapType::const_iterator i = map_.find(n); 
     if (i == map_.end()) throw n; 
     return *(i->second); 
    } 
}; 

CounterRepository counterRepository; 

Counter::Counter (std::string name) { 
    counterRepository.add(name, *this); 
} 

如果你知道相同的計數器將多個線程遞增,然後使用CounterAtomic。對於特定於線程的計數器,請使用Counter

+0

這是一個好的開始國際海事組織,比valgrind的建議更好,因爲它會違反'..所以表現不受影響...'的要求。這個sno snot地址是多線程的方面,增加/重置計數器不一定是原子...我想想在線程私有變量中維護其中的一些... – nhed 2012-07-06 15:52:35

+0

@nhed:感謝提醒我關於OP的MT要求。我用原子操作更新了答案。 – jxh 2012-07-06 16:00:11

+0

這是一個很棒的答案。我會嘗試一下,看看它是如何發展的。也感謝'__sync _ * _ and_fetch',我不知道這些,並且使用了一些互斥體! – Gui13 2012-07-06 19:13:23

0

看看valgrind/callgrind。

它可以用於分析,這是我瞭解你正在尋找。儘管如此,我認爲它在運行時不起作用,但它可以在您的進程完成後生成。

+0

@ Gui13:你是否在尋找編譯時統計信息,就像valgrind可能提供的那樣?或者您在尋找運行時統計信息,例如用戶點擊彈出式窗口的次數? – 2012-07-06 15:48:05

+0

不幸的是,我的應用運行在valgrind不支持的嵌入式平臺上。 – Gui13 2012-07-06 15:50:31

3

我收集你正試圖實現運行時統計信息的收集 - 例如發送了多少字節,運行了多長時間以及用戶激活特定功能的次數。通常,爲了從各種來源(如工作線程)編譯運行時統計信息(例如這些統計信息),我會讓每個源(線程)增加自己的本地計數器的最基本數據,但不執行任何冗長的數學或對該數據的分析。

然後回到主線程(或任何你想要分析的這些統計信息&顯示),我發送一個RequestProgress類型消息給每個工作線程。作爲迴應,工作者線程將收集所有基本數據,並可能進行一些簡單的分析。該數據連同基本分析結果一起發送回ProgressReport消息中的請求(主)線程。主線程然後聚合所有這些數據,做額外的(可能是昂貴的)分析,格式化並顯示給用戶或記錄。

主線程根據用戶請求發送此RequestProgress消息(例如,當他們按S鍵時)或定時間隔。如果定時間隔是我要做的,我通常會實現另一個新的「心跳線」。所有這些線程都會在指定的時間內執行Sleep(),然後發送Heartbeat消息到主線程。主線程依次通過發送RequestProgress消息到要從中收集統計信息的每個工作線程來對此Heartbeat消息進行處理。

收集統計數據的行爲看起來應該相當簡單。那麼爲什麼這麼複雜的機制呢?答案是雙重的。

首先,工作線程有工作要做,計算使用情況統計不是這樣。試圖重構這些線程以承擔與其主要目的正交的第二個責任,就像試圖將一個方形釘塞入圓孔一樣。他們不是爲了這樣做而設計的,所以代碼將不會被寫入。

其次,運行時統計的計算可能代價過高,如果您嘗試做太多,太頻繁。假設你有一個工作線程在網絡上發送多播數據,並且你想收集吞吐量數據。多少個字節,多長時間以及每秒多少字節的平均值。你可以讓工作者線程自己計算所有這些,但是這需要很多工作,而且工作線程正在做它應該做的事情 - 發送多播數據,CPU時間會更好。相反,如果您只是遞增一個計數器,以確定每次發送消息時發送了多少字節,則計數對線程性能的影響最小。然後響應偶爾RequestProgress消息,你可以計算出開始&停止時間,併發送只是一起讓主線程做的所有divison等

1

使用共享內存(POSIX,System V的,MMAP或什麼你有可用)。通過將原始內存塊轉換爲數組定義,將揮發性無符號32位或64位整數的固定長度數組(即可以在您的平臺上原子級增大的最大數組)放入其中。請注意,易失性不會讓你具有原子性;它會阻止編譯器優化,這可能會損害您的統計數據值。使用像gcc的__sync_add_and_fetch()或更新的C++ 11原子類型的內部函數。

然後,您可以編寫一個附屬於同一塊共享內存的小程序,並可以打印出一個或所有的統計數據。這個小型統計閱讀器程序和你的主程序將不得不共享一個通用頭文件,強制陣列中每個統計數據的位置。

這裏的一個明顯的缺點是你被困在固定數量的計數器中。但是很難打敗,在性能方面。影響是程序中各個點的整數的原子增量。

1

在嵌入式系統中,常見的技術是爲「日誌」預留一塊內存,並將其視爲一個循環隊列。編寫一些可以讀取這塊內存的代碼;這將有助於在運行時拍攝「快照」。

在網上搜索「調試日誌記錄」。應該提供一些你可以用來玩的資源。我去過的大多數商店通常都會推出自己的產品。

如果您有額外的非易失性存儲器,您可以預留一個區域並寫入該區域。如果您的系統足夠大以支持文件系統,則這還包括文件。

最糟糕的情況是,將數據寫入調試(串行)端口。對於實際的實時測量,我們通常使用連接到GPIO或測試點的示波器並向GPIO /測試點輸出脈衝。

0

這是一個很好的答案,@John Dibling!我有一個非常相似的系統。然而,我的「stat」線程每秒鐘查詢工作人員10次,並且每當「stat」線程每次請求數據時都會影響工作線程的性能,訪問這些數據的關鍵部分(計數器等)和它意味着工作線程在檢索數據時被阻塞。事實證明,在工作線程繁重的情況下,這10Hz的統計查詢影響了工人的整體表現。

因此,我切換到一個略有不同的統計報告模型 - 而不是積極從主線程查詢工作線程,我現在有工作線程報告他們的基本統計計數器到他們的獨佔統計數據庫,可以由主要查詢在任何時候都不會對工人產生直接影響。

0

如果你是C++ 11,你可以使用std ::原子<>

#include <atomic> 

class GlobalStatistics { 
public: 

    static GlobalStatistics &get() { 
     static GlobalStatistics instance; 
     return instance; 
    } 

    void incrTotalBytesProcessed(unsigned int incrBy) { 
     totalBytesProcessed += incrBy; 
    } 

    long long getTotalBytesProcessed() const { return totalBytesProcessed; } 


private: 

    std::atomic_llong totalBytesProcessed; 

    GlobalStatistics() { } 
    GlobalStatistics(const GlobalStatistics &) = delete; 
    void operator=(const GlobalStatistics &) = delete; 
};