您认为如何让这个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次对该函数的调用,每次都在循环周围。
如果您还没有这样做,重新排序,如果该类型的测试,首先把最常用的列类型 - 这减少了所需的测试次数。
它的一个很好的部分是,Access是不是服务器的数据库 - 所有的文件进行读/写,锁定,光标处理等走的是客户端应用程序内进行(通过网络,对吧?),需要如果其他用户同时打开数据库。
如果没有,您可能会删除光标设置,并以只读方式打开数据库。
我可能是错,但我认为,因为它是客户端游标,所以当循环开始时,数据已经存在于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);
等等......
@Milan:你为什么编辑我的文章?根据Herb Sutter的“Exceptional C++”,应该尽量减少调用*运算符 2008-11-07 07:47:01
作为一个基本的想法,你应该尝试看看代码的速度,当你只有VT_BSTR转换,然后用VT_DATE和最后与其他类型,看看哪些花费最多的时间。
我唯一的观察结果是itoa不是标准C.实施可能会非常缓慢,因为您可以从this文章中看到。
谢谢。在我写下我的问题之后,我就是这么做的。我现在知道“var = pFields-> GetItem(i) - > GetValue()”需要大约一半的时间,转换/流入ostrm占用另一半。没有一个转换个体地突出,因为特别糟糕。谢谢你itoa。 – 2008-11-07 11:18:01
你不需要itoa - 你正在写一个流。
对不起,我的错。我省略了一些代码,以使示例更清晰,但流中也有分隔符。我不得不将所有内容都转换为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;
等等......
宏,不是一个函数,像 的#define V_VT(X)X-> VT – 2008-11-07 04:25:06