2014-02-06 11 views
0

我需要一個函數來記錄一些信息,如:ALLOCA VS固定緩衝格式化字符串

void log(char* format, ...); 

對於形式的緩衝方法有幾種:

有一個固定大小的緩衝區。這不會導致溢出,但可能會截斷消息。

void log1(char* format, ...) 
{ 
    char buffer[BUFFER_SIZE]; 

    va_list args; 
    va_start(args, format); 
    vsnprintf(buffer, BUFFER_SIZE, format, args); 
    va_end(args); 
} 

有一個可變長度緩衝區。分配堆棧上的內存時可能會導致溢出。當內存分配在堆上時,我不會考慮這種情況。這是一個設計限制。

void log2(char* format, ...) 
{ 
    va_list args; 
    va_start(args, format); 
    int length = vsnprintf(NULL, 0, format, args); 
    char* buffer = (char*)alloca(length + 1); 
    vsnprintf(buffer, length+1, format, args); 
    va_end(args); 
} 

該函數將從多個線程中調用,因此保持一個通用的全局緩衝區不是一個選項(鎖定太多)。由於一些舊的編譯器/可執行格式限制,TLS也不是一種選擇。

我應該選擇哪個版本?使用第一種或第二種方法是否還有其他明顯的優勢/劣勢?

+2

第一個解決方案稍快,因爲vsnprintf不會被調用兩次。如果你認爲一行日誌永遠不會太長,那麼alloca不太可能觸發一個stackoverflow。我會選擇第一個解決方案,併爲BUFFER_SIZE(1000到2000個字符)選擇一個合理的大小。 –

+1

建議過長線本身是一個不應該傳播的錯誤。因此,我會選擇一個慷慨的固定大小的緩衝區。 – chux

回答

3

如上文所述,您應該選擇截斷消息的變體,因爲它是您以目前定義的方式執行的唯一選項。

1

我應該選擇哪個版本?是否還有其他明顯的使用第一或第二種方法的優點/缺點

第一種方法。

該函數將從多個線程中調用,因此保留一個通用的 全局緩衝區不是一個選項(鎖定太多)。

恕我直言,你跳到一個毫無根據的結論,或至少是一個無法衡量的表現期望。我認爲這是一種過早優化的形式。

我在多個嵌入式系統中使用了多年的RAM日誌。 (我的選擇:一個固定的緩衝區長度,沒有截斷,我使用循環隊列入隊,最舊的消息是over-write。緩衝區大小通常是啓動期間的簡短進度消息(帶時間戳)的5到10分鐘。我的內存日誌通常在選定的持續時間之後,在啓動問題時處於暫停捕獲狀態(以避免覆蓋)。但是否則日誌連續運行。

ram日誌對於調查許多其他問題(包括客戶報告的問題)非常有用。因此,至少在一個項目中,我們隨附的日誌已啓用,並教授現場服務如何訪問它......一位客戶瞭解到一些其他設備無意中發送了我們的系統正確響應的意外環回代碼。 )

在大多數實際的系統中,

  • sprintf的()(或等同物)到RAM緩衝器將完成在比典型的上下文切換時間少得多。

  • 如果您沒有基於優先級的搶先式調度(如vxWorks那樣),則啓用另一個任務(由於中斷或定時器或網絡輸入)將導致當前任務繼續運行,直到時間片,或者'放棄'cpu。兩者都不會導致互斥體碰撞。

  • 在vxWorks中,在嵌入式系統中,使用我的日誌的所有任務都具有相同的優先級(通過先前的決定),因此通常在下一個「啓用」相同的優先級任務到達之前,日誌條目操作將完成運行,因此沒有其他任務甚至有機會進入鎖定的關鍵部分。但爲了設計安全,關鍵部分鎖定(使用信號量)已到位。

由於這些因素,我相信多線程或任務很少會在日誌記錄中「碰撞」。

  • 您的結果可能會有所不同......這是要衡量的事情(即每秒運行多少次碰撞)。但是,我認爲在測量它之前不需要作出設計決定。

FYI:在我7歲的戴爾公司,與Ubuntu 12.04,我在5.4我們衡量一個 '典型的' 循環RAM日誌排隊。我典型的排隊是< 80個字符,有3到5次轉換(如%d,%f等)。另外,我觀察到,在我的桌面上,sprintf()大約是stringstream I/O的兩倍。另一個基準測試表明,信號量強制上下文切換大約需要14.6 us。

我還沒有探索我的公羊記錄中的碰撞計數,因爲我不知道如何測試。一個Linux線程或進程可以搶佔另一個? (如果沒有,計數應該是0)對我的未來研究。


更新:(13:20)

FYI:在我的戴爾,我加了一個測量的關鍵部分是如何長的鎖定(只有1個線程,所以沒有互斥命中(無鎖定的?沒有碰撞?)時間窗口是1.5 us到3.2 us,通常是< 2 us。

在調用任務或線程的上下文中,完成所有格式化(以及時間戳記和進入日誌的限制檢查)鎖定RR緩衝區,鎖定被應用,char被使用簡單的循環傳送到RR緩衝區,並且鎖定被釋放。

該傳輸時間超過一半的日誌:: ENQ()的持續時間稍差,並建議在其中可能發生碰撞的甚至更小的目標時間窗口。