【发布时间】:2011-11-08 06:54:48
【问题描述】:
我注意到有时 MSVC 2010 根本不会重新排序 SSE 指令。我以为我不必关心循环内的指令顺序,因为编译器处理得最好,但似乎并非如此。
我应该怎么想?什么决定了最佳的指令顺序?我知道有些指令比其他指令具有更高的延迟,并且有些指令可以在 cpu 级别上并行/异步运行。哪些指标与上下文相关?我在哪里可以找到它们?
我知道我可以通过分析避免这个问题,但是这样的分析器很昂贵(VTune XE)并且我想知道它背后的理论,而不仅仅是经验结果。
我还应该关心软件预取 (_mm_prefetch) 还是我可以假设 cpu 会比我做得更好?
假设我有以下功能。我应该交错一些指令吗?我应该在流之前进行存储,按顺序进行所有加载,然后进行计算等...?我是否需要考虑 USWC 与非 USWC,以及时间与非时间?
auto cur128 = reinterpret_cast<__m128i*>(cur);
auto prev128 = reinterpret_cast<const __m128i*>(prev);
auto dest128 = reinterpret_cast<__m128i*>(dest;
auto end = cur128 + count/16;
while(cur128 != end)
{
auto xmm0 = _mm_add_epi8(_mm_load_si128(cur128+0), _mm_load_si128(prev128+0));
auto xmm1 = _mm_add_epi8(_mm_load_si128(cur128+1), _mm_load_si128(prev128+1));
auto xmm2 = _mm_add_epi8(_mm_load_si128(cur128+2), _mm_load_si128(prev128+2));
auto xmm3 = _mm_add_epi8(_mm_load_si128(cur128+3), _mm_load_si128(prev128+3));
// dest128 is USWC memory
_mm_stream_si128(dest128+0, xmm0);
_mm_stream_si128(dest128+1, xmm1);
_mm_stream_si128(dest128+2, xmm2);;
_mm_stream_si128(dest128+3, xmm3);
// cur128 is temporal, and will be used next time, which is why I choose store over stream
_mm_store_si128 (cur128+0, xmm0);
_mm_store_si128 (cur128+1, xmm1);
_mm_store_si128 (cur128+2, xmm2);
_mm_store_si128 (cur128+3, xmm3);
cur128 += 4;
dest128 += 4;
prev128 += 4;
}
std::swap(cur, prev);
【问题讨论】:
-
我认为这个问题的答案必须在测量测试中。尽管 x86 已经有 OOE 很长一段时间了,但无论排序如何,它都可以很好地处理这种情况。
-
@Skizz - 我知道,但在某种程度上,这并没有太大的区别谁实际上是在做这件事。如果是两个都可以做到而只有一个在做的情况下,如果你努力强迫另一个人也这样做,那么你根本不会注意到任何区别。跨度>
-
为什么你需要一个昂贵的分析器呢?你可以很容易地手动计时。只需获取当前时间,将代码运行数十亿次,然后再次获取时间,然后除以得到每次迭代所花费的时间。
-
我同意@awoodland。重新排序指令不仅浪费时间,而且您不知道其他机器上正在使用什么指令集。什么版本的 SSE 会有所作为。您可以尝试以几种方式改组说明,看看是否有任何变化。我对此表示怀疑。
-
@Tavison:真的编写过 SSE 代码吗?它对排序、缓存效果和其他任何东西都非常敏感。说这是浪费时间是愚蠢的。您通常只在关心性能时才使用 SSE,然后高效执行指令并不是“浪费时间”。
标签: c++ optimization sse simd micro-optimization