0

このループは私が予想するよりも遅く、まだどこにあるのかわかりません。何か見えますか?

クライアント側のカーソルを使用して、AcccesDBを読み取っています。20列の127,000行がある場合、このループには約10秒かかります。20列は、string、int、およびdateタイプです。すべてのタイプは、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();

Microsoftが生成したCOMラッパーをバイパスした場合、コードは高速になりますか?私の推測は違います。

時間の残りの半分は、データを変換してostringstreamにストリーミングするステートメントに費やされます。

これを書いているので、変換に時間がかかっているのか、ストリーミングに時間がかかっているのかは、今のところわかりません。

ostringstreamを使用せず、代わりにバッファを拡張する独自のロジック(再割り当て、コピー、削除)を使用して独自のバッファを管理した場合、より高速になりますか?私のロジックが悲観的な推測を行い、ostringstreamバッファー用に多くのスペースを事前に予約した場合、より速くなりますか?これらは試す価値のある実験かもしれません。

最後に、変換自体。私のタイミングでは、3つのうちどれも悪いものとして目立ちません。ある答えは、私のitoaは他の方法よりも遅いかもしれないと言っています。チェックアウトする価値があります。

4

8 に答える 8

2

あなたのコードを見てもわかりませんが、COM/ATL に詳しい人ならもっと良い答えがあるかもしれません。

試行錯誤により、パフォーマンスの急上昇が見られるまで内部ループ操作をコメントアウトすることで遅いコードを見つけることができます。その後、犯人がわかり、それに集中する必要があります。

于 2008-11-07T04:18:18.967 に答える
2

V_VT は関数であると仮定します。その場合、日付値ごとに V_VT(&var) が 6 回呼び出されます。簡単な最適化は、V_VT(&var) の値をローカルに保存して、ループのたびにこの関数への最大 5 回の呼び出しを保存することです。

まだ行っていない場合は、型の if テストを並べ替えて、最も一般的な列型を最初に配置します。これにより、必要なテストの数が減ります。

于 2008-11-07T04:20:36.997 に答える
1

基本的な考え方として、VT_BSTR変換しかない場合はコードの速度を確認し、その後VT_DATEを使用し、最後に他のタイプを使用して、どれが最も時間がかかっているかを確認する必要があります。

私が持っている唯一の観察は、itoaが標準Cではないということです。この記事からわかるように、実装は非常に遅いかもしれません。

于 2008-11-07T09:51:52.337 に答える
1

その良い部分は、Access がサーバー データベースではないということです。ファイルの読み取り/書き込み、ロック、カーソル処理などはすべて、クライアント アプリケーション内で (ネットワークを介して行われますよね?)、そうする必要があります。他のユーザーが同時にデータベースを開いています。

そうでない場合は、カーソル設定を削除して、データベースを読み取り専用で開くことができます。

于 2008-11-07T04:29:26.853 に答える
1

for ループ内のコードをコメント アウトして、時間を比較してみてください。読んだら、ボトルネックにぶつかるまで、さまざまなセクションのコメントを外し始めます。

于 2008-11-07T07:30:59.110 に答える
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);

等々...

于 2008-11-07T07:16:04.397 に答える
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;

等々...

于 2008-11-07T15:51:36.823 に答える
0

itoa は必要ありません。ストリームに書き込みます。

于 2008-11-07T10:17:38.447 に答える