在运行时从 simd 寄存器获取任意浮点数?

2024-02-12

我想从 simd 寄存器访问任意浮点数。我知道我可以做这样的事情:

float get(const __m128i& a, const int idx){
    // editor's note: this type-puns the FP bit-pattern to int and converts to float
    return _mm_extract_ps(a,idx);
}

or

float get(const __m128i& a, const int idx){
    return _mm_cvtss_f32(_mm_shuffle_ps(a,_MM_SHUFFLE(0,0,0,idx));
}

甚至使用 Shift 代替 Shuffle。问题是这些都需要在编译时知道 idx(shuffle、shift 和 extract 都需要 8 位立即数)。

我也可以使用_mm_store_ps()然后使用结果数组,但这需要进入内存。有没有比这更快的方法?

编辑:忽略第一个代码片段,我想要那个位置的浮点数,而不是整数,就像_mm_extract_ps返回。


First of all you definitely don't want , unless you want to type-pun FP to int1.

但无论如何,对于运行时变量索引,您可能不想分支以选择具有正确 imm8 的指令。

Godbolt 编译器资源管理器上 gcc/icc/clang/msvc 的源 + asm 输出 https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAKxAEZSAbAQwDtRkBSAJgCFufSAZ1QBXYskwgA5AHo5AagCqgvG3kBZAMoA1AMLyZAcQBu8iMczIi4pgwYBKecPkWrJQfKbFM8gA5NBQUx0eVV5b2BBDgAGAEEFZzQsAFpgTBZQjxUAWzxmYnkieQB3BCYCeQBPUTApELSKpgAzAkwC1QZVVWAAOhjY/u4AZlVkBhEseQ4h3Txs3JYCYlUehGnsQa4RljGJn2ndQQJ8RdX1zZGmliwm%2BQB9O%2BRfccFnkQ95T4UNHV1%2Bfky8lygW6hQQeEEpE81xKPnQqBYtQqLEwwVCFSaJHkf14ANYlWKTEqF1G40mByO6FseGALDOQ2wXxk8kxBV0tFonk6tICEHsUOcBHBHiSPlszmKJAA1h4Ati%2BnFhul8E1Boqtngmjd7ndYtoABoPfpNBiocryBq0LgADjuTGMAA8IA9slbrZ4oaoKnh0A77BwAOx8OKfT4ut14Fw%2Bh1TIYAEXu80exgIKiGXDuKjdEGj/qGPFDhdDCmM2VQxnQ/UL4Ztn0ECBETRNaOmCbuSd8bWyIlaxi8d18gggTChxlz0wLBmZxk7xFyDEHVdD3gIYgy7eyydTgjuTXTEHrjeb6DzwYGAbjF2VmrV8WZD3KSzwACMe5gHlACF4GhBuFxAkEQx/vYIFTvI8KYIIiJSBU%2BCCEwz4MD4er6qEty2N4TDoJU8jpAhSHoD08gAOqsK0IRFEEPiaJojKiqk6QKrEJpmhUlo2pmgSYEMzrtm6HqhIsoS%2Bo4gZnoWaAsEc8giF66Z3BUprFAALLGCbRA60RDNEXDRLQ0TRAKrS%2BMprYadEykWVZpn5kunyyYs8kVEESFuAUrbyEpqkAtGABURyYCZE52Tqro2pGY6%2BmpiabsgKZphmWY2gemCudYp4hQotitMQLDlHgFggJ5ZohEw8jKckz6VK0JSqPCxSCUU0Q9D0tAAKwssQqDZECmBlsQlRESU7g%2BMwrQ7MSIbFsyRz5cg8iSQFDq%2BAUDkEE5TgNk2SFRG1PCmW1CYeWJxUqVCXn8AFJm%2BfQp2mbwV3Kb5XDnagKmXcZT1DFMF7BVNTI1taEXRtFG5bglmZ4Nmh7bZBHB7dG8Nxhlir/aFEabUeBGg0mMPNncgV4NazpJsgAQEIOkPZkw/JRiJf2xIWK5rjF4M7nuXAk7F5NJbag4Hltx4gQzgaXqjAwalqmC3A8KEZncxqmuaDRcG1ABsdqOpzLqq2rAlesJfo/eJoaA8DUUeWDcWpng8m8zm9P5kWRYlmWFYhTr6vmw6us49kuuPDzUMcSouvmOOtni4WCioLOrAhCQWAFLHvioCoNVNF1PUBD1rJgj4qAMCEXrLFJeDzXl2SYENEC6MwagQVBSKeMgEi%2BBUQo%2BCiDWVz4xSEAgogVChbVWvIIAyP6aOe3rmOwyElvzAHs7dr2XjWg68n8yOdM%2B%2Brp5Mi4K%2BLmjcjM7lMUB9bgS7vueMEQfYFaHogLAioahChCIXn%2BupPxez%2B4NxX3JoOZeghoaCwfo/BQz99AQiBBCd%2BwAwRf0VL9U%2BzJY5tHjvIRObQgTlGQAgSCnhBC5yxJ3eQ3cqFMCrkVOqmAYxNDwMQI4UIRAsE6FKHwg4trPmhCEGcXY8gn0ZtNGedZIEtnjJfdWA4uxvgdH2YgA4hyRT3mrKENMJyHyEXOURSprg3nForViFpMAEDdHcbwSt0C8TCu6HeBtczG0ysyfuQpTr4OpLSKuQkcFHBIJgEQUI0DZF8HkNoWQCB5AYAtQeacfBFB8RkKq8h0zyGfJYJg7wknEJwlRfOThrCYBCik3ktA1aOBYuaAg4T4Y8GtEjBm1ZcYlNURAOpvgtEozEZ8H%2BhR6kI19M0yO54xYDHVJcbUssDRGjiAoNa7cVEVGyESTwxhUA%2BnCMELZagVC0lwQUAAXm0VAuEHTjRUAiFkWJIw4LwPwIYApURFNaEcJiNS2IWIDjYkq9jfY7yWUsBShtRJBjcbVTxSlvHchYH4xoMJAneBCQtbqESkKsOKbE%2BJqBEmFHOSkzJOEMlZLJrk/OBTXmUORaUtG5ShzpmqUrDuQzGmjJNmGJecjaUdK6T0lpy4LEsy6Q0xGh0RboIlkMa8qoTFTM1DMu4hgAByihdDzLvGi8JkSsUROQFKTwxSgkyD%2BVhYpxByiYGAJNZiLLzGWI4rSEQyB7H8ScUJFxYlv7CovkwMVIyJVO0hWMVgyDVy5Q8IQRq5zEZcDVkBDBFpW7aoxSQ6NjCmBWAYDhLhPgGjWNSiVQYUqSRSxlrqOZCs4hfIdQHZ1rqJEeu9CJVxaMBn%2BuGQ6DlUw0ETMMSqMCszDTVulbKodyq1UatHbeGQvl50LvnfIAAKtgTQy6SIAEll0AAlsQAHkVXrtiCqjdINYgADFV0ACV5CbpVQAGXvfeww8hF3vvfTIfoChN3gR9NBDukEKiLS/EJVw1hISZJ7PnbwnhYOoGfGOUQggc1%2BDTioRCBd25zDwMc/KCIPCd1gxAPNgksAOlBGVEDZE4OWsqFPSZWrQ1sFqFkKRuDsO5FOe0DwFgBoWjxaVGEEapK4T49VcEagvSxvmMEPAVq57Ng8KUdIaGQSYe/ZgojqbdWQZYKgZy5zuxEKYqY2pQHOKATdbWbREKk3MfDWuQjwoY1H2EQuYu8xrQ133QUMq%2BnkixyhQgQ2ZkGPR2ZMAFNuTZR%2BBhvwtZghDWZ26r1fqlQoSbt0PoHBsCgTlhkG7dAMheGNmfD6kTDqrEAW4sOKEXBemi1vMMRV0sdSqvVZq2t7yCB3AbdZxx/o7N9LAp/DwscYlcZIZRKRHg87Uc9SwXwPZlMDy5HEsJabWEVZZuxW0/WRwNamLwKrTqWAurq/IaIjWpXdYsw2x4QQBv609UNzlYEovzRwQ5418GdiYFeuagg/cJBDSy/oWlhGQfVx2xfPbfXzuuqcaJE78ODtQlCPwWgN3%2B1bFlc1mt9qeuFtsdrPiNm3uw4yPDs1djDso4LDTotWFLvXclf2hV5adQoS60Tiz9oHS0Ge7Z97ChqgiFqBRSThqhTmk27p4aIgi4ySCG5ucIiPAOnmFCLX2RPReaGjwaD%2BmoQGeIQUWXGQfudxwrYOJFLKHgZIMkSSSxC4uCiXgBEVPTu2gF3Vo7AJ4f%2B53mzsZTWTGSyVXLXnZjicC7J/7dWnhKftt9dTn5ciE91eUgzutWetaXaqezszgGjiax9ncLgrgAXJ53hI58npXveowQMlWBefY57z%2B3jW2fG%2BhGgZgzjtgioOdYx7y5lqrBNH4nnEcpkMGPPjDZScnwfc94r5zEceBu%2BZ974X/vO%2BdGjaAzsilqBbg0toT4e%2BPgnfEEhX2dA6BBwCPzhkBEPhGFLCzasmkCAKgskBMMAtFFJMByZgt5BkgmxyZkg1lPEAQ4xYgAAtfgK9ZApwc5FDN6eqUTFLHqQQEQZ8fTOccUBaVgZuMoXwTsFgEtXHGVIxOVRjO7cvWnDMZAJ7JtdhL0ZZUFI/YbCLZNeaXQWiZzG/EpSDM3KJcCBEJEJiQQjxELYFFZUIKET7N/a3EVYhY7Hgb6aTaETwZ/bwEENgOAjAAuJsIIAgNxSMbgNWDyAMQVfpdPfPDWWnOrHfHQ1wknf5JxJ5HHZrPHRgqQfkBgaQNqKQUgFgaQaISI1AaQHEPgE7YQMQCQY7IYWgSIggGIkI/kKUEAIYbzZSAMUeNqG0AATj0mUloHKPVkYGkGUkiOyDoAMiiJyNIHiKkEiMEBAEMmyKkFiP5DgFgBgEQBQHRV1XIEoHl0xRAGAGtHoGYQYByh6IgGfHaOfFUC8EqGkEyNIDCQRX3U4R2IGMiKwDWTYCQnaPwG8CsAKkgnaMYUsDfF2MiK9FSnaKfGaKkEyNCLDRQH%2BF4EYBfB6MgH5Amy9ykmkGSH3W%2BmSFIjsCgLWXEDWHjDKEEGKFSgYG6NEHEEkGx3qKkAiLaNOI6OkAdGtDVmSDVlUnUO805AgFwEICxGGFul0AmMxXSOx2xEBJ4CyJyJAlIHyO0h6GUgWIDGUi4FoADGtHKOUksnKMJMaNIGaLakMmiNJM6O6N6NIH6MGNIGGLGJmLaCmIgGNOIBAHLmQFoGtFICWJWMoHWNJM2LygGleP2O6kOOOOuL6jDSuNJJuMsBiQsB6NJKeOQBeJ%2BLeMWA%2BNJK%2BNeL%2BLYABNxCBM6GfFBL5A6OwwI2hNhKgIRLiTgK8CIVbHRMxLsBxNSPxNCPCMiI1NiLJKkApKpJpNCBTRtJ6GamiDMCZOsC5KhHZJ1U5OGCO0SP4H5NOMFOISwjaEoBrKkGVNVPVPaK1KEB1L1NyKFIKOajFOlMlOlNlPlOiEVLCKkCGDrJXOkAnP1MNNvKQHNNNPNJQHrmADVlaPtKiUdI2K2LdKjI9Nk0WCOJzR9IuOAH9IbMDLuJDMeIdGeNaHdPeNPIbPjKjMTPYF5OBPTPgHBOzKhKkGSAdHmnhLtygLzKLJRNLICHLOxKEFxIkDoHnOJPrLiPJMpOpNUh%2BzVk7M7J7PwD7NZIHI5PwWGCGEcDHN4GvM3OFNoE7OUgyIsnKO0nKNoCGFqLaiVKaJADVJJIbNXJ6L6IFINNGIgHvKErIAoDNLMufJqLuBpLtLyAdLWO/NdJOL2IOPSAICApOIgt9MuMkADJYSDPuNDIbPDMjL2MQs%2BOWG%2BN%2BMYH%2BM4BTIEDTIzJwpiRzPwv3RjCMFMGSGLKQDlkrLxIYsJKYsvKbLYtbLyxqPkF0mlPkGvWXWIl4uZPci2DZLMvSNzx5MSskqnPAKTjnI0pVK0uXM1KvLXIMsnLyIKJkosnkuUkUuPJUrUsJPPJ0pYq6N1IFPnK4AvLGs2o3MFL42uWiOUiAA对于这个答案中的所有功能。包括(在底部)一些使用编译时常量 idx 的测试调用程序,以便您可以看到在实际程序中发生内联 + 常量传播时会发生什么。和/或来自同一向量的两个索引(仅 gcc CSE 并从同一存储重新加载两次,其他编译器存储两次)。

使用 gcc/clang/ICC 进行存储/重新加载优化(但variable-idx版本的延迟较高)。其他方法只能很好地优化 clang 的持续输入。 (clang甚至可以看穿pshufb版本并将其变成vshufps imm8 or vpermilps imm8,或者 idx=0 时无操作)。其他编译器会做一些愚蠢的事情,例如将向量归零vxorps并将其用作vpermilps控制!


128 位向量:如果有 SSSE3,请使用变量洗牌pshufb, or AVX

使用 AVX1,您只需 2 ALU uops 即可完成 128 位向量的操作vpermilps,这是一个使用双字选择器元素的变量洗牌,与pshufb.

这可以让您进行与您的操作完全相同的随机播放_mm_shuffle_ps(包括将低位元素复制到高位 3 个元素,这很好),但使用运行时索引而不是立即数。

// you can pass vectors by value.  Not that it matters when inlining
static inline
float get128_avx(__m128i a, int idx){
    __m128i vidx = _mm_cvtsi32_si128(idx);          // vmovd
    __m128  shuffled = _mm_permutevar_ps(a, vidx);  // vpermilps
    return _mm_cvtss_f32(shuffled);
}

gcc 和 clang 对于 x86-64 (Godbolt 编译器资源管理器)如下编译:

    vmovd           xmm1, edi
    vpermilps       xmm0, xmm0, xmm1
    ret

没有 AVX 但使用 SSSE3,您可以加载或创建掩码pshufb。对 4 的数组进行索引是相当常见的__m128i向量,特别是使用_mm_movemask_ps结果作为索引。但这里我们只关心低 32 位元素,所以我们可以做得更好。

事实上,该模式的常规性质意味着我们可以使用两个 32 位立即数操作数通过乘法和加法来创建它。

static inline
float get128_ssse3(__m128 a, int idx) {
    const uint32_t low4 = 0x03020100, step4=0x04040404;
    uint32_t selector = low4 + idx*step4;
    __m128i vidx = _mm_cvtsi32_si128(selector);

    // alternative: load a 4-byte window into 0..15 from memory.  worse latency
    // static constexpr uint32_t shuffles[4] = { low4, low4+step4*1, low4+step4*2, low4+step4*3 };
    //__m128i vidx = _mm_cvtsi32_si128(shuffles[idx]);
    __m128i shuffled = _mm_shuffle_epi8(_mm_castps_si128(a), vidx);
    return _mm_cvtss_f32(_mm_castsi128_ps(shuffled));
}

海湾合作委员会输出-O3 -march=nehalem(其他编译器也这样做,模块可能浪费了movaps):

get128_ssse3(float __vector(4), int):
    imul    edi, edi, 67372036        # 0x04040404
    add     edi, 50462976             # 0x03020100
    movd    xmm1, edi
    pshufb  xmm0, xmm1
    ret                     # with the float we want at the bottom of XMM0

因此,如果没有 AVX,存储/重新加载会保存指令(和微指令),特别是如果编译器可以避免符号扩展或零扩展索引。

在 Core2(Penryn) 及更新版本的 Intel CPU 上,从 idx 到结果的延迟 = imul(3) + add(1) + movd(2) + pshufb(1)。从输入向量到结果的延迟仅为pshufb, 尽管。 (加上 Nehalem 上的旁路延迟延迟。)http://agner.org/optimize/ http://agner.org/optimize/


__m256256 位向量:使用 AVX2 进行随机播放,否则可能会存储/重新加载

与 AVX1 不同,AVX2 具有跨车道可变洗牌,例如vpermps http://felixcloutier.com/x86/VPERMPS.html。 (AVX1 仅具有整个 128 位通道的立即洗牌。)我们可以使用vpermps作为 AVX1 的直接替代品vpermilps从 256 位向量中抓取一个元素。

有两个内在函数vpermps (See ).

  • _mm256_permutevar8x32_ps(__m256 a, __m256i idx):旧名称,操作数与 asm 指令的顺序相反。
  • _mm256_permutexvar_ps(__m256i idx, __m256 a):AVX512 引入的新名称,操作数顺序正确(与 asm 操作数顺序匹配,与_mm_shuffle_epi8 or _mm_permutevar_ps). The asm指令集参考手册入门 http://felixcloutier.com/x86/VPERMPS.html仅列出此版本,并以错误的类型列出它(__m256 i为控制操作数)。

    gcc 和 ICC 仅在启用 AVX2(而非 AVX512)的情况下接受此助记符。但不幸的是 clang 只接受这个-mavx512vl (or -march=skylake-avx512),所以你不能便携地使用它。因此,只需使用更笨重的 8x32 名称,它在任何地方都适用。

#ifdef __AVX2__
float get256_avx2(__m256 a, int idx) {
    __m128i vidx = _mm_cvtsi32_si128(idx);          // vmovd
    __m256i vidx256 = _mm256_castsi128_si256(vidx);  // no instructions
    __m256  shuffled = _mm256_permutevar8x32_ps(a, vidx256);  // vpermps
    return _mm256_cvtss_f32(shuffled);
}

    // operand order matches asm for the new name: index first, unlike pshufb and vpermilps
    //__m256  shuffled = _mm256_permutexvar_ps(vidx256, a);  // vpermps
#endif

_mm256_castsi128_si256从技术上讲,不会使上通道未定义(因此编译器永远不需要花费指令进行零扩展),但无论如何我们并不关心上通道。

这编译为

    vmovd   xmm1, edi
    vpermps ymm0, ymm1, ymm0
     # vzeroupper        # these go away when inlining
     # ret

因此,它在 Intel CPU 上非常棒,从输入向量到结果的延迟仅为 3c,吞吐量成本为 2 uops(但两个 uops 都需要端口 5)。

AMD 上的车道交叉洗牌成本要高得多。


存储/重新加载

存储/重新加载实际上很好的情况:

  • 没有 AVX2 的 256 位向量,或没有 SSSE3 的 128 位向量。
  • 如果您需要同一向量中的 2 个或更多元素(但请注意,如果您实际调用,除 gcc 之外的编译器会多次存储get128_reload。因此,如果您这样做,请手动内联向量存储并对其进行多次索引。)
  • 当ALU端口压力(尤其是shuffle端口)成为问题时,吞吐量比延迟更重要。在英特尔 CPU 上,movd xmm, eax也在端口 5 上运行,因此它与 shuffle 竞争。但希望您只在内部循环之外使用标量提取,周围有许多代码可以执行其他操作。

  • When idx通常是一个编译时常量,您希望让编译器为您选择洗牌。

A bad idx不过,它可能会使你的程序崩溃,而不仅仅是给你错误的元素。将索引直接转变为随机播放控制的方法会忽略高位。

当心ICC 有时会在内联后错过将常量索引优化为随机播放的情况 https://stackoverflow.com/questions/51407959/get-an-arbitrary-float-from-a-simd-register-at-runtime/51413278?noredirect=1#comment89799186_51413278。 ICC 没问题test_reload2在 Godbolt 的例子中。

存储/重新加载到本地阵列对于吞吐量来说完全没问题(也许不是延迟),并且由于存储转发,在典型 CPU 上只有约 6 个周期的延迟。大多数 CPU 比矢量 ALU 具有更多的前端吞吐量,因此,如果您接近 ALU 吞吐量而不是存储/加载吞吐量的瓶颈,那么在混合中包含一些存储/重新加载一点也不坏。

宽的存储可以转发到窄的重新加载,但受到一些对齐限制。我认为向量的 4 个或 8 个元素中的任何一个的自然对齐双字重新加载在主流 Intel CPU 上都很好,但您可以查看 Intel 的优化手册。请参阅性能链接x86 标签维基 https://stackoverflow.com/tags/x86/info.

在 GNU C 中,您可以像数组一样索引向量。如果内联后索引不是编译时常量,它会编译为存储/重新加载。

#ifdef __GNUC__                      // everything except MSVC
float get128_gnuc(__m128 a, int idx) {
    return a[idx]; 
    // clang turns it into idx&3
    // gcc compiles it exactly like get_reload
}
#endif

 # gcc8.1 -O3 -march=haswell
    movsx   rdi, edi                            # sign-extend int to pointer width
    vmovaps XMMWORD PTR [rsp-24], xmm0          # store into the red-zone
    vmovss  xmm0, DWORD PTR [rsp-24+rdi*4]      # reload

完全可移植的编写方式(256 位版本)是:

float get256_reload(__m256 a, int idx) {
    // with lower alignment and storeu, compilers still choose to align by 32 because they see the store
    alignas(32) float tmp[8];
    _mm256_store_ps(tmp, a);
    return tmp[idx];
}

编译器需要多条指令来对齐函数的独立版本中的堆栈,但是当然在内联之后,这种情况只会发生在外部包含函数中,希望在任何小循环之外。

您可以考虑分别存储向量的高/低半部分vextractf128和 128 位vmovups,就像 GCC 所做的那样_mm256_storeu_ps当它不知道目的地是否对齐时,对于tune=generic(帮助Sandybridge和AMD)。这将避免对 32 字节对齐数组的需要,并且对 AMD CPU 基本上没有任何缺点。但与对齐存储相比,英特尔的情况更糟,因为假设对齐堆栈的成本可以分摊到许多 get() 操作上,它会花费额外的 uops。 (函数使用__m256有时无论如何最终都会对齐堆栈,所以你可能已经付出了成本。)你可能应该只使用对齐的数组,除非你只针对 Bulldozer、Ryzen 和 Sandybridge 等进行调整。


脚注1:_mm_extract_ps将 FP 位模式返回为int。底层 asm 指令 (extractps r/m32, xmm, imm8 http://felixcloutier.com/x86/EXTRACTPS.html) 对于将浮点数存储到内存可能很有用,但不能将元素洗牌到 XMM 寄存器的底部。这是 FP 版本pextrd r/m32, xmm, imm8.

所以你的函数实际上是将整数位模式转换为 FP,并使用编译器生成的cvtsi2ss,因为 C 允许隐式转换int to float.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在运行时从 simd 寄存器获取任意浮点数? 的相关文章

  • 现代 x86 硬件不能将单个字节存储到内存中吗?

    说到 C 的并发内存模型 Stroustrup 的C 编程语言 第 4 版 第 1 节 41 2 1 说 就像大多数现代硬件一样 机器无法加载或存储小于单词的任何内容 然而 我的 x86 处理器已经有几年的历史了 它可以并且确实存储小于单词
  • 获取比较指令的值

    据我了解 cmp 指令将设置标志寄存器中的一些位 然后 您可以使用 jle jnp 等指令基于这些指令进行分支 我想知道如何从比较中恢复整数值 示例 以下是有效的 c 语法 y x a gt 13 因此 a 与 13 进行比较 得到 tru
  • 如何禁用浮点单元(FPU)?

    我想在 x86 系统中禁用 FPU MMX SSE 指令 并且我将为设备不可用异常实现一个处理程序 我已经提到过控制寄存器 wiki 页面 http en wikipedia org wiki Control register 看来我必须在
  • 汇编编程语言:程序仅当输入为 ESC 时退出,并在退出前要求确认(y/n),否则循环

    我只是汇编语言编程的初学者 我们的第一个任务是让程序仅在输入为 ESC 时退出 退出之前请求确认 y n 否则循环 我知道 ESC 在 ASCII 代码中具有等效值 但我对插入位置或是否需要添加更多内容感到困惑 请帮我 这是程序 model
  • C/C++ 中的简单“Hello World”内联汇编语言程序

    我使用 devcpp 和 borland c 编译器 asm mov ax 4 I O Func mov bx 1 Output func mov cx name address of the string mov dx 6 length
  • gcc 编译器开关 (-mavx -mavx2 -mavx512f) 到底有什么作用?

    我在 C C 代码中明确使用了英特尔 SIMD 内在扩展 为了编译代码 我需要在命令行上指定 mavx mavx512 或类似的内容 我对这一切都很满意 然而 从阅读 gcc 手册页来看 并不清楚这些命令行标志是否也告诉 gcc 编译器尝试
  • 排列 SSE __m128i 寄存器内的字节

    我有以下问题 In m128i寄存器有 16 个 8bit 值 顺序如下 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16 我想要实现的是有效地洗牌字节以获得此排序 1 2 3 4 5 6 7 8 9 10 11
  • 优化数组压缩

    假设我有一个数组k 1 2 0 0 5 4 0 我可以按如下方式计算掩码m k gt 0 1 1 0 0 1 1 0 仅使用掩码 m 和以下操作 左移 右移 And Or 加 减 乘 我可以将 k 压缩为以下形式 1 2 5 4 以下是我目
  • 为什么这个“std::atomic_thread_fence”起作用

    首先我想谈一下我对此的一些理解 如有错误请指正 a MFENCE在x86中可以保证全屏障 顺序一致性可防止 STORE STORE STORE LOAD LOAD STORE 和 LOAD LOAD 重新排序 这是根据维基百科 https
  • _mm256_fmadd_ps 比 _mm256_mul_ps + _mm256_add_ps 慢?

    我有一个图像处理算法来计算a b c d与AVX 伪代码如下 float a new float N float b new float N float c new float N float d new float N assign va
  • 一条指令可以同时处于两种寻址模式吗?

    我在书中读到了以下内容从头开始编程 处理器有多种不同的访问数据的方式 称为 寻址模式 最简单的模式是立即模式 其中 要访问的数据嵌入在指令本身中 例如 如果我们想将寄存器初始化为 0 而不是给出 计算机要从中读取 0 的地址 我们将指定立即
  • CALL指令是否总是将EIP指向的地址压入堆栈?

    x86架构中函数调用时是否存在返回地址不入栈的情况 No CALL根据定义 将在跳转到目标地址之前将返回地址压入堆栈 该返回地址是EIP or RIP sizeof call instruction 通常为 5 个字节 英特尔 64 和 I
  • C/C++ 特殊 CPU 功能的使用

    我很好奇 新的编译器是否使用了新 CPU 中内置的一些额外功能 例如 MMX SSE 3DNow 所以 我的意思是 在最初的 8086 中甚至没有 FPU 所以旧的编译器甚至不能使用它 但新的编译器可以 因为 FPU 是每个新 CPU 的一
  • 适用于 AVX 和 SSE 的 Visual Studio 的 cpu 调度程序

    我使用两台计算机工作 一种不支持 AVX 另一种支持 AVX 让我的代码在运行时找到我的CPU支持的指令集并选择合适的代码路径会很方便 我按照 Agner Fog 的建议制作了一个 CPU 调度程序 http www agner org o
  • 使用 AVX 内在函数代替 SSE 并不能提高速度 - 为什么?

    我已经使用 Intel 的 SSE 内在函数相当长一段时间了 并取得了良好的性能提升 因此 我希望 AVX 内在函数能够进一步加速我的程序 不幸的是 直到现在情况并非如此 可能我犯了一个愚蠢的错误 所以如果有人能帮助我 我将非常感激 我使用
  • 什么是 __ext_vector_type__ 和 simd?

    我正在使用 Apple Metal API 以及所谓的simd图书馆 标题中有这样的代码 typedef attribute ext vector type 3 float vector float3 我很好奇它实际上做了什么以及为什么编译
  • Linux内核页表更新

    在linux x86 中分页 每个进程都有它自己的页面目录 页表遍历从 CR3 指向的页目录开始 每个进程共享内核页目录内容 假设三个句子是正确的 假设某个进程进入内核 模式并更新他的内核页目录内容 地址映射 访问 权利等 问题 由于内核地
  • CPU寄存器和多任务处理

    我目前正在学习汇编 我很困惑 CPU 寄存器如何与多任务一起工作 所以在多任务系统中 CPU可以随时暂停某个程序的执行并运行另一个程序 那么在这一步中寄存器值是如何保存的呢 寄存器是压入堆栈还是以其他方式 CPU 寄存器如何与多任务一起工作
  • 什么是“矢量化”?

    现在好几次了 我在 matlab fortran 其他一些 中遇到这个术语 但我从来没有找到解释它是什么意思 它有什么作用 所以我在这里问 什么是矢量化 例如 循环矢量化 是什么意思 许多CPU具有 向量 或 SIMD 指令集 它们同时对两
  • 按字节数对向量进行混洗

    有什么办法可以左移 v 0 gt v 1 a m128i by n字节 其中n仅在运行时才知道 我目前仅限于 AVX1 但如果 AVX2 512 使这变得更容易 我非常感兴趣 I found mm bslli si128 m128i imm

随机推荐