您认为如何让这个C++代码变慢? (它通过一个ADODB记录集循环,将COM类型转换为字符串,并填充ostringstream)

问题描述:

此循环比我预期的要慢,我不确定它在哪里。看到什么?您认为如何让这个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可能比替代品慢。值得检查。

尝试注释掉for循环中的代码并比较时间。一旦你有阅读,开始取消注释各个部分,直到你打到瓶颈。

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

通过试n错误我会觉得注释掉内循环操作,直到你看到PERF秒杀,慢速代码,那么你有你的罪魁祸首,并应着眼于这一点。

我假定V_VT是一个函数 - 如果是这样,则对于每个日期值,V_VT(& VAR)被调用6次。一个简单的优化就是在本地存储V_VT的值(& var)以节省多达5次对该函数的调用,每次都在循环周围。

如果您还没有这样做,重新排序,如果该类型的测试,首先把最常用的列类型 - 这减少了所需的测试次数。

+0

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

它的一个很好的部分是,Access是不是服务器的数据库 - 所有的文件进行读/写,锁定,光标处理等走的是客户端应用程序内进行(通过网络,对吧?),需要如果其他用户同时打开数据库。

如果没有,您可能会删除光标设置,并以只读方式打开数据库。

+0

我可能是错,但我认为,因为它是客户端游标,所以当循环开始时,数据已经存在于ADO控制下的内存缓冲区中,而不是Access的控制。 Jet不在图片中。 只是通过行循环几乎没有时间。 – 2008-11-07 11:19:05

尝试剖析。如果你没有一个探查一个简单的方法可能是包裹在你的循环,你认为所有的通话可能需要一些时间,像下面这样:

#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

作为一个基本的想法,你应该尝试看看代码的速度,当你只有VT_BSTR转换,然后用VT_DATE和最后与其他类型,看看哪些花费最多的时间。

我唯一的观察结果是itoa不是标准C.实施可能会非常缓慢,因为您可以从this文章中看到。

+0

谢谢。在我写下我的问题之后,我就是这么做的。我现在知道“var = pFields-> GetItem(i) - > GetValue()”需要大约一半的时间,转换/流入ostrm占用另一半。没有一个转换个体地突出,因为特别糟糕。谢谢你itoa。 – 2008-11-07 11:18:01

你不需要itoa - 你正在写一个流。

+0

对不起,我的错。我省略了一些代码,以使示例更清晰,但流中也有分隔符。我不得不将所有内容都转换为ascii,以免与分隔符相冲突。 – 2008-11-07 11:15:45

要回答新问题,我认为你应该使用的事实,你可以让数据流格式的数据,而不是将其格式化为一个字符串,然后将字符串传递到流,例如:

_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; 

等等......