有没有一种方法可以根据编译时未知的掩码长度来掩码 __m128i 寄存器的一端?

2024-03-14

我有一个看似简单的问题。将字符串加载到 __m128i 寄存器中(使用 _mm_loadu_si128),然后查找字符串的长度(使用 _mm_cmpistri)。现在,假设长度低于 16,我希望在第一个字符串结尾零之后只有零。实现此目的的一种方法是将“len”字节复制到另一个寄存器,或者使用长度为 8 * len 的 1 掩码与原始寄存器进行 AND 运算。但找到创建这种仅依赖于计算长度的掩码的简单方法并不容易。


我会这样做。未经测试。

// Load 16 bytes and propagate the first zero towards the end of the register
inline __m128i loadNullTerminated( const char* pointer )
{
    // Load 16 bytes
    const __m128i chars = _mm_loadu_si128( ( const __m128i* )pointer );

    const __m128i zero = _mm_setzero_si128();
    // 0xFF for bytes that were '\0', 0 otherwise
    __m128i zeroBytes = _mm_cmpeq_epi8( chars, zero );

    // If you have long strings and expect most calls to not have any zeros, uncomment the line below.
    // You can return a flag to the caller, to know when to stop.
    // if( _mm_testz_si128( zeroBytes, zeroBytes ) ) return chars;

    // Propagate the first "0xFF" byte towards the end of the register.
    // Following 8 instructions are fast, 1 cycle latency/each.
    // Pretty sure _mm_movemask_epi8 / _BitScanForward / _mm_loadu_si128 is slightly slower even when the mask is in L1D
    zeroBytes = _mm_or_si128( zeroBytes, _mm_slli_si128( zeroBytes, 1 ) );
    zeroBytes = _mm_or_si128( zeroBytes, _mm_slli_si128( zeroBytes, 2 ) );
    zeroBytes = _mm_or_si128( zeroBytes, _mm_slli_si128( zeroBytes, 4 ) );
    zeroBytes = _mm_or_si128( zeroBytes, _mm_slli_si128( zeroBytes, 8 ) );
    // Now apply that mask
    return _mm_andnot_si128( zeroBytes, chars );
}

Update:这是另一个版本,使用了 Noah 关于 int64 的想法-1操作说明。 可能会稍微快一些。拆卸。 https://godbolt.org/z/Pojs8d

