使用SIMD内在函数时如何将依赖于输入的热数据保留在寄存器中

时间:2016-09-21 07:03:20

标签: c++ x86 simd intrinsics micro-optimization

我正在尝试使用英特尔SIMD内在函数来加速查询答案程序。假设query_cnt取决于输入,但总是小于SIMD寄存器计数(即有足够的SIMD寄存器来保存它们)。由于查询是我的应用程序中的热门数据,而不是每次需要时加载它们,我可以先加载它们并将它们始终保存在寄存器中吗?

假设查询为float类型,并且支持AVX256。现在我必须使用类似的东西:

std::vector<__m256> vec_queries(query_cnt / 8);
for (int i = 0; i < query_cnt / 8; ++i) {
    vec_queries[i] = _mm256_loadu_ps((float const *)(curr_query_ptr)); 
    curr_query_ptr += 8;
}

我知道这不是一个好习惯,因为存在潜在的负载/存储开销,但至少有一点点vec_queries[i]可以优化以便它们可以保存在寄存器中,但我仍然认为这不是一个好方法。

有更好的想法吗?

1 个答案:

答案 0 :(得分:0)

从您发布的代码示例中,您似乎只是在做一个可变长度的memcpy。根据编译器的作用以及周围的代码,您可以通过实际调用memcpy获得更好的结果。例如对于大小为16B倍数的对齐副本,向量循环与rep movsb之间的均衡点可能低至Intel Haswell的~128字节。查看英特尔优化手册,了解有关memcpy的一些实施说明,以及针对几种不同策略的大小与周期的图表。 (标签维基中的链接)。

你没有说出什么是CPU,所以我只是假设最近的英特尔。

我觉得你太担心登记了。在L1缓存中遇到的负载非常便宜。 Haswell(和Skylake)每个时钟可以执行两次__m256次加载(并且在同一周期内存储)。在此之前,Sandybridge / IvyBridge每个时钟可以执行两次内存操作,其中一个最多只能存储一个。或者在理想条件下(256b加载/存储),它们可以管理每个时钟2x 16B加载和1x 16B存储。因此,加载/存储256b向量比使用Haswell更昂贵,但如果它们在L1高速缓存中对齐并且很热,则仍然非常便宜。

我在评论中提到GNU C global register variables可能是一种可能性,但主要是在&#34;这在理论上是技术上可行的&#34;感。您可能不希望在程序的整个运行时间内使用专用于此目的的多个向量寄存器(包括库函数调用,因此您必须重新编译它们)。

实际上,只需确保编译器可以内联(或至少在优化时看到)您在任何重要循环中使用的每个函数的定义。这样就可以避免在函数调用中溢出/重载向量寄存器(因为Windows和System V x86-64 ABI都没有调用保留的YMM(__m256)寄存器)。

请参阅Agner Fog's microarch pdf以了解更多有关现代CPU的微架构细节的信息,至少可以通过实验和调整来衡量这些细节。