最近为了好玩,我在16位DOS上编写了一些东西。我看到很多人提到,远指针比近指针慢,为了避免它们。
为什么?
从装配的角度来看,这是有意义的。涉及到几项额外的指导。您必须存储段寄存器的旧值,将新段存储到寄存器中,然后将其移到CS或DS中(即时模式存储在8086中不是有效的操作码)。然后,你可以做你需要做的任何事情在这一段。之后,你必须恢复旧的价值。
听起来似乎很多,但实际上,这并没有消耗太多的周期。我想,如果您使用的每个指针都位于不同的段中,这可能会加起来,但是数据通常是分组的。所以,除非你在整个地方弹跳,因为其他原因,这是缓慢的,点球不应该那么糟糕。如果你必须使用DRAM,那应该是成本的主导,对吗?
我觉得这个故事还有很多,我很难找到它。希望有个8086巫师能记得这些东西。
澄清:我感兴趣的是实际的16位处理器,如8086和80286的真实模式。
发布于 2022-07-28 07:30:26
为什么远指针速度慢?
段寄存器负载太复杂,无法将核心的前端转换为微操作。相反,它们被存储在一个小ROM中的微型操作系统“模仿”。这从一些分支开始(它是哪种CPU模式?),通常这些分支无法从CPU的分支预测中获益,从而导致延迟/s。
为了避免段寄存器负载(例如,当多次使用相同的远指针时),软件倾向于使用更多的段寄存器(例如倾向于使用ES、FS和GS);这在指令中增加了更多的前缀(段覆盖前缀)。这些额外的前缀也可以减慢指令解码速度。
--我猜如果您使用的每个指针都位于不同的段中,这可能会加起来,但是数据通常是分组的。
编译器没那么聪明。如果一小部分代码使用4个far指针,而这些指针恰好都使用相同的段,那么编译器将不知道它们都在同一个段中,并且不管如何,都会执行昂贵的段寄存器加载。要解决这个问题,您可以将数据描述为一个结构(例如,指向具有4个字段而不是4个不同指针的结构的一个指针);但这需要程序员以不同的方式编写软件。
例如,如果您执行类似于“into (Void){ int a;返回栏(&a) }”的操作,那么ss可能会被传递到堆栈上,然后被调用者(bar)会将其加载到另一个段寄存器中,因为bar()必须假定指针可以指向任何位置。
另一个问题是,有时数据大于段(例如,一个不适合64 KiB段的100000字节数组);因此,某个人(程序员或编译器)必须计算和加载不同的段来访问(部分)相同的数据。所有指针算法都可能需要考虑到这一点(例如,看起来微不足道的pointer++;可能会变得更像offset++; segment += (offset >> 4); offset &= 0x000F;,从而导致段寄存器负载)。
,如果你必须使用DRAM,那应该是成本的主导,对吗?
对于真正的模式,您被限制在大约640 KiB的内存中,并且CPU中的缓存通常要大得多,所以您可以预期每个内存访问都会受到高速缓存的影响。在某些情况下(例如,级联湖CPU,每个核心有一个MiB of L2缓存),您甚至不会使用L3缓存(这将是L2的全部命中)。
您还可以预期,段寄存器加载比缓存命中更昂贵。
希望一个8086年的巫师在那些记得这些东西的人身边徘徊。
当人们说“分割很糟糕,应该避免”时,他们并没有想到一个已经过时了40年(8086)的CPU,他们想到的是本世纪相关的CPU。他们中的一些人也可能考虑的不仅仅是性能(尤其是对于汇编语言程序员来说,分段是一个烦人/额外的负担)。
https://stackoverflow.com/questions/73146953
复制相似问题