如果变量是 aboolean
或 a char
(在 C/C++ 中)或 a byte
(在某些语言中,如 Java - 在 VM 上无需等待 Java)并且 CPU 使用大于 1 字节的字,是否浪费空间?我听说当变量存储在内存中时,它可以存储得更紧凑,例如 1 个字节,即使一个字是 4 个字节。有人可以解释这是怎么发生的吗?这似乎倒退了,因为寄存器比 RAM 更珍贵。
7 回答
尽管它们能够存储数字,但寄存器并不完全是“空间”。
考虑这个例子:你有一堆名片放在一个大盒子里,你想按字母顺序排列它们。在这样做的过程中,您将盒子中的卡片从一个地方移动到另一个地方。尽管您在将卡片移动到盒子中的新位置时将其握在手中,但您的手并不能像盒子那样为卡片提供存储空间。你手中的位置太宝贵了,不能称为“空间”。
继续卡片类比,想象卡片有不同的大小。你手的大小可以让你一次拿着一张大牌、两张中牌或四张小牌。然而,当你对卡片进行排序时,拿走多张卡片的能力对你来说很少有好处,尤其是当卡片被均匀洗牌时:多张卡片有多个目的地,所以如果你抓到,你需要做更复杂或不必要的操作同时多张卡。
同样,一个 CPU 的寄存器可能包含多个字节,但如果需要执行计算,通常没有办法告诉 CPU 使用哪些字节:寄存器作为不可分割的单元参与操作。这就是为什么寄存器中的数据使用一个完整的字,即使只有一个字节就足够了。
对于每个指令集,都有这些类型的定义。例如,一个字节是 8 位或 9 位或其他一些大小。该架构定义了一个词来表示某事。
8086/8088 将一个字节定义为 8 位,将一个字定义为 16 位。您有 16 位寄存器,但您也可以将它们用作 8 位半寄存器 ax 是 16 位寄存器 ah 是 ax 的上半部分,al 是下半部分。不典型,但他们是如何做到的。后来 80x86 EAX 变成了 32 位寄存器,其中 ax 是下半部分,依此类推。由于一个字被定义为 16 位 EAX 被定义为一个双字或双字。后来出现了 64 位寄存器……在这种架构中,64 位被称为四字。
ARM 定义一个字节为 8 位,半字为 16 位,一个字为 32 位。其他人也这样做。
那是在硬件层面。然后你进入编程语言,他们可以自由地更改定义,以及可能想要利用 typedef 或语言中的任何东西来创建或更改其他定义的程序员,并且可以自由地制作他们想要的任何大小。语言大小不必与硬件匹配,有时这样做是个好主意,但他们不必这样做,这取决于为特定目标实现该编译器和/或后端的人。
关于废...
x86 架构使用可变字长指令。这意味着有一些8位指令,一些16位指令,24位,32位等。您可能想从一个寄存器移动到另一个,该指令可能只需要一个字节,但如果您想将一个 16 位值移动到一个寄存器中,这可能需要一个字节来表示我想将一个立即数移动到一个寄存器中,然后再定义两个字节来定义该指令的立即数,总共三个字节。指令集是在内存为 8 位宽时发明的,对于 8 位宽的内存系统,这是有道理的。现在我们使用 32 位和 64 位宽的内存系统,这是非常痛苦的。然而,指令集有 8 位寄存器,ah、al、bh、bl 等,并且可以对 8 位寄存器进行 8 位操作。因此,将 bool 或其他类似的东西设为字节大小以节省一些空间可能是有意义的。无论如何,你已经在没有对齐的情况下将你的内存分割成可变大小,也许是这样。
ARM,考虑传统的 ARM(不是 thumb/thumb2),指令总是 32 位宽,不多不少。寄存器多于 x86 位,它们不分为半寄存器或字节大小的寄存器。你没有像比较等 8 位操作。一切都是 32 位的,因为它是通过寄存器完成的(有一些小的立即数,是的)。如果您的高级语言中有一个变量永远不会小于 -5 或大于 +20,那么您可能希望在高级语言中使用有符号字节以节省一些空间。您会发现有时您必须使用额外的指令来对数据进行符号扩展或屏蔽,以使用 32 位寄存器模拟 8 位操作。节省 3 个字节会花费您 4 个或 8 个或更多。32 位 int 会比有符号字节便宜。
存在对齐问题,只是因为 x86 允许未对齐的访问(比如使用 32 位数据总线从/到地址 0x4 的 32 位读/写),它会花费额外的周期。ARM 和其他人不允许这样做。但是出于同样的原因,即使通过缓存,字节写入也会花费您读取-修改-写入,因为缓存可能是 32 位宽的 ram。要更改一个字节,您必须读取 32 位,修改 8 并写回 32。成本时钟周期。如果您使用 32 位变量而不是 8 位变量,即使该变量可能永远不会超出 -5 到 +20 的范围。你花费了更多的时钟周期。更多的浪费。
现在关于您的问题,为什么具有 8 位寄存器的系统比 32 位系统需要更多的周期来添加 32 位数字。当您很可能上小学并学会用铅笔和纸添加时,您已经知道了这个问题的答案。
如果我想在允许使用 3 位寄存器的世界中添加十进制数 123+789,我可以在一个周期内执行该添加:
110 <--- carry bits/numbers
123
+789
====
912
将其视为基于 32 位寄存器的系统。现在对于基于 8 位寄存器的系统,世界一次只允许一个数字:
一个循环 3 + 9,进位为 0
10
3
+ 9
====
2
进位是1,我们必须执行那个操作才能得到进位,所以我们可以用它作为下一组寄存器的下一个操作的进位,下一个周期2 + 8,我们的进位为1
11
2
+ 8
====
1
进位也是一个,第三个循环 1 + 7,进位为 1
01
1
+ 7
====
9
如果我们需要它,执行是 0...
数字系统无关紧要(以 10 为底,以 2 为底等),它们的工作原理都是一样的。
现在,如果您要问的问题是,例如,我有 2 和 3,并且想将它们加在一起,那么在 64 位处理器中使用两个 64 位寄存器来执行以下操作似乎确实是一种巨大的浪费只需要几列。同样,将它们保存到内存等。对于布尔值或 alu 操作(加法或等),寄存器的宽度无关紧要,它是为该宽度设计的,流水线平均一个时钟。无论如何,它都会执行 64 位加法。是的,这是很多浪费的逻辑空间。见上文,您可以选择使用更少的逻辑但更多的时钟或更多的逻辑和更少的时钟。更多时钟的解决方案也可能涉及更多的内存周期,从而花费更多的时钟。更宽的变量可能会浪费更多的内存,但在更宽的内存系统上使用更少的时钟。这都是一种权衡。现在逻辑在几千兆赫兹中达到最大值,内存非常慢,但是通过使总线更宽和其他并行技巧,您可以使其看起来更快。如果您要以时钟周期为代价来节省逻辑和内存上的钱,您可能无法实时观看 youtube 视频,或者无法看到足够的像素来查看正在发生的事情。或上网,因为绘制通常使用数学函数压缩的字体和图像需要很长时间,用户无法忍受。
我建议你看一下微芯片 pic 指令集
http://en.wikipedia.org/wiki/Microchip_PIC
上页列出的 12 位指令集表。想想您编写的最后一个程序并使用该指令集实现该程序。更好的是,使用 pic 指令集添加三个简单的数字。它只有一个寄存器,要进行数学运算,您必须将一个操作数放入 w 寄存器,使用 f 寄存器执行数学运算,如果您不想弄乱 f 寄存器的内容,可以说,然后您离开导致 w 寄存器。现在,添加另一个 f 寄存器,结果到 w,然后添加第三个寄存器,结果到 w,然后将 w 保存在第四个 f 寄存器中。d = a+b+c。当然,在 arm 或 mips 或其他任何操作上,如果您只需要 a、b、c 来执行一个操作,则您必须执行 3 次读取然后添加,但如果这些操作数中的一个或多个是其他操作的结果,不需要存储到 ram 因为我们有更多的寄存器等,你开始看到规模经济。6502(表面上,除非您了解零页)和其他指令集旨在以时钟周期为代价减少逻辑,减少内存。对于较旧的设计(包括 x86),这是因为逻辑的制造和构建(相对)成本很高,而内存也同样昂贵。
您可以以一条指令为代价,以时钟周期为代价对处理器进行这种简化。
http://en.wikipedia.org/wiki/Single_instruction_set_computer
opencores.org 有几个这样的一个或几个指令处理器。
您提到了 java,如果您要在硬件中构建它,它的虚拟机会以时钟周期为代价(具有可移植性)。有关基于堆栈的处理器的示例(不是 java,只是基于堆栈的解决方案的另一个示例),请参见 opencores 的 zpu。当然不是用 java 发明的,pascal 最初是基于堆栈的伪代码,然后您在目标上实现了该伪代码。small-c 生成的基于堆栈的程序,您为每个目标实现等等。基于堆栈的解决方案非常便携,但以时钟周期为代价。
对您的问题的简短回答是,不要过分关注微观,退后一点,看看整个画面。我们在处理器中使用越来越大的寄存器的原因是因为速度,大多数时候您不会在 64 位处理器上的 alu 操作中使用所有这些地址位或数据位,非常正确,但是对于您确实,那些更宽的寄存器、更宽的总线和存储器会产生巨大的性能差异,足以弥补其他地方的浪费。无论如何,浪费相当便宜,处理器、内存、磁盘空间的成本,如果需要提供相同的用户体验或足够接近,8 位千兆赫系统不会比 64 位千兆赫系统便宜 8 倍相同。
它与总线宽度有关。处理器的每个时钟周期都会根据总线宽度向前移动整个处理器,因此可能无法优化每个操作数类型的寄存器宽度。
通过使用体系结构的存储字节指令(通常将寄存器的低位存储在内存位置),您可以从字大小的寄存器将一个字节的值存储在内存中。
许多早期的计算机以较小的增量存储数据,通常为 4 或 6 位数字。当数据路径很窄并且算术是串行完成时,这工作得相当好,一次一个数字。而且它的存储效率很高(这是一种珍贵的商品)。
存储不再那么珍贵——吞吐量是——并且计算机可以通过更宽的数据路径实现更好的吞吐量。
需要考虑的一件事:
用于处理器设计的“现代”(约 1985 年)RISC 方案考虑到,与处理器芯片的“成本”(在芯片“房地产”中)相比,在处理器芯片上放置大量相当宽的寄存器相对便宜。 “控制逻辑”。
虽然有理由质疑这个论点的全部真实性,但它确实有一些真实的元素,虽然一个寄存器比相同数量的 RAM 贵很多倍(可能是 1000 或更多),但它仍然相对便宜。使寄存器更窄并需要更多周期来完成相同的操作将是错误的经济。相反,设计师试图在一个周期内尽可能多地完成。