gpt4 book ai didi

performance - 哪种对齐方式会导致这种性能差异

转载 作者:行者123 更新时间:2023-12-05 04:35:08 25 4
gpt4 key购买 nike

有什么问题

我正在对以下代码进行基准测试 for (T& x : v) x = x + x; 其中 T 是 int。使用 mavx2 编译时,性能会根据某些条件波动 2 倍。这不会在 sse4.2

上重现

我想了解发生了什么。

基准测试是如何工作的

我正在使用 Google 基准测试。它旋转循环,直到确定时间为止。

主要的基准测试代码:

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_elements_bench

机器、编译器、选项

  • 处理器:intel 9700k
  • 编译器:clang ~14,从主干构建。
  • 选项:-mavx2 --std=c++20 --stdlib=libc++ -DNDEBUG -g -Werror -Wall -Wextra -Wpedantic -Wno-deprecated-copy -O3

我确实将所有函数都对齐到128来尝试,没有效果。

结果

当复制 2 次时,我得到:

------------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------------
double_elements_0 105 ns 105 ns 6617708
double_elements_1 105 ns 105 ns 6664185

与重复 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_uops,UOPS_ISSUED.ANY,branches,branch-misses

慢速案例

------------------------------------------------------------
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 倍。

但是它有额外的效果,比如取决于加载结果的 uops 的重播,这显然可以解释其余的问题,要么使乱序执行不太能够重叠工作和隐藏延迟,要么直接遇到瓶颈,比如“拆分寄存器”。

ld_blocks.no_sr 计算缓存行拆分加载被临时阻止的次数,因为用于处理拆分访问的所有资源都在使用中。

当加载执行单元检测到加载拆分到缓存行时,它必须将第一部分保存在某处(显然在“拆分寄存器”中),然后访问第二个缓存行。在像您这样的 Intel SnB 系列 CPU 上,第二次访问不需要 RS 再次将负载 uop 分派(dispatch)到端口;加载执行单元只是在几个周期后执行它。 (但大概不能在与第二次访问相同的周期内接受另一个负载。)

拆分加载的额外延迟,以及等待这些加载结果的 uops 的潜在重播,是另一个因素,但这些也是未对齐加载的相当直接的后果。 ld_blocks.no_sr 的大量计数告诉您 CPU 实际上用完了拆分寄存器,否则可能会做更多的工作,但由于未对齐的负载本身而不得不停止,而不仅仅是其他影响。

如果您想调查详细信息,您还可以查找由于 ROB 或 RS 已满而导致的前端停顿,但无法执行拆分加载会使这种情况发生得更多。因此,所有后端停顿可能都是未对齐加载的结果(如果从存储缓冲区提交到 L1d 也是一个瓶颈,则可能是存储。)


On a 100KB I reproduce the issue: 1075ns vs 1412ns. On 1 MB I don't think I see it.

对于大型数组(512 位向量除外),数据对齐通常不会产生太大差异。随着缓存行(2x YMM 向量)到达频率降低,后端有时间处理未对齐加载/存储的额外开销,并且仍然跟上。硬件预取做得很好,它仍然可以最大化每核 L3 带宽。对于适合 L2 但不适合 L1d(如 100kiB)的大小,预计会看到较小的效果。

当然,大多数类型的执行瓶颈都会表现出类似的效果,甚至是像未优化的代码这样简单的事情,它会为数组数据的每个向量执行一些额外的存储/重新加载。因此,这本身并不能证明它是错位导致了适合 L1d 的小尺寸(例如 10 KiB)的减速。但这显然是最明智的结论。


代码对齐或其他前端瓶颈似乎不是问题;根据 idq.dsb_uops,您的大部分 uops 来自 DSB。 (相当多的人不是,但慢速与快速之间的百分比差异不大。)

How can I mitigate the impact of the Intel jcc erratum on gcc?对于像您这样的 Skylake 衍生微体系结构可能很重要;甚至有可能这就是为什么您的 idq.dsb_uops 不接近您的 uops_issued.any 的原因。

关于performance - 哪种对齐方式会导致这种性能差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71090526/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com