Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/157.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ lar操作,因此,如果您希望在编译器之间获得良好的性能,则必须自己进行)_C++_C_Bit Manipulation_Simd_Swar - Fatal编程技术网

C++ lar操作,因此,如果您希望在编译器之间获得良好的性能,则必须自己进行)

C++ lar操作,因此,如果您希望在编译器之间获得良好的性能,则必须自己进行),c++,c,bit-manipulation,simd,swar,C++,C,Bit Manipulation,Simd,Swar,本机向量语法的一个优点是,当针对具有硬件SIMD的机器时,它将使用该语法,而不是自动向量化bithack或类似的可怕东西 它使编写vector-=scalar操作变得容易;语法正好起作用,隐式地为您展示标量 还要注意,从uint8\u t数组[]加载uint64\u t*是严格的别名UB,因此要小心。(另请参见:在纯C中确保SWAR bithacks严格别名安全)。您可能希望这样的东西声明 UTIN 64×T/,您可以用指针抛出来访问任何其他对象,例如“代码> Car *< /Cuff>如何在

本机向量语法的一个优点是,当针对具有硬件SIMD的机器时,它将使用该语法,而不是自动向量化bithack或类似的可怕东西

它使编写
vector-=scalar
操作变得容易;语法正好起作用,隐式地为您展示标量


还要注意,从
uint8\u t数组[]
加载
uint64\u t*
是严格的别名UB,因此要小心。(另请参见:在纯C中确保SWAR bithacks严格别名安全)。您可能希望这样的东西声明<代码> UTIN 64×T/<代码>,您可以用指针抛出来访问任何其他对象,例如“代码> Car *< /Cuff>如何在ISOC/C++中工作。< /P> 使用这些选项将uint8数据输入uint64,以便与其他答案一起使用:

// GNU C: gcc/clang/ICC but not MSVC
typedef uint64_t  aliasing_u64 __attribute__((may_alias));  // still requires alignment
typedef uint64_t  aliasing_unaligned_u64 __attribute__((may_alias, aligned(1)));
另一种实现别名安全加载的方法是将
memcpy
放入
uint64\u t
,这也删除了
alignof(uint64\u t
)对齐要求。但是在没有有效的未对齐加载的ISA上,当gcc/clang无法证明指针对齐时,他们就不会进行内联和优化
memcpy
,这将对性能造成灾难性影响

TL:DR:您最好将数据声明为
uint64_t数组[…]
或将其动态分配为
uint64_t
或最好是
alignas(16)uint64_t数组[…]确保至少对齐8个字节,如果指定
alignas
,则至少对齐16个字节

由于
uint8\u t
几乎肯定是
unsigned char*
,因此通过
uint8\u t*
访问
uint64\u t
的字节是安全的(但对于uint8\u t数组,则相反)。因此,对于窄元素类型为
unsigned char
的这种特殊情况,您可以避开严格的别名问题,因为
char
是特殊的


GNU C本机向量语法示例: GNU C本机向量始终允许与其基础类型(例如,
int\uu属性(vector\u size(16)))进行别名。
可以安全地别名
int
,但不能使用
float
uint8\t
或其他任何内容

#include <stdint.h>
#include <stddef.h>

// assumes array is 16-byte aligned
void dec_mem_gnu(uint8_t *array) {
    typedef uint8_t v16u8 __attribute__ ((vector_size (16), may_alias));
    v16u8 *vecs = (v16u8*) array;
    vecs[0] -= 1;
    vecs[1] -= 1;   // can be done in a loop.
}
我认为这与其他非循环答案的基本思想是一样的;先防止进位,然后修正结果

这是5条ALU指令,比我认为的最上面的答案更糟糕。但看起来关键路径延迟只有3个周期,两条指令链(每个指令链包含2条指令)导致XOR。@Restore Monica-ζ--的答案编译为4个周期的dep链(对于x86)5个周期的循环吞吐量因在关键路径上还包含一个幼稚的
而受到限制,并且循环在延迟上没有瓶颈

然而,这对clang是没有用的。它甚至没有按照加载的顺序添加和存储,所以它甚至不能很好地进行软件流水

# RISC-V clang (trunk) -O3
dec_mem_gnu(unsigned char*):
        lb      a6, 7(a0)
        lb      a7, 6(a0)
        lb      t0, 5(a0)
...
        addi    t1, a5, -1
        addi    t2, a1, -1
        addi    t3, a2, -1
...
        sb      a2, 7(a0)
        sb      a1, 6(a0)
        sb      a5, 5(a0)
...
        ret


我不打算尝试编写代码,但如果减量为1,您可以按8 1的组减量,然后检查结果的LSB是否已“翻转”。任何没有切换的LSB都表示从相邻的8位进行进位。应该可以计算出一个AND/OR/XOR序列来处理这个问题,而不需要任何分支。

