1

我正在运行一个文件并处理 30 种左右不同的片段类型。所以每次,我都会读入一个片段并将它的类型(十六进制)与我知道的片段进行比较。这是快还是有其他方法可以让我更快地做到这一点?

这是我正在使用的代码示例:

// 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)

这应该比 switch 更便宜,因为它不会涉及分支错误预测惩罚。也就是说,此开关的成本可能低于实际格式化输出字符串的成本,因此您的优化工作可能有点错位。

于 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

我怀疑这 30 个案例陈述是否是问题所在。与您的 memcpy 和 errorLog 方法所做的任何事情相比,这并不是很多代码。首先验证您的速度是否受 CPU 时间限制,而不是受磁盘访问限制。如果您确实受 CPU 限制,请检查分析器中的代码。

于 2011-06-21T01:52:06.140 回答
1

我认为执行 memcpy 可能会导致大量开销。也许在 (wld + file_pos) 直接访问您的数据时使用您的 switch 语句。

于 2011-06-21T01:49:32.287 回答
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 个单独的副本可以为缓存中的许多其他“东西”腾出空间。

不过,避免每次都复制数据更有可能带来真正的好处。同时,我可能应该补充一点,这可能会导致问题——如果数据在原始缓冲区中没有正确对齐,那么尝试使用指针将不起作用。

于 2011-06-21T02:47:28.687 回答