编译器应该为某些机器生成汇编器(最终是机器代码),并且通常C ++会同情该机器。
对底层机器表示同情意味着:使编写C ++代码变得容易,它将有效地映射到机器可以快速执行的操作上。因此,我们希望提供对硬件平台上快速且“自然”的数据类型和操作的访问。
具体而言,请考虑特定的机器体系结构。让我们以当前的Intel x86家族为例。
英特尔®64和IA-32体系结构软件开发人员手册卷1(链接)第3.4.1节说:
提供32位通用寄存器EAX,EBX,ECX,EDX,ESI,EDI,EBP和ESP用于保存以下各项:
•用于逻辑和算术运算的操作数
•用于地址计算的操作数
•内存指针
因此,我们希望编译器在编译简单的C ++整数算法时使用这些EAX,EBX等寄存器。这意味着当我声明一个时int
,它应该与这些寄存器兼容,以便我可以有效地使用它们。
寄存器的大小始终相同(此处为32位),因此int
变量也始终为32位。我将使用相同的布局(小端),这样就不必每次将变量值加载到寄存器中或将寄存器存储回变量中时都进行转换。
使用godbolt,我们可以确切地看到编译器对一些琐碎的代码所做的工作:
int square(int num) {
return num * num;
}
编译(使用GCC 8.1并-fomit-frame-pointer -O3
简化):
square(int):
imul edi, edi
mov eax, edi
ret
这表示:
- 该
int num
参数是在寄存器EDI中传递的,这意味着它恰好是Intel期望本机寄存器的大小和布局。该函数不必转换任何东西
- 乘法是一条指令(
imul
),速度非常快
- 返回结果只是将其复制到另一个寄存器中(调用者希望将结果放入EAX中)
编辑:我们可以添加相关的比较,以使用非本地版面制作显示差异。最简单的情况是将值存储在非本地宽度中。
再次使用Godbolt,我们可以比较一个简单的本机乘法
unsigned mult (unsigned x, unsigned y)
{
return x*y;
}
mult(unsigned int, unsigned int):
mov eax, edi
imul eax, esi
ret
使用非标准宽度的等效代码
struct pair {
unsigned x : 31;
unsigned y : 31;
};
unsigned mult (pair p)
{
return p.x*p.y;
}
mult(pair):
mov eax, edi
shr rdi, 32
and eax, 2147483647
and edi, 2147483647
imul eax, edi
ret
所有额外的指令都与将输入格式(两个31位无符号整数)转换成处理器可以本地处理的格式有关。如果我们想将结果存储回31位值,则将有另一条或两条指令来执行此操作。
这种额外的复杂性意味着,只有在节省空间非常重要时,您才需要为此而烦恼。在这种情况下,与使用本机unsigned
或uint32_t
类型相比,我们只节省了两位,这将生成简单得多的代码。
有关动态尺寸的说明:
上面的示例仍然是固定宽度值,而不是可变宽度,但是宽度(和对齐方式)不再与本机寄存器匹配。
x86平台具有多种本机大小,除了主要的32位外,还包括8位和16位(为了简化起见,我对64位模式进行了修饰)。
这些类型(char,int8_t,uint8_t,int16_t等)也直接受体系结构支持-部分是为了与较旧的8086/286/386 / etc向后兼容。等指令集。
当然,选择满足要求的最小自然固定大小类型是一种很好的做法-它们仍然快速,单指令加载和存储,您仍然可以获得全速本机算法,甚至可以通过以下方式提高性能:减少缓存丢失。
这与可变长度编码非常不同-我已经处理了其中的一些,而且它们太可怕了。每个加载都变成一个循环,而不是一条指令。每个商店也是一个循环。每个结构都是可变长度的,因此您不能自然地使用数组。
关于效率的进一步说明
据我所知,在随后的评论中,您一直在使用“有效”一词。有时我们确实选择最小化存储大小-当我们将大量的值保存到文件或通过网络发送它们时,这一点很重要。折衷是我们需要将这些值加载到寄存器中以对其进行任何操作,并且执行转换不是免费的。
当我们讨论效率时,我们需要知道我们正在优化什么,以及权衡取舍。使用非本机存储类型是用处理速度换取空间的一种方法,有时是有意义的。使用可变长度存储(至少适用于算术类型),可以以更高的处理速度(以及代码复杂性和开发人员时间)进行交易,以节省空间。
您为此付出的速度损失意味着只有在您需要绝对最小化带宽或长期存储时才值得,并且在这种情况下,使用简单自然的格式通常更容易-然后使用通用系统对其进行压缩(例如zip,gzip,bzip2,xy或其他)。
tl; dr
每个平台都有一个体系结构,但是您可以想出数量不限的不同方式来表示数据。任何语言提供无限数量的内置数据类型都是不合理的。因此,C ++提供了对平台的本机,自然数据类型集的隐式访问,并允许您自己编写任何其他(非本机)表示形式的代码。
unsinged
可以用1个字节表示的最大值是255
。2)考虑变量值的最佳存储大小计算和缩小/扩展存储区域的开销。