SIMD 最小磁极和最大磁极

2023-11-22

我想实现SIMD最小磁极和最大磁极函数。据我了解这些功能是

minmag(a,b) = |a|<|b| ? a : b
maxmag(a,b) = |a|>|b| ? a : b

我想要这些浮点型和双精度型,我的目标硬件是 Haswell。我真正需要的是计算两者的代码。这是我对 SSE4.1 的双精度(AVX 代码几乎相同)

static inline void maxminmag(__m128d & a, __m128d & b) {
    __m128d mask    = _mm_castsi128_pd(_mm_setr_epi32(-1,0x7FFFFFFF,-1,0x7FFFFFFF));
    __m128d aa      = _mm_and_pd(a,mask);
    __m128d ab      = _mm_and_pd(b,mask);
    __m128d cmp     = _mm_cmple_pd(ab,aa);
    __m128d cmpi    = _mm_xor_pd(cmp, _mm_castsi128_pd(_mm_set1_epi32(-1)));
    __m128d minmag  = _mm_blendv_pd(a, b, cmp);
    __m128d maxmag  = _mm_blendv_pd(a, b, cmpi);
    a = maxmag, b = minmag;
}

然而,这并不像我想要的那么有效。是否有更好的方法或至少值得考虑的替代方案?我想尽量避免使用端口 1,因为我已经使用该端口进行了许多添加/删除操作。这_mm_cmple_pd内在的转到端口 1。

我感兴趣的主要功能是:

//given |a| > |b|
static inline doubledouble4 quick_two_sum(const double4 & a, const double4 & b)  {
    double4 s = a + b;
    double4 e = b - (s - a);
    return (doubledouble4){s, e};
}

所以我真正追求的是这个

static inline doubledouble4 two_sum_MinMax(const double4 & a, const double4 & b) {
    maxminmag(a,b);       
    return quick_to_sum(a,b);
}

编辑:我的目标是two_sum_MinMaxtwo_sum below:

static inline doubledouble4 two_sum(const double4 &a, const double4 &b) {
        double4 s = a + b;
        double4 v = s - a;
        double4 e = (a - (s - v)) + (b - v);
        return (doubledouble4){s, e};
}

编辑:这是我想要的最终功能。它执行 20 个添加/订阅,所有这些都进入 Haswell 上的端口 1。使用我的实现two_sum_MinMax在这个问题中,端口 1 上的添加/订阅数量减少到 16 个,但延迟更差,而且速度仍然较慢。您可以查看此函数的程序集,并阅读有关我为何关心此问题的更多信息:优化快速乘法但缓慢加法 fma 和 doubledouble

static inline doublefloat4 adddd(const doubledouble4 &a, const doubledouble4 &b) {
        doubledouble4 s, t;
        s = two_sum(a.hi, b.hi);
        t = two_sum(a.lo, b.lo);
        s.lo += t.hi;
        s = quick_two_sum(s.hi, s.lo);
        s.lo += t.lo;
        s = quick_two_sum(s.hi, s.lo);
        return s;
        // 2*two_sum, 2 add, 2*quick_two_sum = 2*6 + 2 + 2*3 = 20 add
}

这是使用更少指令的替代实现:

static inline void maxminmag_test(__m128d & a, __m128d & b) {
    __m128d cmp     = _mm_add_pd(a, b); // test for mean(a, b) >= 0
    __m128d amin    = _mm_min_pd(a, b);
    __m128d amax    = _mm_max_pd(a, b);
    __m128d minmag  = _mm_blendv_pd(amin, amax, cmp);
    __m128d maxmag  = _mm_blendv_pd(amax, amin, cmp);
    a = maxmag, b = minmag;
}

它使用了一种有点微妙的算法(见下文),并结合了我们可以使用符号位作为选择掩码的事实。

它还使用@EOF的建议,即仅使用一个掩码并切换操作数顺序,从而节省一条指令。

我已经用少量案例对其进行了测试,它似乎与您最初的实现相匹配。


