1

ファイルを実行して、30 ほどの異なるフラグメント タイプを処理しています。そのため、毎回、フラグメントを読み取り、そのタイプ (16 進数) を既知のフラグメントのタイプと比較します。これは速いですか、それとももっと速くできる別の方法はありますか?

ここに私が使用しているコードのサンプルがあります:

// Iterate through the fragments and address them individually
    for(int i = 0; i < header.fragmentCount; i++) 
    {
        // Read in memory for the current fragment
        memcpy(&frag, (wld + file_pos), sizeof(struct_wld_basic_frag));

        // Deal with each frag type
        switch(frag.id) 
        {
        // Texture Bitmap Name(s)
        case 0x03:
            errorLog.OutputSuccess("[%i] 0x03 - Texture Bitmap Name", i);
            break;
        // Texture Bitmap Info
        case 0x04:
            errorLog.OutputSuccess("[%i] 0x04 - Texture Bitmap Info", i);
            break;
        // Texture Bitmap Reference Info
        case 0x05:
            errorLog.OutputSuccess("[%i] 0x05 - Texture Bitmap Reference Info", i);
            break;
        // Two-dimensional Object
        case 0x06:
            errorLog.OutputSuccess("[%i] 0x06 - Two-dimensioanl object", i);
            break;

約 30 個のフラグメントを通過し、数千個のフラグメントがある場合、少しチャグすることがあります。このプロセスをスピードアップするにはどうすればよいですか?

ありがとうございました!

4

8 に答える 8

4

書式文字列を除いてこれらのケースがすべて同じである場合は、次のように、書式文字列の配列を使用し、大文字と小文字を区別しないことを検討してください。

const char *fmtStrings[] = {
  NULL, NULL, NULL,
  "[%i] 0x03 - Texture Bitmap Name",
  "[%i] 0x04 - Texture Bitmap Info",
  /* ... */
};

// ...
errorLog.OutputSuccess(fmtStrings[i], i);
// (range checks elided)

これは、分岐予測ミスのペナルティを伴わないため、スイッチよりも安価である必要があります。とはいえ、この切り替えのコストは、出力文字列を実際にフォーマットするコストよりもおそらく低いため、最適化の取り組みが少し見当違いになる可能性があります。

于 2011-06-21T01:47:16.160 に答える
2

コードが最適化されている場合 (場合によっては最適化されていない場合も)、コードはジャンプ テーブルとして実装されるため、case ステートメントは非常に高速である必要があります。デバッガーに移動し、スイッチにブレークポイントを設定し、逆アセンブリをチェックして、そうであることを確認します。

于 2011-06-21T01:48:00.540 に答える
1

フラグメント識別子がまばらすぎない場合は、フラグメント タイプ名の配列を作成し、それをルックアップ テーブルとして使用できます。

static const char *FRAGMENT_NAMES[] = {
    0,
    0,
    0,
    "Texture Bitmap Name", // 0x03
    "Texture Bitmap Info", // 0x04
    // etc.
};

...

const char *name = FRAGMENT_NAMES[frag.id];

if (name) {
    errorLog.OutputSuccess("[%i] %x - %s", i, frag.id, name);
} else {
    // unknown name
}
于 2011-06-21T01:46:58.057 に答える
1

memcpy を実行すると、おそらく多くのオーバーヘッドが発生していると思います。おそらく、(wld + file_pos) のデータへの直接アクセスで switch ステートメントを使用します。

于 2011-06-21T01:49:32.287 に答える
1

30のケースステートメントが問題であることに私は懐疑的です。これは、 memcpy および errorLog メソッドが行っていることと比較して、それほど多くのコードではありません。最初に、速度がディスク アクセスではなく CPU 時間によって制限されていることを確認します。本当に CPU バウンドの場合は、プロファイラーでコードを調べてください。

于 2011-06-21T01:52:06.140 に答える
0

ログ ステートメントが常に "[%i] 0xdd - message..." の形式の文字列であり、frag.id が常に 0 ~ 30 の整数である場合、代わりに文字列の配列を宣言できます。

std::string messagesArray[] = {"[%i] 0x00 - message one", "[%i] 0x01 - message two", ...}

次に、switch ステートメントを次のように置き換えます。

errorLog.OutputSuccess(messagesArray[frag.id], i);
于 2011-06-21T01:48:52.100 に答える
0

考えられるフラグメント型の値がすべて連続しており、一致時に文字列を出力するよりも複雑なことをしたくない場合は、配列にインデックスを付けることができます。次に例を示します。

  const char* typeNames[] = {"テクスチャ ビットマップ名", "テクスチャ ビットマップ情報", ...};

  /* 各frag.id: */
  if (LOWER_LIMIT <= frag.id && frag.id < UPPER_LIMIT) {
    printf("[%i] %#02x - %s\n", i, frag.id, typeNames[frag.id-LOWER_LIMIT]);
  } そうしないと {
   /* エラーについて文句を言います */
  }
于 2011-06-21T01:49:32.900 に答える
0

詳細を確認せずに断言することはできませんが、memcpy.

struct_wld_basic_frag *frag = (struct_wld_basic_frag *)wld;

for (i=0; i<header.fragmentCount; i++)
    errorlog.OutputSuccess(fragment_strings[frag[i].id], i);

今のところ、@Chris と @Ates で推奨されているように、さまざまなフラグメント タイプの文字列の配列を想定しています。最悪の場合でも、速度を損なうことなく可読性と保守性が向上します。せいぜい、(たとえば) キャッシュの使用率が向上し、速度が大幅に向上する可能性がありますerrorlog.outputSuccess.30 個の個別のコードのコピーではなく、1 つのコードのコピーを呼び出すことで、キャッシュ内の他の多くの「もの」のためのスペースを作ることができます。

ただし、毎回データをコピーしないようにすることで、実際に良い結果が得られる可能性が高くなります。同時に、これが問題を引き起こす可能性があることを付け加えておく必要があります。元のバッファーでデータが正しく配置されていない場合、ポインターを使用しようとしても機能しません。

于 2011-06-21T02:47:28.687 に答える