常见架构的最快整数类型

Posted

技术标签:

【中文标题】常见架构的最快整数类型【英文标题】:Fastest integer type for common architectures 【发布时间】:2011-04-11 06:47:56 【问题描述】:

stdint.h 标头缺少与,uint_fastX_t 类型对应的int_fastest_tuint_fastest_t。对于整数类型的宽度无关紧要的情况,如何选择允许处理最多位且对性能的影响最小的整数类型?例如,如果一个人正在使用一种简单的方法在缓冲区中搜索第一个设置位,则可能会考虑这样的循环:

// return the bit offset of the first 1 bit
size_t find_first_bit_set(void const *const buf)

    uint_fastest_t const *p = buf; // use the fastest type for comparison to zero
    for (; *p == 0; ++p); // inc p while no bits are set
    // return offset of first bit set
    return (p - buf) * sizeof(*p) * CHAR_BIT + ffsX(*p) - 1;

当然,使用char 会导致比int 更多的操作。但是long long 可能会导致比在 32 位系统上使用 int 等的开销更昂贵的操作。

我目前的假设是针对主流架构,使用long 是最安全的选择:在 32 位系统上是 32 位,在 64 位系统上是 64 位。

【问题讨论】:

原生 int 大小会是最快的吗? IOW int. 你确定需要指定 p 指向那里的 buf 吗? 实际上,我没有看到“快速”类型的意义。他们对哪些操作最快?对于您的应用程序,64 位类型可能是最快的,但对于除法,最宽的类型是最慢的。 感谢@Jon Cage,我在写这段代码时非常分心。 我明白了,代码被简化了,我希望你考虑到对齐。取消引用未对齐的指针,如果它不会导致一些致命错误,可能会非常慢。 【参考方案1】:

我不确定我是否真正理解了这个问题,但你为什么不直接使用 int?引用我的(错误的免费草稿副本,即 C++)标准,“普通整数具有执行环境架构所建议的自然大小。”

但我认为,如果你想对某个操作有最佳的整数类型,它会根据它是哪个操作而有所不同。尝试在大型数据缓冲区中找到第一位,或者在整数序列中找到一个数字,或者移动它们,很可能会有完全不同的最优类型。

编辑:

不管它值多少钱,我做了一个小基准测试。在我的特定系统(带有 Linux 的 Intel i7 920,gcc -O3)上,在这个特定的示例中,长整数(64 位)比普通整数(32 位)快很多。我会猜到相反的。

【讨论】:

int 在非常常见的 LP64 x86_64 平台上不是 64 位 @Matt:是的。但如果它更宽,它会自动更快吗?我不这么认为,但我没有对任何东西进行基准测试。 因为标准整数类型只有 5 种不同的可能大小(charshortintlonglong long)并且所有的力量都倾向于拥有宽度为 8、16、32、64,在不久的将来为 128,int 将卡在 32 位上。它的定义与平台上的效率无关,而只是受其约束。在我的机器上,int 是 32 位,然后 int_fast32_tlong,而 long 又是 64 位。 @Jens Gustedt:这排除了int 在 x86 上不再是最快的时候/如果 32 位整数不再是最快的可能性,谢谢。【参考方案2】:

对于所有现有的主流架构,long 是目前循环吞吐量最快的类型。

【讨论】:

也许是最快的标准类型。大多数支持多媒体的主流架构都有矢量扩展。【参考方案3】:

由于问题不完整,无法回答此问题。打个比方,考虑一下这个问题:

最快的车是什么

Bugatti Veyron?肯定很快,但不适合从伦敦到纽约。

问题中缺少的是整数将在其中使用的上下文。在上面的原始示例中,如果数组又大又稀疏,我怀疑您会看到 8、32 或 64 位值之间的很大差异,因为您将在 CPU 限制之前达到内存带宽限制。

主要的一点是,架构并没有定义各种整数类型的大小,这是编译器设计者做的。设计师将仔细权衡给定架构的每种类型的各种尺寸的优缺点,并选择最合适的。

我猜选择 64 位系统上的 32 位 int 是因为对于大多数操作而言,int 用于 32 位就足够了。由于内存带宽是一个限制因素,因此节省内存使用可能是最重要的因素。

【讨论】:

当然,总吞吐量可能是一样的,但我需要这些周期来做其他事情。 @Matt:I need those cycles for other things - 嗯,除非您手动编写汇编程序以在指令级别交错多个任务,否则这些周期将被浪费,操作系统不会在那个频率/粒度。【参考方案4】:

我猜想size_t(用于无符号类型)和ptrdiff_t(用于有符号类型)类型通常对应于任何给定平台上非常有效的整数类型。

但没有什么比检查生成的汇编器并做基准测试更能证明这一点了。

编辑,包括不同的 cmets,在此处和其他回复中:

size_tptrdiff_t 是唯一在 C99 中具有规范性的 typedef,人们可以合理地假设它们与架构相关。

标准整数类型有 5 种不同的可能等级(charshortintlonglong long)。所有的力量都朝着宽度为 8、16、32、64 以及在不久的将来为 128 的类型发展。因此,int 将被卡在 32 位上。它的定义与平台上的效率无关,只是受到宽度要求的限制。

【讨论】:

我怀疑 size_t 被选为与寻址最大可寻址虚拟内存空间所需的整数宽度相匹配。虽然它可能与本机寄存器宽度相同,但我认为选择它时并没有考虑到性能。还有一个挑剔:ssize_t 应该用于这种有符号整数,ptrdiff_t 应该只用于指针减法:)(和 ,uintptr_t 用于一般指针操作:]) @Matt: size_tptrdiff_t 是 C99 保证存在的唯一类型,其他类型是可选的。【参考方案5】:

如果您使用 gcc 进行编译,我建议您使用 __builtin_ffs() 来查找第一个位集:

内置函数:int __builtin_ffs (unsigned int x) 返回一加 x 的最低有效 1 位的索引,或者如果 x 为零,则返回零。

这将被编译成(通常是单个)本机汇编指令。

【讨论】:

该示例仅用于说明。我用 ffsX 来表示非特定宽度的 ffs 实现。【参考方案6】:

int_fast8_t 始终是正确实现中最快的整数类型。永远不可能有小于 8 位的整数类型(因为需要 CHAR_BIT>=8),并且由于 int_fast8_t 是最快的整数类型,至少有 8 位,因此它是最快的整数类型,句点。

【讨论】:

理论上这个答案是正确的(非常令人印象深刻的推理),但在我的系统上:typedef unsigned char uint_fast8_t;,我敢肯定这不会是最快的。 在 x86 上,除了 16 位(很慢)之外的所有类型都同样快。 实际上,稍微修正一下:对于单个变量,它们都同样快,但是如果您使用大量整数变量(尤其是数组),那么较小的类型当然会更快,因为减少了对缓存的影响。因此,从某种意义上说,char 是 x86 上最快的类型。 哦,看来我错过了你想要做的事情,但你的问题也误导了你说你不在乎大小。您确实关心大小,因为它会影响您必须循环的次数。在这种情况下,我建议只使用size_tuintptr_t,它们几乎可以肯定是系统字长(int 可能不是)。 VC++2013 有typedef unsigned char uint_fast8_t;,对吗? char 是否提供与int 相同的速度?我记得在 crc8 实现中将 unsigned char 替换为 unsigned int 时,在 x86 和 PowerPC 平台上都提供了 4 倍的加速【参考方案7】:

理论上,int 是最好的选择。它应该映射到 CPU 的本机寄存器大小,因此在您所询问的意义上是“最佳的”。

但是,您可能仍然发现 int-64 或 int-128 在某些 CPU 上比 int-32 更快,因为尽管它们大于寄存器大小,但它们会减少循环的迭代次数,因此可以通过最小化循环开销和/或利用 DMA 更快地加载/存储数据来提高效率。

(例如,在 ARM-2 处理器上,加载一个 32 位寄存器需要 4 个内存周期,但顺序加载两个只需要 5 个周期,顺序加载 4 个需要 7 个周期。您上面建议的例程将被优化使用尽可能多的寄存器(通常是 8 到 10 个),因此每次循环迭代使用多个寄存器可以使运行速度提高 3 到 4 倍)

确定的唯一方法是编写几个例程,然后在特定目标机器上对它们进行分析,以找出哪个产生最佳性能。

【讨论】:

除了我不认为int 映射到 x86_64 上的本机寄存器大小(这只是 LP64 传统的属性而不是 ARM 上的情况吗?),你让好点子! @Matt:是的,这就是为什么我从“理论上”开始介绍自己的原因 :-) 'int' 的大小最终是编译器定义的,所以这种类型的优化确实必须确定每个平台上的每个编译器。【参考方案8】:

答案是int 本身。至少在 C++ 中,标准的 3.9.1/2 说:

平原ints 有自然大小 建议的架构 执行环境

我希望 C 语言也是如此,尽管我没有任何标准文档。

【讨论】:

感谢标准报价,但您如何解释 LP64 x86_64? @Matt:我相信 Intel x86_64 CPU 上的 64 位算术比 32 位算术慢。我的回忆是,AMD 最先推出了 64 位版本的 x86 通用指令集,64 位算法可能比 32 位快,也可能不如 32 位,英特尔不得不快速赶上,因此它的 64 位操作在与以前相同的 32 位宽数据路径上内部实现,因此花费的时间是普通 32 位操作的两倍。谁能证实这一点? 如果我们能得到确认,这绝对是最好的答案。 @Matt:可能值得搜索“64 位”或类似问题的 SO 问题......这可能就是我从那里得到的。抱歉,我现在没有时间自己做这件事。【参考方案9】:

如果您想确定自己获得了最快的实现,为什么不在您期望运行的系统上对每一个进行基准测试,而不是尝试猜测?

【讨论】:

证明为什么如果这可以在一个平台一个平台的基础上进行基准测试,它不在 stdint.h 和它的同伴,,uint_leastX_t 类型可能已经经历了相同的检查? 这取决于您尝试实现的操作/算法。例如,看看 Jason Williams 的回答。这些类型可能会让您非常接近最快的解决方案,但可能在少数情况下有更快的方法来做事。最终,唯一确定的方法就是进行基准测试。

以上是关于常见架构的最快整数类型的主要内容,如果未能解决你的问题,请参考以下文章

rust的常见整数类型

MySQL常见的数据类型

三种常见类型的接口测试工具架构对比

微服务架构中常见的protobuf数据类型?

python常见数据类型

使用 MySQL 生成非顺序、仅限整数的 UUID 的最快方法?