【问题标题】:Integer concatenation c整数连接 c
【发布时间】:2011-11-03 03:01:09
【问题描述】:

我正在尝试连接两个 uint32_t 并返回一个 uint64_t。这是我的方法

uint64_t concat_int1_int2 (uint32_t int1, uint32_t int2) {
  uint64_t concatenated = int1;
  return concatenated << 32 | int2;
}

这似乎非常慢(我需要做大约 1,000,000 次,大约需要 6 分钟)。两个问题,为什么位移需要这么长时间(这似乎是限制步骤),有没有人建议更快的方法来做到这一点?

【问题讨论】:

  • 我的第一个猜测是你正在编译一个 32 位二进制文​​件,而编译器不能使用原生的“long”类型。仔细检查您的构建是否生成了 x64 可执行文件?
  • 正如大卫所说,这是正确的方法 - 你使用的是什么硬件 - 是偶然的 32 位吗?
  • 我们能看到调用代码,或者一些分析程序的输出吗?如果你正在做类似for(int i = 0; i &lt; 1000000; i++) printf("%" PRIu64, concat_int1_int2(int1, int2)); 的事情,那么我可以告诉你为什么需要 6 分钟...
  • 另外,如果您将它们命名为highlow,而不是int1int2,可能会更清楚。
  • 我在 64 位机器上运行。评论了大卫关于为什么我认为转变是限制性步骤的回答。关于我的思路是否有效的任何问题?

标签: c


【解决方案1】:

你这样做的方式是正确的。 inline 该功能可能会有所帮助。您的性能问题很可能出在您没有向我们展示的代码中。

【讨论】:

  • 嗯...我的信念是位移是限制步骤,因为如果我简单地取出它并计算我的程序执行得相当好的连接 OR int2很快。
  • 你是对的。这是因为我没有显示代码。我计算的 uint64_t 被用作 GHashTable 的键。重要的是在键中同时包含两个整数。出于某种原因,这样做 uint64_t concat_int1_int2 (uint32_t int1, uint32_t int2) { uint64_t concatenated = int2;返回串联
  • 一个简单的移位和或对于哈希键来说太天真了。它有时会起作用(如果输入具有正确的特征),而在其他情况下会失败(如果没有)。试试:uint64_t key=int2; key*=2654435761U; return key^int1;(谢谢你,Knuth!)
  • 让我澄清一下,这不是为了混合功能,只是为了创建插入哈希表的唯一键。
  • 啊,那么问题很可能是您的表要求键均匀分布。您可以通过切换到没有此要求的表或通过“混合”键以使它们分布更均匀来解决它。
【解决方案2】:

您的解决方案几乎是最佳的,它不能更快​​。将函数标记为inline 可能帮助很小,但不会有太大的不同。 我使用您的代码在我的机器上进行了测试,运行 1,000,000 次迭代需要 10 毫秒。您的速度问题出在其他地方。

【讨论】:

    【解决方案3】:

    为什么要移位?只需将 32 位整数铲入适当的内存位置即可。比如:

     *((uint32_t*) concatenated) = int1;
     *(((uint32_t*) concatenated)+1) = int2;
    

    既然我已经给了你这个想法,请注意在这个函数中,int1 和 int2 已经在堆栈上彼此相邻。因此,如果它们的顺序正确,只需将其中一个参数转换为 uint64_t 即可满足您的要求!耶指针!

    【讨论】:

    • 问题是哪个内存位置是“合适的”取决于硬件的细节。更糟糕的是,这强制操作发生在内存中,而他的代码可以完全发生在寄存器中,因此它的性能可能会更差。
    • 为什么让它看起来比它必须的更黑客? ((uint32_t *)concatenated)[0] = int1; ((uint32_t *)concatenated)[1] = int2;(这种方法甚至可以帮助解决字节序问题 - 将 01 更改为 HIGHLOW,它们是 #defined 适合目标系统的字节序。
    • 你们这些新奇的“阵列”。
    • 在 ARM 和其他 RISC 架构上,参数通常通过寄存器而不是堆栈传递。甚至可以将 x86 编译器配置为生成通过寄存器传递参数的代码(GCC 中的-mregparm 开关)。程序不应该做出这样的假设,并按照您的建议进行演员表。无论如何,转换值是不行的,编译器会保证数字的语义等价,用零填充额外的字节。要使这个 hack 起作用,参数的地址必须转换为 (uint64_t*),而不是值本身。
    • 标准的 x86-64 调用约定甚至在寄存器中传递了前几个参数,所以它真的一点也不奇怪。如果您使用指向这些参数的指针,编译器将不得不将它们溢出到堆栈中,但是发生的顺序当然取决于编译器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-24
    • 2020-08-29
    • 2016-09-28
    • 2012-09-23
    • 2019-01-22
    • 2012-12-28
    • 1970-01-01
    相关资源
    最近更新 更多