- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
这与以下问题相关,但不相同:Performance optimisations of x86-64 assembly - Alignment and branch prediction,并且与我之前的问题:Unsigned 64-bit to double conversion: why this algorithm from g++略有相关
以下是非真实的测试案例。此素数测试算法不明智。我怀疑任何现实世界的算法都不会执行这么小的内循环那么多次(num
是大约2 ** 50的素数)。在C ++ 11中:
using nt = unsigned long long;
bool is_prime_float(nt num)
{
for (nt n=2; n<=sqrt(num); ++n) {
if ( (num%n)==0 ) { return false; }
}
return true;
}
g++ -std=c++11 -O3 -S
产生以下内容,其中RCX包含
n
,XMM6包含
sqrt(num)
。请参阅我以前的文章以获取剩余的代码(由于RCX不会变得足够大而不能被视为带负号的负号,因此在此示例中不会执行)。
jmp .L20
.p2align 4,,10
.L37:
pxor %xmm0, %xmm0
cvtsi2sdq %rcx, %xmm0
ucomisd %xmm0, %xmm6
jb .L36 // Exit the loop
.L20:
xorl %edx, %edx
movq %rbx, %rax
divq %rcx
testq %rdx, %rdx
je .L30 // Failed divisibility test
addq $1, %rcx
jns .L37
// Further code to deal with case when ucomisd can't be used
std::chrono::steady_clock
计时。我一直在获得怪异的性能更改:仅添加或删除其他代码。我最终将其归结为对齐问题。命令
.p2align 4,,10
尝试对齐2 ** 4 = 16字节边界,但最多仅使用10个字节的填充来对齐,我想在对齐和代码大小之间取得平衡。
.p2align 4,,10
条指令替换
nop
。以下散点图显示了20次运行中最快的15次,以秒为单位的时间,x轴上的填充字节数:
objdump
开始,无填充,pxor指令将出现在偏移量0x402f5f处。在笔记本电脑上运行,Sandybridge i5-3210m,禁用涡轮增压,我发现
sqrt(num)
缓存在64位整数中,然后使循环完全基于整数,我消除了问题-对齐现在完全没有区别。
最佳答案
这是我在Skylake上找到的相同循环的内容。用于在硬件is on github上重现我的测试的所有代码。
我根据对齐方式观察到三个不同的性能级别,而OP实际只看到了两个主要性能级别。级别非常独特且可重复2:
我们在这里看到三个不同的性能级别(该模式从偏移32开始重复),我们将其称为区域1、2和3,从左到右(区域2分成横跨区域3的两部分)。最快的区域(1)从偏移量0到8,中间的区域(2)从9-18和28-31,最慢的区域(3)从19-27。每个区域之间的差异接近或正好是1个循环/迭代。
根据性能计数器,最快的区域与其他两个区域有很大的不同:
所有指令均从传统解码器而不是DSB1传递。
对于循环的每次迭代,正好有2个解码器<->微码开关(idq_ms_switches)。
另一方面,两个较慢的区域非常相似:
所有指令都是从DSB(uop缓存)传递的,而不是从旧式解码器传递的。
循环的每次迭代恰好有3个解码器<->微码开关。
由于偏移问题,当偏移量从8变为9时,从最快的区域过渡到中间区域的过程恰好与循环开始适合uop缓冲区的时间相对应。您用与彼得回答中完全相同的方式来计算:
偏移量8:
LSD? <_start.L37>:
ab 1 4000a8: 66 0f ef c0 pxor xmm0,xmm0
ab 1 4000ac: f2 48 0f 2a c1 cvtsi2sd xmm0,rcx
ab 1 4000b1: 66 0f 2e f0 ucomisd xmm6,xmm0
ab 1 4000b5: 72 21 jb 4000d8 <_start.L36>
ab 2 4000b7: 31 d2 xor edx,edx
ab 2 4000b9: 48 89 d8 mov rax,rbx
ab 3 4000bc: 48 f7 f1 div rcx
!!!! 4000bf: 48 85 d2 test rdx,rdx
4000c2: 74 0d je 4000d1 <_start.L30>
4000c4: 48 83 c1 01 add rcx,0x1
4000c8: 79 de jns 4000a8 <_start.L37>
...???a?
或
...???b?
(每个集合覆盖32个字节,又称为
0x20
),而1表示方式1(最多3个)。
test
指令无处可去,所以这会从uop缓存中消失,这3种方式都用光了。
00000000004000a9 <_start.L37>:
ab 1 4000a9: 66 0f ef c0 pxor xmm0,xmm0
ab 1 4000ad: f2 48 0f 2a c1 cvtsi2sd xmm0,rcx
ab 1 4000b2: 66 0f 2e f0 ucomisd xmm6,xmm0
ab 1 4000b6: 72 21 jb 4000d9 <_start.L36>
ab 2 4000b8: 31 d2 xor edx,edx
ab 2 4000ba: 48 89 d8 mov rax,rbx
ab 3 4000bd: 48 f7 f1 div rcx
cd 1 4000c0: 48 85 d2 test rdx,rdx
cd 1 4000c3: 74 0d je 4000d2 <_start.L30>
cd 1 4000c5: 48 83 c1 01 add rcx,0x1
cd 1 4000c9: 79 de jns 4000a9 <_start.L37>
test
指令已滑入下一个32B行(
cd
行),因此所有内容都适合uop缓存。
div
尝试了一些更简单的测试,您可以使用更简单的循环来重现此内容,而无需任何浮点数。它对您放入循环中的其他随机变量很奇怪并且很敏感。
ALIGN 32
<add some nops here to swtich between DSB and MITE>
.top:
add r8, r9
xor eax, eax
div rbx
xor edx, edx
times 5 add eax, eax
dec rcx
jnz .top
add r8, r9
指令,该指令实际上并未与循环的其余部分进行交互,从而加快了MITE版本(但没有DSB版本)的工作。
00000000004000b2 <_start.L37>:
ab 1 4000b2: 66 0f ef c0 pxor xmm0,xmm0
ab 1 4000b6: f2 48 0f 2a c1 cvtsi2sd xmm0,rcx
ab 1 4000bb: 66 0f 2e f0 ucomisd xmm6,xmm0
ab 1 4000bf: 72 21 jb 4000e2 <_start.L36>
cd 1 4000c1: 31 d2 xor edx,edx
cd 1 4000c3: 48 89 d8 mov rax,rbx
cd 2 4000c6: 48 f7 f1 div rcx
cd 3 4000c9: 48 85 d2 test rdx,rdx
cd 3 4000cc: 74 0d je 4000db <_start.L30>
cd 3 4000ce: 48 83 c1 01 add rcx,0x1
cd 3 4000d2: 79 de jns 4000b2 <_start.L37>
00000000004000b3 <_start.L37>:
ab 1 4000b3: 66 0f ef c0 pxor xmm0,xmm0
ab 1 4000b7: f2 48 0f 2a c1 cvtsi2sd xmm0,rcx
ab 1 4000bc: 66 0f 2e f0 ucomisd xmm6,xmm0
cd 1 4000c0: 72 21 jb 4000e3 <_start.L36>
cd 1 4000c2: 31 d2 xor edx,edx
cd 1 4000c4: 48 89 d8 mov rax,rbx
cd 2 4000c7: 48 f7 f1 div rcx
cd 3 4000ca: 48 85 d2 test rdx,rdx
cd 3 4000cd: 74 0d je 4000dc <_start.L30>
cd 3 4000cf: 48 83 c1 01 add rcx,0x1
cd 3 4000d3: 79 de jns 4000b3 <_start.L37>
ab
高速缓存行,但偏移量为19的情况下只有3条指令。如果我们假设DSB只能从一个缓存集中将uops传递到IDQ,则这意味着在偏移18场景中某个时刻可以比在19场景中更早地发出并执行一个uop(例如, IDQ为空)。具体取决于uop在周围uop流中所连接的端口,这可能会使环路延迟一个周期。实际上,区域2和3之间的差约为1个周期(在误差范围内)。
div
的简单循环以相同的周期计数执行,但仍分别显示DSB和MITE路径的3和2开关。因此,这是正常现象,并不直接意味着经济放缓。
+----------------------------+----------+----------+----------+
| | Region 1 | Region 2 | Region 3 |
+----------------------------+----------+----------+----------+
| cycles: | 7.7e8 | 8.0e8 | 8.3e8 |
| uops_executed_stall_cycles | 18% | 24% | 23% |
| exe_activity_1_ports_util | 31% | 22% | 27% |
| exe_activity_2_ports_util | 29% | 31% | 28% |
| exe_activity_3_ports_util | 12% | 19% | 19% |
| exe_activity_4_ports_util | 10% | 4% | 3% |
+----------------------------+----------+----------+----------+
frontend_retired.latency_ge_8
,它定义为:
div
指令似乎有点象-甚至一个只有一个
div
的简单循环显示:
FE Frontend_Bound: 57.59 % [100.00%]
BAD Bad_Speculation: 0.01 %below [100.00%]
BE Backend_Bound: 0.11 %below [100.00%]
RET Retiring: 42.28 %below [100.00%]
div
操作生成的所有微指令的能力的限制。 Toplev可能会弄错这个真正的错误,因为(1)可能是微码定序器传递的微指令未在
frontend_retired.latency...
计数器中计数,因此每个
div
操作都会导致该事件对所有后续指令进行计数(即使是尽管在此期间CPU处于忙碌状态-没有真正的停顿),或者(2)微代码定序器可能实质上将所有ups都“提前”交付,向IDQ猛击约36 oups,此时它没有交付直到
div
完成为止。
toplev
以获得提示:
ms_switches
的惩罚增加(因为它们每次迭代产生3,而传统路径产生2;在内部,
toplev
估计为2这样的开关在前端的循环惩罚,当然,这些惩罚是否真的减慢了速度,这取决于指令队列和其他因素的复杂方式,如上所述,使用
div
的简单循环不会显示任何区别在DSB和MITE路径之间,需要执行带有附加指令的循环,因此,可能是多余的开关气泡被更简单的循环吸收了(其中,由
div
生成的所有uops的后端处理是主要因素),但是一旦您在循环中添加其他工作,开关至少会成为
div
和non-div`工作之间过渡期间的一个因素。
div
4 uops)还是从微码定序器(似乎每个
div
32 uops)传递,尽管它随输入值的变化而变化。
div
操作)-但我们不知道这些uops是什么(尽管我们可以看到它们的端口分布)。所有这些使行为变得相当不透明,但是我认为这可能归结于MS交换机前端拥塞,或者uop交付流程中的细微差异导致了不同的调度决策,最终使MITE订单成为了主订单。
prime=1000000000000037
进行的典型测试的分支预测率为〜99.999997%,在整个运行过程中仅反映了3个错误预测的分支(可能在第一次遍历循环和最后一次迭代时)。
关于performance - 涉及英特尔SnB系列CPU上涉及微编码指令的循环的分支对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53021446/
我是 PHP 新手。我一直在脚本中使用 for 循环、while 循环、foreach 循环。我想知道 哪个性能更好? 选择循环的标准是什么? 当我们在另一个循环中循环时应该使用哪个? 我一直想知道要
我在高中的编程课上,我的作业是制作一个基本的小计和顶级计算器,但我在一家餐馆工作,所以制作一个只能让你在一种食物中读到。因此,我尝试让它能够接收多种食品并将它们添加到一个价格变量中。抱歉,如果某些代码
这是我正在学习的一本教科书。 var ingredients = ["eggs", "milk", "flour", "sugar", "baking soda", "baking powder",
我正在从字符串中提取数字并将其传递给函数。我想给它加 1,然后返回字符串,同时保留前导零。我可以使用 while 循环来完成此操作,但不能使用 for 循环。 for 循环只是跳过零。 var add
编辑:我已经在程序的输出中进行了编辑。 该程序要求估计给定值 mu。用户给出一个值 mu,同时还提供了四个不等于 1 的不同数字(称为 w、x、y、z)。然后,程序尝试使用 de Jaeger 公式找
我正在编写一个算法,该算法对一个整数数组从末尾到开头执行一个大循环,其中包含一个 if 条件。第一次条件为假时,循环可以终止。 因此,对于 for 循环,如果条件为假,它会继续迭代并进行简单的变量更改
现在我已经习惯了在内存非常有限的情况下进行编程,但我没有答案的一个问题是:哪个内存效率更高;- for(;;) 或 while() ?还是它们可以平等互换?如果有的话,还要对效率问题发表评论! 最佳答
这个问题已经有答案了: How do I compare strings in Java? (23 个回答) 已关闭 8 年前。 我正在尝试创建一个小程序,我可以在其中读取该程序的单词。如果单词有 6
这个问题在这里已经有了答案: python : list index out of range error while iteratively popping elements (12 个答案) 关
我正在尝试向用户请求 4 到 10 之间的整数。如果他们回答超出该范围,它将进入循环。当用户第一次正确输入数字时,它不会中断并继续执行 else 语句。如果用户在 else 语句中正确输入数字,它将正
我尝试创建一个带有嵌套 foreach 循环的列表。第一个循环是循环一些数字,第二个循环是循环日期。我想给一个日期写一个数字。所以还有另一个功能来检查它。但结果是数字多次写入日期。 Out 是这样的:
我想要做的事情是使用循环创建一个数组,然后在另一个类中调用该数组,这不会做,也可能永远不会做。解决这个问题最好的方法是什么?我已经寻找了所有解决方案,但它们无法编译。感谢您的帮助。 import ja
我尝试创建一个带有嵌套 foreach 循环的列表。第一个循环是循环一些数字,第二个循环是循环日期。我想给一个日期写一个数字。所以还有另一个功能来检查它。但结果是数字多次写入日期。 Out 是这样的:
我正在模拟一家快餐店三个多小时。这三个小时分为 18 个间隔,每个间隔 600 秒。每个间隔都会输出有关这 600 秒内发生的情况的统计信息。 我原来的结构是这样的: int i; for (i=0;
这个问题已经有答案了: IE8 for...in enumerator (3 个回答) How do I check if an object has a specific property in J
哪个对性能更好?这可能与其他编程语言不一致,所以如果它们不同,或者如果你能用你对特定语言的知识回答我的问题,请解释。 我将使用 c++ 作为示例,但我想知道它在 java、c 或任何其他主流语言中的工
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
我是 C 编程和编写代码的新手,以确定 M 测试用例的质因数分解。如果我一次只扫描一次,该功能本身就可以工作,但是当我尝试执行 M 次时却惨遭失败。 我不知道为什么 scanf() 循环有问题。 in
这个问题已经有答案了: JavaScript by reference vs. by value [duplicate] (4 个回答) 已关闭 3 年前。 我在使用 TSlint 时遇到问题,并且理
我尝试在下面的代码中添加 foreach 或 for 循环,以便为 Charts.js 创建多个数据集。这将允许我在此折线图上创建多条线。 我有一个 PHP 对象,我可以对其进行编码以稍后填充变量,但
我是一名优秀的程序员,十分优秀!