2008-11-07 42 views
0

此循環比我預期的要慢,我不確定它在哪裏。看到什麼?您認爲如何讓這個C++代碼變慢? (它通過一個ADODB記錄集循環,將COM類型轉換爲字符串,並填充ostringstream)

我正在閱讀Accces DB,使用客戶端遊標。當我有12萬行20列時,這個循環大約需要10秒。 20列是字符串,整型和日期類型。所有類型在放入ostringstream緩衝區之前都會轉換爲ANSI字符串。

void LoadRecordsetIntoStream(_RecordsetPtr& pRs, ostringstream& ostrm) 
{ 
    ADODB::FieldsPtr pFields = pRs->Fields; 
    char buf[80]; 
    ::SYSTEMTIME sysTime; 
    _variant_t var; 

    while(!pRs->EndOfFile) // loop through rows 
    { 
     for (long i = 0L; i < nColumns; i++) // loop through columns 
     { 

      var = pFields->GetItem(i)->GetValue(); 

      if (V_VT(&var) == VT_BSTR) 
      { 
       ostrm << (const char*) (_bstr_t) var; 
      } 
      else if (V_VT(&var) == VT_I4 
      || V_VT(&var) == VT_UI1 
      || V_VT(&var) == VT_I2 
      || V_VT(&var) == VT_BOOL) 
      { 
       ostrm << itoa(((int)var),buf,10); 
      } 
      else if (V_VT(&var) == VT_DATE) 
      { 
       ::VariantTimeToSystemTime(var,&sysTime); 
       _stprintf(buf, _T("%4d-%02d-%02d %02d:%02d:%02d"), 
       sysTime.wYear, sysTime.wMonth, sysTime.wDay, 
       sysTime.wHour, sysTime.wMinute, sysTime.wSecond); 

       ostrm << buf; 
      } 
     } 

     pRs->MoveNext(); 
    } 
} 

編輯:更多的試驗後...

現在我知道,一半的時間用於該行:
VAR = pFields->的GetItem(I) - >的GetValue();

如果我繞過微軟生成的COM包裝,我的代碼會更快嗎?我的猜測是否定的。

時的行吟詩人的一半是花在其中的數據轉換和它傳輸到ostringstream的語句。

我不知道現在,因爲我寫這個,無論是轉換還是流媒體花費更多時間。

難道是更快,如果我沒有用ostringstream,而是管理我自己的緩衝區,用我自己的邏輯來增加緩衝區(重新ALLOC,複製,刪除)?如果我的邏輯做出了悲觀的猜測,並且預先爲ostringstream緩衝區保留了大量空間,會更快嗎?這些可能是值得嘗試的實驗。

最後,轉換本身。在我的時間裏,三人中沒有一人表現出色。一個答案說,我的itoa可能比替代品慢。值得檢查。

回答

1

嘗試註釋掉for循環中的代碼並比較時間。一旦你有閱讀,開始取消註釋各個部分,直到你打到瓶頸。

2

我看不出你的代碼,有人更熟悉COM/ATL可能有更好的答案。

通過試n錯誤我會覺得註釋掉內循環操作,直到你看到PERF秒殺,慢速代碼,那麼你有你的罪魁禍首,並應着眼於這一點。

2

我假定V_VT是一個函數 - 如果是這樣,則對於每個日期值,V_VT(& VAR)被調用6次。一個簡單的優化就是在本地存儲V_VT的值(& var)以節省多達5次對該函數的調用,每次都在循環周圍。

如果您還沒有這樣做,重新排序,如果該類型的測試,首先把最常用的列類型 - 這減少了所需的測試次數。

+0

宏,不是一個函數,像 的#define V_VT(X)X-> VT – 2008-11-07 04:25:06

1

它的一個很好的部分是,Access是不是服務器的數據庫 - 所有的文件進行讀/寫,鎖定,光標處理等走的是客戶端應用程序內進行(通過網絡,對吧?),需要如果其他用戶同時打開數據庫。

如果沒有,您可能會刪除光標設置,並以只讀方式打開數據庫。

+0

我可能是錯,但我認爲,因爲它是客戶端遊標,所以當循環開始時,數據已經存在於ADO控制下的內存緩衝區中,而不是Access的控制。 Jet不在圖片中。 只是通過行循環幾乎沒有時間。 – 2008-11-07 11:19:05

0

嘗試剖析。如果你沒有一個探查一個簡單的方法可能是包裹在你的循環,你認爲所有的通話可能需要一些時間,像下面這樣:

#define TIME_CALL(x) \ 
do { \ 
    const DWORD t1 = timeGetTime();\ 
    x;\ 
    const DWORD t2 = timeGetTime();\ 
    std::cout << "Call to '" << #x << "' took " << (t2 - t1) << " ms.\n";\ 
}while(false) 

所以,現在你可以說:

TIME_CALL(var = pFields->GetItem(i)->GetValue()); 
TIME_CALL(ostrm << (const char*) (_bstr_t) var); 

等等......

+0

@Milan:你爲什麼編輯我的文章?根據Herb Sutter的「Exceptional C++」,應該儘量減少調用頂級運算符<<()的次數,因爲它可以拋出異常。電話較少,例外的可能性較小。請停止編輯帖子到您自己的偏好! – 2008-11-07 07:47:01

1

作爲一個基本的想法,你應該嘗試看看代碼的速度,當你只有VT_BSTR轉換,然後用VT_DATE和最後與其他類型,看看哪些花費最多的時間。

我唯一的觀察結果是itoa不是標準C.實施可能會非常緩慢,因爲您可以從this文章中看到。

+0

謝謝。在我寫下我的問題之後,我就是這麼做的。我現在知道「var = pFields-> GetItem(i) - > GetValue()」需要大約一半的時間,轉換/流入ostrm佔用另一半。沒有一個轉換個體地突出,因爲特別糟糕。謝謝你itoa。 – 2008-11-07 11:18:01

0

你不需要itoa - 你正在寫一個流。

+0

對不起,我的錯。我省略了一些代碼,以使示例更清晰,但流中也有分隔符。我不得不將所有內容都轉換爲ascii,以免與分隔符相沖突。 – 2008-11-07 11:15:45

0

要回答新問題,我認爲你應該使用的事實,你可以讓數據流格式的數據,而不是將其格式化爲一個字符串,然後將字符串傳遞到流,例如:

_stprintf(buf, _T("%4d-%02d-%02d %02d:%02d:%02d"), 
        sysTime.wYear, sysTime.wMonth, sysTime.wDay, 
        sysTime.wHour, sysTime.wMinute, sysTime.wSecond); 

ostrm << buf; 

變成:

ostrm.fill('0'); 
ostrm.width(4); 
ostrm << sysTime.wYear << _T("-"); 
ostrm.width(2); 
ostrm << sysTime.wMonth; 

等等......

相關問題