这并不能并行工作,这是OP的问题。是的@nickelpro是对的,这会一个接一个地进行减法,我想同时减去所有8位整数。我非常感谢你的回答,谢谢bro@nickelpro当我开始回答问题时,还没有对问题的平行部分进行编辑,因此我直到提交后才注意到它,如果它对其他人有用,它将保留,因为它至少回答了进行位运算的部分通过使用
为每个(std::execution::par_unseq,…
而不是我的错,我提交了问题,然后意识到我没有说它需要并行,所以编辑它们是需要8位还是可以改为7位?它们必须是8位对不起:(这类技术被称为相关技术:您是否希望一个包含零的字节包装为0xff?您能否解释或参考那里发生的情况?这似乎很有趣。我试图在没有SIMD指令的情况下完成这项工作,但我发现这一点很有趣:)另一方面,SIMD代码很糟糕。编译器完全误解了这里发生的事情。E:这是一个“显然是由编译器完成的,因为没有人会这么愚蠢”的例子@PeterCordes:我更多地考虑了
\uuu vector\u循环(索引、开始、过去、pad)
实现可以将其视为的构造(index=start;index@PeterCordes:而
restrict
是有帮助的(如果标准认可了“至少可能基于”的概念,然后定义了“基于”和“至少可能基于”的话,则会更有帮助)直截了当,没有愚蠢和不可行的角盒)我的建议还将允许编译器执行比要求的更多的循环执行——这将大大简化矢量化,但标准没有对此做出规定。
for
不会并行运行,您是否对每个
都与
相混淆?@LTPCGO不,我不打算对这个for循环进行并行化,这个实际上会破坏算法。但这段代码并行处理64位整数中的不同8位整数,即所有8个减法都是同时进行的,但它们最多需要8个步骤。我意识到我的要求可能有点不合理,但这与我需要的非常接近。谢谢:)我需要我的代码在没有SIMD指令的RISC-V机器上运行,更不用说支持MMX@cam-怀特明白了——这可能是你能做的最好的了。我会跳上戈德博尔特去检查他的精神状态
SWAR sub z = x - y
    z = ((x | H) - (y &~H)) ^ ((x ^~y) & H)
#define U64MASK 0x0101010101010101U
#define MSBON 0x8080808080808080U
uint64_t decEach(uint64_t i){
      return ((i | MSBON) - U64MASK) ^ ((i ^ MSBON) & MSBON);
}
in:  0000000000000000
out: ffffffffffffffff

in:  f200000015000013
out: f1ffffff14ffff12

in:  0000000000000100
out: ffffffffffff00ff

in:  808080807f7f7f7f
out: 7f7f7f7f7e7e7e7e

in:  0101010101010101
out: 0000000000000000
uint64t[rax] decEach(rcx):
    movabs  rcx, -9187201950435737472
    mov     rdx, rdi
    or      rdx, rcx
    movabs  rax, -72340172838076673
    add     rax, rdx
    and     rdi, rcx
    xor     rdi, rcx
    xor     rax, rdi
    ret
// Repeat the SWAR dec in a loop as a microbenchmark
uint64_t perftest(uint64_t dummyArg){
    uint64_t dummyCounter = 0;
    uint64_t i = 0x74656a6d27080100U; // another dummy value.
    while(i ^ dummyArg) {
        IACA_START
        uint64_t naive = i - U64MASK;
        i = naive + ((i ^ naive ^ U64MASK) & U64MASK);
        dummyCounter++;
    }
    IACA_END
    return dummyCounter;
}


Throughput Analysis Report
--------------------------
Block Throughput: 4.96 Cycles       Throughput Bottleneck: Backend
Loop Count:  26
Port Binding In Cycles Per Iteration:
--------------------------------------------------------------------------------------------------
|  Port  |   0   -  DV   |   1   |   2   -  D    |   3   -  D    |   4   |   5   |   6   |   7   |
--------------------------------------------------------------------------------------------------
| Cycles |  1.5     0.0  |  1.5  |  0.0     0.0  |  0.0     0.0  |  0.0  |  1.5  |  1.5  |  0.0  |
--------------------------------------------------------------------------------------------------
uint64_t sub(uint64_t arg) {
   uint64_t res = 0;

   for (int i = 0; i < 64; i+=8) 
     res += ((arg >> i) - 1 & 0xFFU) << i;

    return res;
   }
uint64_t sub(uint64_t arg) {
    uint64_t x1 = arg | 0x80808080808080;
    uint64_t x2 = ~arg & 0x80808080808080;
    // or uint64_t x2 = arg ^ x1; to save one instruction if you don't have an andnot instruction
    return (x1 - 0x101010101010101) ^ x2;
}
// GNU C: gcc/clang/ICC but not MSVC
typedef uint64_t  aliasing_u64 __attribute__((may_alias));  // still requires alignment
typedef uint64_t  aliasing_unaligned_u64 __attribute__((may_alias, aligned(1)));
#include <stdint.h>
#include <stddef.h>

// assumes array is 16-byte aligned
void dec_mem_gnu(uint8_t *array) {
    typedef uint8_t v16u8 __attribute__ ((vector_size (16), may_alias));
    v16u8 *vecs = (v16u8*) array;
    vecs[0] -= 1;
    vecs[1] -= 1;   // can be done in a loop.
}
# GCC8.2 -O3 for RISC-V for vector_size(8) and only one vector

dec_mem_gnu(unsigned char*):
        lui     a4,%hi(.LC1)           # generate address for static constants.
        ld      a5,0(a0)                 # a5 = load from function arg
        ld      a3,%lo(.LC1)(a4)       # a3 = 0x7F7F7F7F7F7F7F7F
        lui     a2,%hi(.LC0)
        ld      a2,%lo(.LC0)(a2)       # a2 = 0x8080808080808080
                             # above here can be hoisted out of loops
        not     a4,a5                  # nx = ~x
        and     a5,a5,a3               # x &= 0x7f... clear high bit
        and     a4,a4,a2               # nx = (~x) & 0x80... inverse high bit isolated
        add     a5,a5,a3               # x += 0x7f...   (128-1)
        xor     a5,a4,a5               # x ^= nx  restore high bit or something.

        sd      a5,0(a0)               # store the result
        ret
# RISC-V clang (trunk) -O3
dec_mem_gnu(unsigned char*):
        lb      a6, 7(a0)
        lb      a7, 6(a0)
        lb      t0, 5(a0)
...
        addi    t1, a5, -1
        addi    t2, a1, -1
        addi    t3, a2, -1
...
        sb      a2, 7(a0)
        sb      a1, 6(a0)
        sb      a5, 5(a0)
...
        ret