我正在为(T
我想了解发生了什么事。
主要基准代码:
using T = int;
constexpr std::size_t size = 10'000 / sizeof(T);
NOINLINE std::vector<T> const& data()
{
static std::vector<T> res(size, T{2});
return res;
}
INLINE void double_elements_bench(benchmark::State& state)
{
auto v = data();
for (auto _ : state) {
for (T& x : v) x = x + x;
benchmark::DoNotOptimize(v.data());
}
}
然后,我从基准测试驱动程序的多个实例调用double\u elements\u bench。
我确实将所有函数与128对齐以进行尝试,但没有效果。
当复制2次时,我得到:
------------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------------
double_elements_0 105 ns 105 ns 6617708
double_elements_1 105 ns 105 ns 6664185
Vs重复3次:
------------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------------
double_elements_0 64.6 ns 64.6 ns 10867663
double_elements_1 64.5 ns 64.5 ns 10855206
double_elements_2 64.5 ns 64.5 ns 10868602
这也会在更大的数据量上复制。
我寻找我知道的与代码对齐相关的计数器
LSD缓存(几年前由于某些安全问题在我的机器上关闭)、DSB缓存和分支预测器:
<代码>LSD。UOPS,idq。dsb\U uops,uops\U发布。任何、分支、分支未命中
慢速情况
------------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------------
double_elements_0 105 ns 105 ns 6663885
double_elements_1 105 ns 105 ns 6632218
Performance counter stats for './transform_alignment_issue':
0 LSD.UOPS
13,830,353,682 idq.dsb_uops
16,273,127,618 UOPS_ISSUED.ANY
761,742,872 branches
34,107 branch-misses # 0.00% of all branches
1.652348280 seconds time elapsed
1.633691000 seconds user
0.000000000 seconds sys
快速案例
------------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------------
double_elements_0 64.5 ns 64.5 ns 10861602
double_elements_1 64.5 ns 64.5 ns 10855668
double_elements_2 64.4 ns 64.4 ns 10867987
Performance counter stats for './transform_alignment_issue':
0 LSD.UOPS
32,007,061,910 idq.dsb_uops
37,653,791,549 UOPS_ISSUED.ANY
1,761,491,679 branches
37,165 branch-misses # 0.00% of all branches
2.335982395 seconds time elapsed
2.317019000 seconds user
0.000000000 seconds sys
在我看来,两者都差不多。
代码:https://github.com/DenisYaroshevskiy/small_benchmarks/blob/ade1ed42fc2113f5ad0a4313dafff5a81f9a0d20/transform_alignment_issue.cc#L1
我认为这可能是对malloc返回的数据的对齐
0x4f2720为快速,0x8e9310为慢速
因此,由于clang没有对齐,我们得到了未对齐的读/写。我在一个对齐的变换上进行了测试-似乎没有这种变化。
有没有办法证实这一点?
是的,数据错位可以解释您对适合L1d的小数组的2倍减速。您会希望,由于每个其他加载/存储都是缓存行拆分,如果拆分的加载或存储需要2次访问L1d而不是1次,它可能只会减慢1.5倍,而不是2倍。
但它有额外的影响,比如根据加载结果重播UOP,这显然是问题的其余部分的原因,要么使无序的exec无法重叠工作并隐藏延迟,要么直接遇到瓶颈,如“拆分寄存器”。
<代码>ld\U块。no\u sr统计缓存线拆分加载被临时阻止的次数,因为用于处理拆分访问的所有资源都在使用中。
当负载执行单元检测到负载跨缓存线拆分时,它必须将第一部分保存在某个位置(显然是在“拆分寄存器”中),然后访问第二条缓存线。在像您这样的Intel SnB系列CPU上,这种第二次访问不需要RS再次将加载uop发送到端口;负载执行单元只是在几个周期后执行。(但可能无法在与第二次访问相同的周期内接受另一个负载。)
另一个因素是拆分负载的额外延迟,以及UOP等待这些负载结果的潜在重播,但这些也是未对齐负载的直接后果。ld\u块的大量计数。no\u sr告诉您,CPU实际上用完了拆分寄存器,本来可以做更多的工作,但由于负载本身未对齐,而不仅仅是其他影响,不得不暂停。
如果您想调查详细信息,还可以查找由于ROB或RS已满而导致的前端失速,但无法执行拆分负载会使这种情况发生得更多。因此,所有后端暂停可能都是未对齐加载的结果(如果从存储缓冲区到L1d的提交也是一个瓶颈,则可能是存储)
在100KB上,我复制了这个问题:1075ns对1412ns。在1 MB上,我想我看不到它。
数据对齐通常不会对大型阵列产生太大的影响(512位向量除外)。由于缓存线(2x YMM向量)到达的频率较低,后端有时间处理未对齐的加载/存储带来的额外开销,并且仍能保持不变。硬件预取工作做得足够好,它仍然可以最大限度地利用每核L3带宽。预计适合L2但不适合L1d(如100kiB)的尺寸会产生较小的影响。
当然,大多数类型的执行瓶颈都会显示出类似的效果,即使是像未优化的代码这样简单的东西,它会为数组数据的每个向量做一些额外的存储/重新加载。因此,仅此并不能证明是错位导致了确实适合L1d的小尺寸的速度变慢,例如您的10 KiB。但这显然是最明智的结论。
代码对齐或其他前端瓶颈似乎不是问题所在;根据idq,大多数UOP来自DSB。dsb\U uops。(有很大一部分人不是,但慢与快之间的百分比差异不大。)
如何减轻“英特尔jcc勘误表”对gcc的影响?在Skylake衍生的微体系结构(如您的微体系结构)上可能很重要;甚至有可能这就是为什么你的idq。dsb\U uops与您发布的uops\U不太接近。任何 。
问题内容: 我对Java线程技术比较陌生,并且我注意到,每次使用Thread.sleep()时,我都必须捕获InterrupetdException。 哪种行为会导致这种情况,并且在具有监视器线程的简单应用程序中,我可以忽略该异常吗? 问题答案: 好吧,如果其他一些线程调用thread.interupt(),则在该线程处于休眠状态时,您将获得Exception。是的,您可能只需将try..catc
在哪种情况下会发生这种情况?
我正在尝试使用printf来获得以下编程输出: 关键规格是: 字段宽度始终为4 符号总是左对齐 数字总是对齐的 零没有符号 到目前为止,我还不能提出一个能够给出所需结果的printf语句。我最接近的是: 它产生: 有没有一种方法可以做到这一点,而不必做任何繁琐的字符串操作?
本文向大家介绍字符串拼接有哪些方式?哪种性能好?相关面试题,主要包含被问及字符串拼接有哪些方式?哪种性能好?时的应答技巧和注意事项,需要的朋友参考一下 性能最好的是连接: 继续补充:
我一直在尝试编写一个程序来实现任意域上的多项式,一种数学结构。我选择了Haskell作为编程语言,我使用了语言扩展。但是,我不明白为什么GHCi不能推导出的约束条件。 在我看来,保证是的实例,这意味着是的实例。所以调用就像调用一样,应该是合理的。此外,我已经编写了作为约束,并且的构造函数具有的形状,因此它还应该知道的类型是的实例。 显然,译员的想法不同。我哪里搞错了?