在 ISO/IEC 9899:2018 (C18) 中,它在 7.20.1.3 下说明:
7.20.1.3 最快的最小宽度整数类型
1 以下每种类型都指定了一个整数类型,在所有至少具有指定宽度的整数类型中,该类型通常是最快的268)。
2 typedef 名称
int_fastN_t
指定宽度至少为 Nuint_fastN_t
的最快有符号整数类型。 typedef 名称指定宽度至少为 N的最快无符号整数类型。3 需要以下类型:
int_fast8_t
,int_fast16_t
,int_fast32_t
,int_fast64_t
,uint_fast8_t
,uint_fast16_t
,uint_fast32_t
,uint_fast64_t
此表格的所有其他类型都是可选的。
268)不保证指定的类型在所有用途中都是最快的;如果实现没有明确的理由选择一种类型而不是另一种,它只会选择一些满足符号和宽度要求的整数类型。
但是没有说明为什么这些“快速”整数类型更快。
我用 C++ 标记了这个问题,因为在 C++17 的头文件中也可以使用快速整数类型cstdint
。不幸的是,在 ISO/IEC 14882:2017 (C++17) 中没有关于它们的解释的这样的部分;我已经在问题正文中以其他方式实施了该部分。
信息:在 C 中,它们在stdint.h
.
LOOP(英特尔参考手动输入)递减ecx/rcx,然后如果非零则跳转.这很慢,但是英特尔不能廉价地把它变得很快吗? dec/jnz
已经将宏观融合成 Sandybridge家族的一个 uop; 唯一的区别是设置标志.
loop
关于各种微体系结构,来自Agner Fog的说明表:
Bulldozer-family/Ryzen:1 m-op(与宏观融合测试和分支相同,或者jecxz
)
P4:4次(相同jecxz
)
loope
/ loopne
).吞吐量= 4c(loop
)或7c(loope/ne
).loope
/ loopne
). 吞吐量=每5个循环一个,这是将循环计数器保留在内存中的瓶颈!jecxz
只有2 uops,吞吐量与普通吞吐量相同jcc
难道解码器不能像lea rcx, [rcx-1]
/ 那样解码jrcxz
吗?这将是3 uops.至少那是没有地址大小前缀的情况,否则它必须使用ecx
和截断RIP
,EIP
如果跳转; 也许奇怪的地址大小选择控制减量的宽度解释了许多uops?
或者更好,只需将其解码为不设置标志的融合分支和分支? dec ecx …
两者有什么区别?我知道无论环境如何,int32_t都是32位,但正如它的名字暗示它的速度快,int_fast32_t与int32_t相比要快多少?如果它明显更快,那么为什么呢?
write(1,"hi",3)
在linux上反汇编,gcc -s -nostdlib -nostartfiles -O3
结果如下:
ba03000000 mov edx, 3 ; thanks for the correction jester!
bf01000000 mov edi, 1
31c0 xor eax, eax
e9d8ffffff jmp loc.imp.write
Run Code Online (Sandbox Code Playgroud)
我不是到编译器的开发,但由于移动到这些寄存器的每一个值是恒定的和已知的编译时间,我很好奇,为什么不GCC使用dl
,dil
和al
来代替.也许有人会说,此功能不会让任何性能上的差异,但有一个在之间的可执行文件的大小有很大的区别mov $1, %rax => b801000000
,并mov $1, %al => b001
当我们谈论数千寄存器的程序访问.如果软件的优雅部分不仅体积小,它确实会对性能产生影响.
有人可以解释为什么"海湾合作委员会决定"它无所谓?
有时gcc使用32位寄存器,当我希望它使用64位寄存器时.例如以下C代码:
unsigned long long
div(unsigned long long a, unsigned long long b){
return a/b;
}
Run Code Online (Sandbox Code Playgroud)
使用-O2选项编译(省略一些样板文件):
div:
movq %rdi, %rax
xorl %edx, %edx
divq %rsi
ret
Run Code Online (Sandbox Code Playgroud)
对于无符号除法,寄存器%rdx
需要0
.这可以通过xorq %rdx, %rdx
但xorl %edx, %edx
似乎具有相同的效果来实现.
至少在我的机器上没有性能提升(即加速)进行xorl
了xorq
.
我实际上不只是一个问题:
xorl
并且不使用xorw
?xorl
比这更快的机器xorq
?一个普遍的说法是,缓存中的字节存储可能导致内部读 - 修改 - 写周期,或者与存储完整寄存器相比会损害吞吐量或延迟.
但我从未见过任何例子.没有x86 CPU是这样的,我认为所有高性能CPU也可以直接修改缓存行中的任何字节.一些微控制器或低端CPU是否有不同之处,如果它们有缓存的话?
(我不计算字可寻址的机器,或者字节可寻址但没有字节加载/存储指令的Alpha.我说的是ISA本身支持的最窄的存储指令.)
在我的研究中回答现代x86硬件可以不将单个字节存储到内存中吗?,我发现Alpha AXP省略字节存储的原因假设它们被实现为真正的字节存储到缓存中,而不是包含字的RMW更新.(因此,它会使L1d缓存的ECC保护更加昂贵,因为它需要字节粒度而不是32位).
所有现代架构(早期Alpha除外)都可以对不可缓存的内存(而不是RMW周期)进行真正的字节加载/存储,这对于为具有相邻字节I/O寄存器的设备编写设备驱动程序是必需的.(例如,使用外部启用/禁用信号来指定更宽总线的哪些部分保存实际数据,例如此ColdFire CPU /微控制器上的2位TSIZ(传输大小),或者像PCI/PCIe单字节传输,或者像DDR一样SDRAM控制信号掩盖选定的字节.)
对于微控制器设计,可能需要在缓存中为字节存储执行RMW循环,即使它不是针对像Alpha这样的SMP服务器/工作站的高端超标量流水线设计?
我认为这种说法可能来自可以用字寻址的机器.或者来自未对齐的32位存储,需要在许多CPU上进行多次访问,并且人们错误地将其从一般存储到字节存储.
为了清楚起见,我希望到同一地址的字节存储循环将在每次迭代中以与字存储循环相同的周期运行.因此,对于填充阵列,32位存储可以比8位存储快4倍.(也许如果少了32位门店饱和的内存带宽,但8位店家没有.)但是,除非字节存储有一个额外的惩罚,你不会得到更超过4倍的速度差.(或者无论宽度是多少).
而我在谈论asm.一个好的编译器会自动向量化C中的字节或int存储循环,并使用更宽的存储或目标ISA上的最佳存储.
; x86-64 NASM syntax
mov rdi, rsp
; RDI holds at a 32-bit aligned address
mov ecx, 1000000000
.loop: ; do {
mov byte [rdi], al
mov byte [rdi+2], dl ; store two bytes in the same dword
; no pointer increment, this is the same 32-bit dword every time
dec ecx
jnz …
Run Code Online (Sandbox Code Playgroud) 的X32 ABI指定,除其他事项外,对于x86_64体系生成的代码32位指针。它结合了 x86_64 架构(包括 64 位 CPU 寄存器)的优点和 32 位指针减少的开销。
的<stdint.h>
报头定义的typedef int_fast8_t
,int_fast16_t
,int_fast32_t
,和int_fast64_t
(和相应的无符号类型uint_fast8_t
等人)中,其中的每一个是:
在所有至少具有指定宽度的整数类型中,通常操作最快的整数类型
附注:
不能保证指定的类型在所有用途中都是最快的;如果实现没有明确的理由选择一种类型而不是另一种,它只会选择一些满足符号和宽度要求的整数类型。
(引自N1570 C11 草案。)
问题是,无论有没有 x32 ABI ,应该如何为 x86_64 架构定义[u]int_fast16_t
和[u]int_fast32_t
类型?是否有指定这些类型的 x32 文档?它们是否应该与 32 位 x86 定义(均为 32 位)兼容,或者,由于 x32 可以访问 64 位 CPU 寄存器,它们在使用或不使用 x32 ABI 的情况下是否应该具有相同的大小?(请注意,无论是否使用 x32 ABI,x86_64 都有 64 位寄存器。)
这是一个测试程序(取决于特定于 gcc 的__x86_64__
宏):
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
int main(void) {
#if …
Run Code Online (Sandbox Code Playgroud) 这是复制(转换)未签名寄存器的指令:http : //www.felixcloutier.com/x86/MOVZX.html
基本上,该指令具有8-> 16、8-> 32、8-> 64、16-> 32和16-> 64。
32-> 64转换在哪里?我需要为此使用签名版本吗?
如果是这样,如何将全64位用于无符号整数?
我不是C++专家,但我开始通过www.learncpp.com学习,这很棒btw,我建议任何新的c ++学习者和本主题:
http://www.learncpp.com/cpp-tutorial/24a-fixed-width-integers/
提到以下内容:
快速类型(int_fast#_t)为您提供一个最快类型的整数,其宽度至少为#位(其中#= 8,16,32或64).例如,int_fast32_t将为您提供至少32位的最快整数类型.
最快的整数类型是什么意思?是什么定义了速度?
我假设并非所有整数都以相同的方式访问,有些比其他更容易访问,但我需要知道什么可能导致访问速度?
我在一个问题中读过:
在某些处理器上,如果变量存储在较长的寄存器中,则编译器可能必须添加额外的代码以丢弃任何额外的位.例如,如果uint16_t x; 存储在ARM7-TDMI上的32位寄存器中,代码为x ++; 可能需要评估为x =((x + 1)<< 16)>> 16);. 在该平台的编译器上,uint_fast16_t很可能被定义为uint32_t的同义词以避免这种情况.
是什么让这更快?在任何一种情况下,32位将在寄存器级别中循环.
我正在阅读英特尔文档,第一卷。1 和3.6.1 64 位模式下的操作数大小和地址大小一章。有三个前缀REX.W
,操作数大小66
和地址大小67
前缀。并提到操作数的大小默认为 32 位。并且只能用REX.W
指令前缀(在其他前缀之后)以使其长度为 64 位。
我不知道为什么会这样,为什么我不能将完整的 64 位空间用于例如int
操作数?跟签有关系吗?或者为什么会有这个限制?(所以,C是否在 int 上unsigned int
使用REX.W
前缀操作(正如也提到的,前缀仅持续特定指令,而不是整个段,这应该是(大小,地址或操作数)默认值和包含在段描述符中)。
我理解正确吗?