算法:

 if (mean(a, b) >= 0)       // this can just be reduced to (a + b) >= 0
 {
     minmag = min(a, b);
     maxmag = max(a, b);
 }
 else
 {
     minmag = max(a, b);
     maxmag = min(a, b);
 }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SIMD 最小磁极和最大磁极 的相关文章

  • AVX-512 指令编码 - {er} 含义

    在 Intel x86 指令集参考中 有许多 AVX 512 指令在指令中具有可选的 er 例如 VADDPD 的一种形式定义为 EVEX NDS 512 66 0F W1 58 r VADDPD zmm1 k1 z zmm2 zmm3 m
  • 设置 IRQ 映射

    我正在遵循一些教程和参考文献来尝试设置我的内核 我在教程中遇到了一些不熟悉的代码 但根本没有解释它 这是我被告知映射的代码16 IRQs 0 15 到 ISR 地点32 47 void irq remap void outportb 0x2
  • 如何在程序中将自己缝合到自己的尾部,无限循环地封装 64KB 代码段?

    如果指令的顺序执行经过偏移量 65535 则8086将从同一代码段中的偏移量 0 处获取下一个指令字节 接下来的 COM 程序利用这一事实 不断将其整个代码 总共 32 个字节 缝合到自己的尾部 环绕在 64KB 代码段中 你可以称之为二元
  • 如何编译GCC生成的asm?

    我正在玩一些汇编代码 有些事情困扰着我 我编译这个 include
  • 为什么 FMA _mm256_fmadd_pd() 内在函数有 3 个 asm 助记符:“vfmadd132pd”、“231”和“213”?

    有人可以向我解释一下为什么融合乘法累加指令有 3 种变体 vfmadd132pd vfmadd231pd and vfmadd213pd 而只有一个 C 内在函数 mm256 fmadd pd 为了简单起见 在 AT T 语法中 有什么区别
  • 从 std::round 转换为 int 是否安全?

    我有一个问题标准 圆形 http www cplusplus com reference cmath round 带签名 double round double x 假设我有这个代码 int i std round 0 9 在这种情况下 s
  • 如何仅使用单个数组在 JavaScript 中模拟调用堆栈

    我正在看维基百科页面 https en wikipedia org wiki Call stack在调用堆栈上 并尝试理解这个图像 据我所知 哈哈 const memory memory 0 3 top of stack pointer m
  • 如何处理 PHP 中浮点数的奇怪舍入

    众所周知 浮点运算并不总是完全准确 但是如何处理它的不一致之处呢 As an example in PHP 5 2 9 this doesn t happen in 5 3 echo round 14 99225 4 14 9923 ech
  • 什么时候应该使用双精度而不是十进制?

    我可以说出使用的三个优点double or float 代替decimal 使用更少的内存 速度更快 因为处理器本身支持浮点数学运算 可以表示更大范围的数字 但这些优点似乎只适用于计算密集型操作 例如建模软件中的操作 当然 当需要精度时 例
  • 使用按位 OR 0 对数字进行取整

    我的一位同事偶然发现了一种使用按位或来对浮点数进行底数的方法 var a 13 6 0 a 13 我们正在谈论它并想知道一些事情 它是如何工作的 我们的理论是 使用这样的运算符将数字转换为整数 从而删除小数部分 与这样做相比 它有什么优势吗
  • 高效memcspn

    有谁知道 memcspn 函数的有效实现吗 它的行为应该类似于 strcspn 但在内存缓冲区中查找跨度 而不是在以 null 结尾的字符串中查找跨度 目标编译器是 VisualC 谢谢 卢卡 一种近乎最佳的实现 size t memcsp
  • 32位PPC rlwinm指令

    我在理解上有点困难rlwinmPPC 汇编指令 旋转左字立即然后与掩码 我正在尝试反转函数的这一部分 rlwinm r3 r3 0 28 28 我已经知道什么了r3 is r3在本例中是一个 4 字节整数 但我不确定这条指令到底是什么rlw
  • 从类模板参数为 asm 生成唯一的字符串文字

    我有一个非常特殊的情况 我需要为类模板中声明的变量生成唯一的汇编程序名称 我需要该名称对于类模板的每个实例都是唯一的 并且我需要将其传递给asm关键字 see here https gcc gnu org onlinedocs gcc 12
  • Python 中的舍入浮点问题

    我遇到了 np round np around 的问题 它没有正确舍入 我无法包含代码 因为当我手动设置值 而不是使用我的数据 时 返回有效 但这是输出 In 177 a Out 177 0 0099999998 In 178 np rou
  • 不同编程语言中的浮点数学

    我知道浮点数学充其量可能是丑陋的 但我想知道是否有人可以解释以下怪癖 在大多数编程语言中 我测试了 0 4 到 0 2 的加法会产生轻微的错误 而 0 4 0 1 0 1 则不会产生错误 两者计算不平等的原因是什么 在各自的编程语言中可以采
  • 使用按位运算符相乘

    我想知道如何使用按位运算符将一系列二进制位相乘 但是 我有兴趣这样做来查找二进制值的十进制小数值 这是我正在尝试做的一个例子 假设 1010010 我想使用每个单独的位 以便将其计算为 1 2 1 0 2 2 1 2 3 0 2 4 虽然我
  • 为什么X86中没有NAND、NOR和XNOR指令?

    它们是您可以在计算机上执行的最简单的 指令 之一 它们是我亲自实施的第一个指令 执行 NOT AND x y 会使执行时间和依赖链长度和代码大小加倍 BMI1 引入了 andnot 这是一个有意义的补充 是一个独特的操作 为什么不是这个问题
  • 何时可以重用avx指令中的源寄存器

    在 avx 指令中用作源的寄存器何时可以在指令开始处理后重用 例如 我想使用vgatherdps该指令消耗两个 ymm 寄存器 其中之一是位移索引 我意识到vgatherdps由于数据的局部性较差 因此需要花费大量时间来收集 位移索引寄存器
  • 无法识别的仿真模式:MinGW32 上的 elf_i386

    我正在尝试制作内核 但无法链接C与程序集一起输出 这ld 我收到错误 无法识别的仿真模式 elf i386 我正在使用 Windows 10 专业版以及 MinGW32 和 MSYS 我正在使用的代码 link ld link ld OUT
  • 当前的 x86 架构是否支持非临时加载(来自“正常”内存)?

    我知道有关此主题的多个问题 但是 我没有看到任何明确的答案或任何基准测量 因此 我创建了一个处理两个整数数组的简单程序 第一个数组a非常大 64 MB 第二个数组b很小 无法放入 L1 缓存 程序迭代a并将其元素添加到相应的元素中b在模块化

随机推荐