gpt4 book ai didi

c++ - 检查uint8_t [8]是否包含任何非0并以一个内存负载访问非零插槽

转载 作者:行者123 更新时间:2023-12-01 14:42:13 25 4
gpt4 key购买 nike

基本上我有一个定义的结构

#define BATCH_SIZE 8
#define BATCH_SIZE_LOG 3
//#define BATCH_MASK 0x7070707070707070

// for the sake of understanding the ASM turn this into a no-op
#define BATCH_MASK (~(0UL))
struct batcher {
uint8_t indexes[8];
uint64_t vals[8 * BATCH_SIZE];

uint32_t __attribute__((noinline))
push(const uint64_t i, const uint64_t v) {
if(__builtin_expect(indexes[i] < (BATCH_SIZE - 1), 1)) {
vals[8 * i + indexes[i]++] = v;
return 0;
}
return 1;
}

uint32_t __attribute__((noinline))
claim(const uint64_t i) {
if(__builtin_expect(indexes[i] == (BATCH_SIZE - 1), 1)) {
indexes[i] = 8;
return 0;
}
return 1;
}

uint32_t
can_pop() const {
if(*((uint64_t *)(&indexes[0])) & BATCH_MASK) {
return 1;
}
return 0;
}

uint64_t __attribute__((noinline))
pop() {
if(__builtin_expect(can_pop(), 1)) {
const uint32_t idx = _tzcnt_u64(*((uint64_t *)(&indexes[0])) & BATCH_MASK) >> BATCH_SIZE;
return vals[8 * idx + --indexes[idx]];
}
return 0;
}
};
我很好奇的是,是否可以仅通过 pop进行1次内存加载来实现 indexes(所以总共需要2种内存,从 indexes中实现1种内存,从 vals中实现1种内存)
第一个内存负载是将所有 indexes解释为 uint64_t,以便我可以检查它是否为非0,如果是,则使用这些索引之一。
我一直在看汇编输出 here
它实现了 pop
batcher::pop():
movq (%rdi), %rax // first load from indexes
testq %rax, %rax
jne .L11
ret
.L11:
xorl %edx, %edx
movzbl (%rdi,%rdx), %eax // second load from indexes
decl %eax
movb %al, (%rdi,%rdx)
movzbl %al, %eax
movq 8(%rdi,%rax,8), %rax
ret
编译器实现此方法的方式是从 %(rdi)%rax,将其解释为 uint64_t(测试是否存在非0索引),如果条件通过,则第二次加载将加载计算出的 uint8_t索引。
我想知道是否有一种方法可以在没有两个负载的情况下在程序集中实现 pop(我将要做什么)。我知道我可以通过对第一次加载的结果进行移位/屏蔽来完成相同的逻辑。我特别想知道的是,是否有一种方法可以索引第一次加载产生的 uint64_t,就好像 uint8_t[8]数组在哪里。
我的猜测是,这可能不是因为寄存器没有内存地址,所以这样做并不完全有意义,但是我可能会丢失一些专门为隔离 uint64_t中的字节而编写的指令,或者某种方式可以重构 pop的汇编实现以启用此功能。
注意:我仅限于Intel Skylake上可用的指令集。
如果有人有任何想法,我将不胜感激。谢谢!

最佳答案

大概是tzcnt,将其四舍五入到8位的倍数,然后右移(使用BMI2 shrx,因此它是单个uop)。然后,非零字节位于寄存器的底部,您可以在其中movzbl将其零扩展到任何其他reg(not the same one, that would defeat mov-elimination)中。

    tzcnt  %rax, %rcx          # input in RAX
and $-8, %ecx # 0xff...f8
shrx %rcx, %rax, %rdx # rdx = rax >> cl
movzbl %dl, %eax # zero latency between separate registers
(如果全零是可能的,则如果需要检测这种情况,则为 test / jz,或者只是让移位发生。qword移位64会使值保持不变,因此结果将为 0。)
您可以使用_tzcnt_u64之类的内在函数来做到这一点;对于此使用内联asm没有明显的好处。您可以使用GNU C进行未对齐的严格混叠安全qword加载 typedef uint64_t aliasing_u64 __attribute__((aligned(1), may_alias))

仅使用8个字节,对移动掩码结果上的 pcmpeqb / pmovmskb / tzcnt的常规SIMD查找字节位置可能会过大。 (然后,整数 movzbl使用字节偏移量从内存中加载该字节)。

关于c++ - 检查uint8_t [8]是否包含任何非0并以一个内存负载访问非零插槽,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63540228/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com