C++ 使用SIMD内部函数时,如何在寄存器中保留与输入相关的热数据

C++ 使用SIMD内部函数时,如何在寄存器中保留与输入相关的热数据,c++,x86,simd,intrinsics,micro-optimization,C++,X86,Simd,Intrinsics,Micro Optimization,我正在尝试使用Intel SIMD Intrinsic来加速查询-应答程序。假设query\u cnt依赖于输入,但始终小于SIMD寄存器计数(即有足够的SIMD寄存器容纳它们)。由于查询是我的应用程序中的热门数据,我是否可以先加载它们并将它们始终保存在寄存器中,而不是每次需要时都加载它们 假设查询是float类型,并且支持AVX256。现在,我必须使用类似于: std::vector<__m256> vec_queries(query_cnt / 8); for (int i =

我正在尝试使用Intel SIMD Intrinsic来加速查询-应答程序。假设
query\u 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;
}
std::向量向量查询(query\u cnt/8);
对于(int i=0;i
我知道这不是一个好的做法,因为存在潜在的加载/存储开销,但至少有一点可能
vec_查询[I]
可以优化,以便将它们保存在寄存器中,但我仍然认为这不是一个好方法


有更好的想法吗?

从您发布的代码示例来看,您似乎只是在做一个可变长度的memcpy。根据编译器的功能和周围的代码,只需实际调用
memcpy
可能会得到更好的结果。e、 g.对于大小为16B倍数的对齐副本,在Intel Haswell上,向量循环和
rep movsb
之间的盈亏平衡点可能低至~128字节。查看英特尔的优化手册,了解memcpy的一些实施说明,以及两种不同策略的大小与周期图。(标记wiki中的链接)

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

我觉得你太担心登记册了。在一级缓存中命中的负载非常便宜。Haswell(和Skylake)每个时钟可以进行两次m256加载(并在同一周期内进行存储)。在此之前,Sandybridge/IvyBridge可以在每个时钟上执行两个内存操作,其中最大一个是存储。或者在理想条件下(256b加载/存储),它们可以管理每个时钟2个16B加载和1个16B存储。因此,加载/存储256b向量比在Haswell上更昂贵,但如果它们在一级缓存中对齐且处于热状态,则仍然非常便宜

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

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


请参阅了解更多有关现代CPU微体系结构的详细信息,至少是可以通过实验测量和调整的详细信息。

从您发布的代码示例中,看起来您只是在做一个可变长度的memcpy。根据编译器的功能和周围的代码,只需实际调用
memcpy
可能会得到更好的结果。e、 g.对于大小为16B倍数的对齐副本,在Intel Haswell上,向量循环和
rep movsb
之间的盈亏平衡点可能低至~128字节。查看英特尔的优化手册,了解memcpy的一些实施说明,以及两种不同策略的大小与周期图。(标记wiki中的链接)

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

我觉得你太担心登记册了。在一级缓存中命中的负载非常便宜。Haswell(和Skylake)每个时钟可以进行两次m256加载(并在同一周期内进行存储)。在此之前,Sandybridge/IvyBridge可以在每个时钟上执行两个内存操作,其中最大一个是存储。或者在理想条件下(256b加载/存储),它们可以管理每个时钟2个16B加载和1个16B存储。因此,加载/存储256b向量比在Haswell上更昂贵,但如果它们在一级缓存中对齐且处于热状态,则仍然非常便宜

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

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


请参阅以了解更多有关现代CPU微体系结构的详细信息,至少是可以通过实验测量和调整的详细信息。

您是否在循环中处理多个查询而不做任何其他事情?如果没有,那么在进行下一次查询时,数据将不会仍然在寄存器中。或者您是说您认为在查询中使用全局寄存器变量是值得的?GNU C可以做到这一点,
\uuum256 vec\uquery0 asm(“ymm0”)应该是正确的语法IIRC。您是否查看了实际的asm以确保向量没有保存在寄存器中?如果幸运的话,编译器可能会优化掉大部分std::vector动态分配开销。如果没有,请尝试使用固定大小的数组(因为它的大小上限较低)。@PeterCordes谢谢你的建议。我没有看实际的asm,但您建议使用固定大小的数组可能是一个好的选择,这样我就可以使用类似于
\uuuuum256 vec\uquery0 asm(“ymm0”)