当在汇编指令级别分析代码时,鉴于现代CPU不串行或按顺序执行指令,指令指针的位置真正意味着什么?例如,假设以下x64汇编代码:
mov RAX, [RBX]; // Assume a cache miss here.
mov RSI, [RBX + RCX]; // Another cache miss.
xor R8, R8;
add RDX, RAX; // Dependent on the load into RAX.
add RDI, RSI; // Dependent on the load into RSI.
指令指针将在哪条指令上花费大部分时间?我可以为他们所有人想出好的理由:
这是一个好问题,但在我所做的性能调整中,这并不重要。这其实并不重要,因为你要找的是速度缺陷。这些是代码正在做的事情,需要时钟时间,可以做得更好,也可以根本不做。示例:
-花费I/O时间在DLL中查找实际上不需要查找的资源
-花费时间在内存分配例程中,制作和释放可以简单重复使用的对象
-重新计算可以存储的函数中的内容
。。。这只是我脑海中的一小部分
你最大的敌人是一种沾沾自喜的倾向,即说“我不会有意识地写任何bug。我为什么要写?”当然,你知道这就是你测试软件的原因。但是对于速度错误也是一样的,如果你不知道如何找到那些错误,你就假设没有,这是一种说“我的代码没有可能的加速,除非一个分析器可以告诉我如何减少几个周期。”
在我半个世纪的经验中,没有一段代码像第一次写的那样不包含速度错误。此外,还有一个巨大的倍增效应,你删除的每一个速度错误都会使剩下的错误更加明显。作为一个人为的例子,假设错误a占时钟时间的90%,错误B占9%。如果你只修复B,那有什么大不了的——代码要快11%。如果你只修复了一个,那很好——速度快了10倍。但如果你同时修复了这两个问题,那真的很好——速度快了100倍。固定A使B变大。
因此,在性能调优中,您最需要的是找到速度错误,而不是遗漏任何错误。当你完成所有这些之后,你就可以开始循环剃须了。
CPU认为只有架构寄存器(RAX、RBX等)和特定的指令指针(IP)。程序员和编译器瞄准了这种虚构。
然而,正如您所指出的,现代CPU不会连续或按顺序执行。在您的程序员/用户请求IP之前,它就像量子物理一样,IP是正在执行的指令波;所有这些都是为了让处理器能够尽可能快地运行程序。当您请求当前IP时(例如,通过调试器断点或分析器中断),处理器必须重新创建您期望的虚构,以便折叠这种波形(所有“飞行中”指令),将寄存器值收集回架构名称,并为执行调试器例程构建上下文,等等。
在这种情况下,有一个IP指示处理器应恢复执行的指令。在无序执行期间,该指令是尚未完成的最古老的指令,即使在中断时,处理器可能正在获取远远超过该点的指令。
例如,中断可能指示<代码>mov RSI,[RBX RCX] 作为IP,但异或已经执行并完成;然而,当处理器在中断后恢复执行时,它将重新执行异或。
指针可以指向一份普通类型的数据,例如 int、double、char 等,也可以指向一份指针类型的数据,例如 int *、double *、char * 等。 如果一个指针指向的是另外一个指针,我们就称它为 二级指针,或者 指向指针的指针。 假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,它们的关系如下图所示: 将这种关系转换为C语言代码: 指针变
例如,Angular 2内置的属性指令ngClass和ngStyle,可以在任何组件或元素上工作。
我的理解是,在处理器流水线的开始,指令指针(指向要执行的下一条指令的地址)在提取之后由分支预测器更新,以便这个新地址可以在下一个周期被提取。 但是,如果在管道的早期修改了指令指针,这是否会影响当前处于执行阶段的可能依赖于旧指令指针值的指令?例如,在进行时,需要将当前 EIP 推送到堆栈中,但是在分支预测期间更新指令指针时,这不会受到影响吗?
View Example 指令与我们看到的其他指令有不同的语法。 如果你熟悉for...of语句,你会注意到,他们几乎相同。 ngFor允许您指定要迭代的iterable对象,以及在范围内引用每个项的名称。 在我们的示例中,您可以看到该 可用于插值以及属性绑定。 该指令做一些额外的解析,所以当它扩展到模板形式,它看起来有点不同: View Example 请注意,模板元素上有一个奇怪的let-ep