__m128i loadNullTerminated_v2( const char* pointer )
{
    // Load 16 bytes
    const __m128i chars = _mm_loadu_si128( ( const __m128i* )pointer );

    const __m128i zero = _mm_setzero_si128();
    // 0xFF for bytes that were '\0', 0 otherwise
    const __m128i zeroBytes = _mm_cmpeq_epi8( chars, zero );

    // If you have long strings and expect most calls to not have any zeros, uncomment the line below.
    // You can return a flag to the caller, to know when to stop.
    // if( _mm_testz_si128( eq_zero, eq_zero ) ) return chars;

    // Using the fact that v-1 == v+(-1), and -1 has all bits set
    const __m128i ones = _mm_cmpeq_epi8( zero, zero );
    __m128i mask = _mm_add_epi64( zeroBytes, ones );
    // This instruction makes a mask filled with lowest valid bytes in each 64-bit lane
    mask = _mm_andnot_si128( zeroBytes, mask );

    // Now need to propagate across 64-bit lanes

    // ULLONG_MAX if there were no zeros in the corresponding 8-byte long pieces of the string
    __m128i crossLaneMask = _mm_cmpeq_epi64( zeroBytes, zero );
    // Move the lower 64-bit lanes of noZeroes64 into higher position
    crossLaneMask = _mm_unpacklo_epi64( mask, crossLaneMask );
    // Update the mask.
    // Lower 8 bytes will not change because _mm_unpacklo_epi64 copied that part from the mask.
    // However, upper lane may become zeroed out.
    // Happens when _mm_cmpeq_epi64 detected at least 1 '\0' in any of the first 8 characters.
    mask = _mm_and_si128( mask, crossLaneMask );

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

有没有一种方法可以根据编译时未知的掩码长度来掩码 __m128i 寄存器的一端? 的相关文章

  • 使用 SSE 内在函数时如何确保 NaN 传播?

    我最近读到了这篇关于NaNSSE 算术运算中的值 对两个非数字 NAN 参数进行算术运算的结果是未定义的 因此 使用 NAN 参数的浮点运算将与相应汇编指令的预期行为不匹配 Source http msdn microsoft com en
  • 如何写入LLDB中的XMM寄存器

    我正在尝试使用 LLDB API 从 python 中的寄存器读取和写入值 对于通用寄存器 我一直在使用frame register register name value读取和写入寄存器值 这对我来说很成功 然而 当我接近浮点寄存器时 我
  • 如何使用 SIMD 检查偶数/奇数通道是否在给定范围内?

    Given a m128i其中存储 16char是 偶数索引车道指的是even车道 即 0 2 4 14 处的车道 奇数索引车道指的是odd车道 即 1 3 5 15 处的车道 在我的应用程序中 偶数 奇数车道必须在给定范围内 例如 假设e
  • 进行水平 SSE 向量和(或其他简化)的最快方法

    给定一个由三个 或四个 浮点数组成的向量 对它们求和的最快方法是什么 SSE movaps shuffle add movd 总是比 x87 快吗 SSE3 中的水平相加指令值得吗 转移到 FPU 然后是 faddp faddp 的成本是多
  • AVX2浮点比较并得到0.0或1.0而不是全0或全1位

    基本上 在生成的向量中 我想为所有输入浮点值 gt 1 保存 1 0 而为所有输入浮点值 float f 8 1 2 0 5 1 7 1 9 0 34 22 9 18 6 0 7 float r 8 Must be 1 0 1 1 0 1
  • 我应该使用 SIMD 或向量扩展还是其他什么?

    我目前正在开发一个开源 3D 应用程序框架c questions tagged c 2b 2b with c 11 questions tagged c 2b 2b11 我自己的数学库是这样设计的XNA数学库 http msdn micro
  • 调用always_inline‘_mm_mullo_epi32’时内联失败:目标特定选项不匹配

    我正在尝试使用 cmake 编译 C 程序 该程序使用 SIMD 内在函数 当我尝试编译它时 出现两个错误 usr lib gcc x86 64 linux gnu 5 include smmintrin h 326 1 错误 调用alwa
  • 为什么矢量化通常比循环更快?

    为什么在执行操作的硬件的最低级别和所涉及的一般底层操作 即 运行代码时所有编程语言的实际实现通用的事情 矢量化通常比循环快得多 计算机在循环时会做什么而在使用矢量化时不会做什么 我指的是计算机执行的实际计算 而不是程序员编写的计算 或者它有
  • C++ 错误:“_mm_sin_ps”未在此范围内声明

    我正在尝试对将函数应用于数组的不同方法进行基准测试 why is mm sin ps在我的范围内不知道但是 mm sqrt ps is 我怎样才能让它知道 并且编译没有错误 include
  • SSE,行主要与列主要性能问题

    出于个人和娱乐目的 我正在使用 SSE 4 1 编写一个 geom 库 我花了最后 12 个小时试图理解处理行主要与列主要存储矩阵时的性能问题 我知道 Dirext OpenGL 矩阵是以行主顺序存储的 因此对我来说 将矩阵按行主顺序存储会
  • 跨 AVX 通道的最佳方式是什么?

    有些问题具有类似的标题 但我的问题涉及其他地方未涵盖的一个非常具体的用例 我有 4 个 128d 寄存器 x0 x1 x2 x3 我想将它们的内容重新组合在 5 个 256d 寄存器 y0 y1 y2 y3 y4 中 以准备其他计算 on
  • 如何使用 Neon SIMD 将无符号字符转换为有符号整数

    如何转换变量的数据类型uint8 t to int32 t使用霓虹灯 我找不到执行此操作的任何内在因素 假设您想要将 16 x 8 位整数的向量转换为 4 个 4 x 32 位整数的向量 您可以通过首先解压缩为 16 位 然后再次解压缩为
  • 排列 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 以下是我目
  • orpd等SSE2指令有什么意义?

    The orpd指令是 压缩双精度浮点值的按位逻辑或 这不是做完 全相同的事情吗por 按位逻辑或 如果是这样 拥有它还有什么意义呢 请记住 SSE1orps https www felixcloutier com x86 orps首先 实
  • 利用 SSE 和其他 CPU 扩展

    在我的代码库中有几个地方 对于大型数据集 相同的操作会重复很多次 在某些情况下 处理这些需要花费相当长的时间 我相信使用SSE来实现这些循环应该可以显着提高它们的性能 特别是在对同一组数据执行许多操作的情况下 因此一旦数据最初被读入缓存 就
  • 使用 (float&)int 进行类型双关可以正常工作,(float const&)int 会像 (float)int 一样转换吗?

    VS2019 发布 x86 template
  • 按字节数对向量进行混洗

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

    转置 8x8 矩阵可以通过制作四个 4x4 矩阵并对每个矩阵进行转置来实现 这不是我想要的 在另一个问题中 一个答案给出了解决方案 https stackoverflow com a 2518670 4144148x8 矩阵只需要 24 条
  • SIMD 和 VLIW 指令是一样的吗?

    SIMD 单指令多数据 和 VLIW 超长指令字 到底有什么区别 其中一个是另一个的子集吗 或者它们是两个完全不同的东西 完全不相关且正交 一台机器可以有一个或两个 或者两者都没有 SIMD 指令可以作为扩展添加到 VLIW ISA 但 V

随机推荐