(注:问题下方有更新)
看看这个精简的small\u vector基准测试:
#include <cstdlib>
#define NOINLINE __attribute__((noinline))
class Array {
public:
Array() : m_data(m_buffer), m_size(0) {}
~Array() {
// if (m_data != m_buffer) free(m_data);
}
void append(int v) {
if (m_size >= 3) expand();
m_data[m_size++] = v;
}
private:
int *m_data;
int m_size;
int m_buffer[3];
NOINLINE void expand() {}
};
NOINLINE
void add(Array &array) {
array.append(11);
array.append(22);
array.append(33);
}
int main() {
for (int i = 0; i < 1000000000; i++) {
Array array;
add(array);
}
}
(使用NOINLINE是因为这是编译器决定内联原始small_vector代码的方式)
如果此代码是用clang 11编译的,那么如果我取消对数组中已注释行的注释,速度会更快(注意,从不执行自由调用)。在我的机器(i7 8750)上,差异为18%。在快速工作台上。com,差异较小,为5.3%。
我知道这是一个微基准测试,可能会发生疯狂的事情,但是:add
是一个37条指令、120字节的代码,所以它并没有那么小。并且在两个版本中都是一样的。唯一的区别是main
,也没有太大的不同,只是循环编译有点不同。然而,有很大的性能差异,并且具有更多指令/分支的版本运行得更快。如果我运行perf stat-d-d
,我没有看到任何可疑的地方(分支/cache-misses没有显着差异,但仍然,insn/循环差异很大:2.4 vs 3.12):
3939.23 msec task-clock # 1.000 CPUs utilized
10 context-switches # 0.003 K/sec
0 cpu-migrations # 0.000 K/sec
107 page-faults # 0.027 K/sec
13345669446 cycles # 3.388 GHz (38.26%)
32029499558 instructions # 2.40 insn per cycle (45.98%)
6005787151 branches # 1524.610 M/sec (45.99%)
71062 branch-misses # 0.00% of all branches (46.09%)
6000238616 L1-dcache-loads # 1523.202 M/sec (46.20%)
180237 L1-dcache-load-misses # 0.00% of all L1-dcache accesses (46.30%)
35516 LLC-loads # 0.009 M/sec (30.87%)
13655 LLC-load-misses # 38.45% of all LL-cache accesses (30.87%)
not supported L1-icache-loads
545548 L1-icache-load-misses (30.87%)
6003584439 dTLB-loads # 1524.051 M/sec (30.86%)
5290 dTLB-load-misses # 0.00% of all dTLB cache accesses (30.76%)
4583 iTLB-loads # 0.001 M/sec (30.65%)
4222 iTLB-load-misses # 92.12% of all iTLB cache accesses (30.55%)
not supported L1-dcache-prefetches
not supported L1-dcache-prefetch-misses
3.939756460 seconds time elapsed
3.939678000 seconds user
0.000000000 seconds sys
3316.00 msec task-clock # 1.000 CPUs utilized
5 context-switches # 0.002 K/sec
0 cpu-migrations # 0.000 K/sec
110 page-faults # 0.033 K/sec
11235910328 cycles # 3.388 GHz (38.24%)
35013565821 instructions # 3.12 insn per cycle (45.96%)
7002622651 branches # 2111.770 M/sec (45.96%)
59596 branch-misses # 0.00% of all branches (46.02%)
7001546754 L1-dcache-loads # 2111.446 M/sec (46.14%)
143554 L1-dcache-load-misses # 0.00% of all L1-dcache accesses (46.26%)
20608 LLC-loads # 0.006 M/sec (30.88%)
3562 LLC-load-misses # 17.28% of all LL-cache accesses (30.88%)
not supported L1-icache-loads
431694 L1-icache-load-misses (30.88%)
7003243717 dTLB-loads # 2111.958 M/sec (30.88%)
3296 dTLB-load-misses # 0.00% of all dTLB cache accesses (30.82%)
2836 iTLB-loads # 0.855 K/sec (30.70%)
3436 iTLB-load-misses # 121.16% of all iTLB cache accesses (30.58%)
not supported L1-dcache-prefetches
not supported L1-dcache-prefetch-misses
3.316414943 seconds time elapsed
3.312479000 seconds user
0.003995000 seconds sys
注意:如果我在主菜单中手动展开循环,则如下所示:
for (int i = 0; i < 250000000; i++) {
{Array array; add(array);}
{Array array; add(array);}
{Array array; add(array);}
{Array array; add(array);}
}
性能差异保持不变。
你知道差异的原因吗?有什么提示需要检查吗?
以下是asm列表供参考:
401190: 55 push rbp
401191: 41 56 push r14
401193: 53 push rbx
401194: 48 83 ec 20 sub rsp,0x20
401198: bd 00 ca 9a 3b mov ebp,0x3b9aca00
40119d: 4c 8d 74 24 14 lea r14,[rsp+0x14]
4011a2: 48 8d 5c 24 08 lea rbx,[rsp+0x8]
4011a7: 66 0f 1f 84 00 00 00 nop WORD PTR [rax+rax*1+0x0]
4011ae: 00 00
4011b0: 4c 89 74 24 08 mov QWORD PTR [rsp+0x8],r14
4011b5: c7 44 24 10 00 00 00 mov DWORD PTR [rsp+0x10],0x0
4011bc: 00
4011bd: 48 89 df mov rdi,rbx
4011c0: e8 4b ff ff ff call 401110 <add(Array&)>
4011c5: 83 c5 ff add ebp,0xffffffff
4011c8: 75 e6 jne 4011b0 <main+0x20>
4011ca: 31 c0 xor eax,eax
4011cc: 48 83 c4 20 add rsp,0x20
4011d0: 5b pop rbx
4011d1: 41 5e pop r14
4011d3: 5d pop rbp
4011d4: c3 ret
4011b0: 55 push rbp
4011b1: 41 56 push r14
4011b3: 53 push rbx
4011b4: 48 83 ec 20 sub rsp,0x20
4011b8: bd 00 ca 9a 3b mov ebp,0x3b9aca00
4011bd: 48 8d 5c 24 14 lea rbx,[rsp+0x14]
4011c2: 4c 8d 74 24 08 lea r14,[rsp+0x8]
4011c7: eb 0c jmp 4011d5 <main+0x25>
4011c9: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0]
4011d0: 83 c5 ff add ebp,0xffffffff
4011d3: 74 26 je 4011fb <main+0x4b>
4011d5: 48 89 5c 24 08 mov QWORD PTR [rsp+0x8],rbx
4011da: c7 44 24 10 00 00 00 mov DWORD PTR [rsp+0x10],0x0
4011e1: 00
4011e2: 4c 89 f7 mov rdi,r14
4011e5: e8 46 ff ff ff call 401130 <add(Array&)>
4011ea: 48 8b 7c 24 08 mov rdi,QWORD PTR [rsp+0x8]
4011ef: 48 39 df cmp rdi,rbx
4011f2: 74 dc je 4011d0 <main+0x20>
4011f4: e8 37 fe ff ff call 401030 <free@plt>
4011f9: eb d5 jmp 4011d0 <main+0x20>
4011fb: 31 c0 xor eax,eax
4011fd: 48 83 c4 20 add rsp,0x20
401201: 5b pop rbx
401202: 41 5e pop r14
401204: 5d pop rbp
401205: c3 ret
(慢速和快速相同)
401130: 53 push rbx
401131: 48 89 fb mov rbx,rdi
401134: 8b 4f 08 mov ecx,DWORD PTR [rdi+0x8]
401137: 83 f9 03 cmp ecx,0x3
40113a: 7c 0b jl 401147 <add(Array&)+0x17>
40113c: 48 89 df mov rdi,rbx
40113f: e8 cc 00 00 00 call 401210 <Array::expand()>
401144: 8b 4b 08 mov ecx,DWORD PTR [rbx+0x8]
401147: 48 8b 03 mov rax,QWORD PTR [rbx]
40114a: 8d 51 01 lea edx,[rcx+0x1]
40114d: 89 53 08 mov DWORD PTR [rbx+0x8],edx
401150: 48 63 c9 movsxd rcx,ecx
401153: c7 04 88 0b 00 00 00 mov DWORD PTR [rax+rcx*4],0xb
40115a: 8b 4b 08 mov ecx,DWORD PTR [rbx+0x8]
40115d: 83 f9 03 cmp ecx,0x3
401160: 7c 0e jl 401170 <add(Array&)+0x40>
401162: 48 89 df mov rdi,rbx
401165: e8 a6 00 00 00 call 401210 <Array::expand()>
40116a: 8b 4b 08 mov ecx,DWORD PTR [rbx+0x8]
40116d: 48 8b 03 mov rax,QWORD PTR [rbx]
401170: 8d 51 01 lea edx,[rcx+0x1]
401173: 89 53 08 mov DWORD PTR [rbx+0x8],edx
401176: 48 63 c9 movsxd rcx,ecx
401179: c7 04 88 16 00 00 00 mov DWORD PTR [rax+rcx*4],0x16
401180: 8b 4b 08 mov ecx,DWORD PTR [rbx+0x8]
401183: 83 f9 03 cmp ecx,0x3
401186: 7c 0e jl 401196 <add(Array&)+0x66>
401188: 48 89 df mov rdi,rbx
40118b: e8 80 00 00 00 call 401210 <Array::expand()>
401190: 8b 4b 08 mov ecx,DWORD PTR [rbx+0x8]
401193: 48 8b 03 mov rax,QWORD PTR [rbx]
401196: 8d 51 01 lea edx,[rcx+0x1]
401199: 89 53 08 mov DWORD PTR [rbx+0x8],edx
40119c: 48 63 c9 movsxd rcx,ecx
40119f: c7 04 88 21 00 00 00 mov DWORD PTR [rax+rcx*4],0x21
4011a6: 5b pop rbx
4011a7: c3 ret
我设法使差异更大,将main更改为this(这只是一个添加的malloc和免费调用):
void *d;
int main() {
d = malloc(1);
for (int i = 0; i < 1000000000; i++) {
Array array;
add(array);
}
free(d);
}
使用此主功能,慢速版本会变得更慢,慢速和快速之间的差异约为30%!
这个问题来自两个综合问题:
事实上,我可以在我的i5-9600KF Skylake处理器上重现这个问题,并且事件idq.mite
对于慢速版本来说,与@PeterCordes在评论中指出的快速版本相比是相当大的。使用Clang标志-mclasts-venin-32B-边界
似乎可以解决这个问题,因为idq.mite_uops
要小得多。但是,这不足以消除两个版本之间的差异。
此外,像mov DWORD PTR[rbx 0x8]、edx或mov ecx、DWORD PTR[rdi 0x8]这样的指令在慢速版本中速度明显较慢。三个mov DWORD PTR[rax rcx*4],xxx在两个版本中都需要大量时间,但在速度较慢的版本中尤其如此。可以注意到,rax rcx*4指向主函数的堆栈。结果表明,在慢速版本中,rbx和rbx 0x8指向两条不同的缓存线,而在快速版本中,它们指向同一缓存线。此外,在两个版本中,rax rcx*4指向相同的缓存线。使用叮当标志-mstackreallign修复了我机器上两个版本之间的差异(结果时间比快速版本慢,但比慢速版本快)。但是,对于使用此标志的两个版本,对齐方式都非常糟糕。更好的替代方法是用alignas(64)int*m\u数据(64)替换int*m\u数据(用于更便携的代码)。
flags / config | "slow" | "fast"
nothing | 2,950 | 2,418
-mbranches-within-32B-boundaries | 2,868 | 2,472
-mstackrealign | 2,669 | 2,833
-mstackrealign -mbranches-within-32B-boundaries | 2,701 | 2,725
alignas(64) | 2,585 | 2,583
-mbranches-within-32B-boundaries alignas(64) | 2,497 | 2,499
问题内容: 我已经修改了一些Go代码,以解决与我姐夫玩的电子游戏有关的我的好奇心。 本质上,下面的代码模拟了游戏中与怪物的互动,以及他期望他们在失败后掉落物品的频率。我遇到的问题是,我希望这样的一段代码非常适合并行化,但是当我并发添加时,完成所有模拟所花费的时间往往会使原始代码的速度降低4-6倍没有并发。 为了使您更好地理解代码的工作方式,我有三个主要功能:交互功能,它是玩家和怪物之间的简单交互。
问题内容: 假设我键入“ sout”,智能感知应将其扩展为“ System.out.println()”。有没有添加此类模板的方法? 问题答案: 该功能在Eclipse中称为“代码模板”。您可以使用以下方法添加模板: 窗口->首选项-> Java->编辑器->模板。 两篇好文章: 不要编写代码,生成它 自定义模板 另外,这个SO问题: 有用的Eclipse Java代码模板 已映射到,因此您可以通
Java: 如果java以微弱优势击败了C和C#我不会感到惊讶,但速度快了20倍?! 文件的格式如下: 另外,我认为值得注意的是,java在NetBeans中运行时大约需要11秒(即使是在“运行”模式下,而不是在“调试”模式下)。 我也尝试编译为C++而不是C,但没有什么不同。 我对C和C#都使用VS2015。 Java: 好吧,我按照建议重新做了测试: 首先,我在C和C#中都使用了类/struc
为什么注释掉for循环的前两行并取消注释第三行会导致42%的加速? 在时间的背后是非常不同的汇编代码:循环中的13条和7条指令。该平台运行的是视窗7。NET 4.0 x64。代码优化已启用,测试应用程序在VS2010之外运行。[更新:重现项目,用于验证项目设置。] 消除中间布尔值是一个基本的优化,是我1980年代龙书时代最简单的优化之一。在生成 CIL 或 JITing x64 机器代码时,优化是
我使用SSIS包导入一个基本文本文件,它有3个日期字段,有时一些日期字段是空的。 导入的表显示空字段,我想是因为它们是varchar(50)数据类型。但是我需要将该表中的记录插入到另一个表中,其中这些列被定义为Date数据类型。 当我运行insert语句时,目标表中的结果值都显示日期为1900-01-01,而不是NULL或空白。我试图强制该值为null,但无效: 如何使日期列只接受空白或空值?
我最近用Java写了一个计算密集型算法,然后把它翻译成C++。令我吃惊的是,C++的执行速度要慢得多。我现在已经编写了一个更短的Java测试程序,以及一个相应的C++程序-参见下面。我的原始代码具有大量的数组访问功能,测试代码也是如此。C++的执行时间要长5.5倍(请参阅每个程序末尾的注释)。 以下1st21条评论后的结论... null null Java代码: C++代码: