如果 N < 宽度DST,x86_64 有效地设置寄存器 DST 中的位 N,否则 DST = 0

如何解决如果 N < 宽度DST,x86_64 有效地设置寄存器 DST 中的位 N,否则 DST = 0

我正在尝试找到一种在 x86_64 程序集中执行以下操作的有效方法:

if(N < word_size) {
    dst[N] = 1; // as in Nth bit of dst = 1 
}
else {
    dst[word_size - 1:0] = 0
} 

如果“else”情况没有取消设置其他位,或者如果“if”情况没有取消其他位,我可以获得所需的结果。重要的是如果 N > word_size 它不会设置任何位

我找不到任何可能执行此操作的指令,因为 bt[s/c],shlx,sal,rol,shld 都出现在以宽度为 src 的模块中。

用例基本上是我将迭代一个已知长度的位向量,并且想要 A) 找到第一个设置位并返回它的位置,或者 B) 测试所有位,如果没有设置位找到向量的返回长度。

// rsi has length
L(keep_searching):
movq %(rdi),%rax
testq %rax,%rax
jnz L(found)
subq $64,rsi
jbe L(done) // this done will return origional value of rsi
addq $8,%rdi
jmp L(keep_searching)

我认为如果我可以在 rax if rsi

有人知道可以执行此操作的指令吗?我能想到的每条指令都在 src 上使用模块。任何帮助将不胜感激。

谢谢!

一些对我来说适用于 32 位的版本。如果我使用 MMX,@PeterCordes 指出 pllsq 正是我想要的。


uint64_t __attribute__((noinline,noclone)) shift(uint64_t cnt) {
    uint64_t ret = 0;
    asm volatile(
        "cmpq $32,%[cnt]\n\t"
        "setbe %b[ret]\n\t"
        "shlxq %[cnt],%[ret],%[ret]\n\t"
        : [ ret ] "+r"(ret)
        : [ cnt ] "r"(cnt)
        : "cc");
    return ret;
}


uint64_t __attribute__((noinline,noclone)) shift2(uint64_t cnt) {
    uint64_t ret = 0,tmp = 0;
    asm volatile(
        "leaq -33(%[cnt]),%[tmp]\n\t"
        "movl $1,%k[ret]\n\t"
        "shlxq %[cnt],%[ret]\n\t"
        "sarq $63,%[tmp]\n\t"
        "andq %[tmp],%[ret]\n\t"
        : [ ret ] "+r"(ret),[ tmp ] "+r"(tmp),[ cnt ] "+r"(cnt)
        :
        : "cc");
    return ret;
}


uint64_t __attribute__((noinline,noclone)) shift3(uint64_t cnt) {
    uint64_t ret,tmp;
    asm volatile(
        "leaq -33(%[cnt]),%[tmp]\n\t"
        "btsq %[cnt],[ cnt ] "+r"(cnt)
        :
        : "cc");
    return ret;
}

解决方法

尚未验证,但

mov rax,1  // common
mov rdx,0  // common

cmp rcx,64
shlxq rbx,rax,rcx
cmova rbx,rdx

可能比建议的替代方案性能略高,因为比较和移位现在是独立的,可以并行执行。

编辑

从用例来看,这似乎是一个 XY 问题——迭代 bitset 中的位的有效方法是使用 n & (n-1) 技巧或变体; popcount(n ^ (n-1)) 应该给出最小位集的索引。 n&=n-1 将清除 LSB。

,

SIMD 移位(如 SSE2 psllq xmm1,xmm2)使计数饱和,但这在这里不太可能有用,因为我认为您想将其 OR 到内存中的数据中作为此循环的标量版本的结束条件?

我更倾向于使用仍从 cmov 设置的 FLAGS 的清零寄存器中的 sub。您可以在 SUB 之前或之后使用 BTS 将 1<<(rsi&63) 创建到清零寄存器中; SUB之前好,因为BTS修改了CF。请注意,rsi&63 不受 rsi -= 64 的影响。

对于循环条件,这可能不是一个好的选择:只需使用单 uop sub/ja,单独的 test/jnz 是通常不采取。其中之一位于底部而不是无条件的 jmp:这是这里最明显和基本的优化:Why are loops always compiled into "do...while" style (tail jump)?

或者更好的是,使用 SSE2(x86-64 的基线)一次检查 16 个字节(2 个 qwords 或 4 个双字)是否为全非零。或者,如果您不希望很快找到第一个设置位,甚至可以将几个向量一起 POR 一起检查,即调整大行程计数,代价是最终找到它的处理速度较慢。 (最后一次迭代可以是标量)。

(查看 glibc 的 strlen 或特别是 memchr 以获取有关使用 SIMD 优化大数组 not-found-early 情况的更多想法。在这种情况下,他们使用 {{1}如果任何向量在该位置具有零,则获得零,但您想要相反的:pminub 如果任何向量具有非零,则获得非零。)

将内存中的两个值“或”在一起也适用于标量,作为展开的一种方式。

por

但请注意, mov (%rdi),%rax or 8(%rdi),%rax jnz found ... add $16,%rdi /or 为 2 uop,而 jnz/test 为 1。
OTOH,让 jnz / cmpq $0,(%rdi) 在 Intel 上进行微型和宏观熔断器可能是不可能的; IIRC 可能带有寄存器源。因此,如果您非常积极地调整,内存源 jne 可能会花费 2 uop 来完成两倍的工作,而不是仅多 1 uop。您需要与展开并执行两个单独的 load/test/jcc 或 cmp-mem/jcc 的循环进行比较,以保持指针增量逻辑的循环开销公平。 (还有展开逻辑以处理可能的奇数个 qwords。)


但作为练习,让我们看看我们可以用你的想法做些什么:在这种情况下,可以提前计算一次非零移位结果(因为 rsi-=64 不会改变 rsi%64),并被吊出环。

or

不幸的是,OR 不能像 TEST 那样与 JCC 进行宏融合。 (或英特尔 SnB 系列上的 SUB)。但是内存源 OR 是前端的一个 uop。

不幸的是, xor %edx,%edx bts %rsi,%rdx # rdx = 1 << (rsi&63) // rsi has length L(keep_searching): add $8,%rdi xor %eax,%eax # need to re-create a zero every time sub $64,%rsi cmovbe %rdx,%rax # 0 or 1<<(rsi&63) to put a bit there for us to find or -8(%rdi),%rax jnz L(keep_searching) found_or_done: tzcnt %rax,%rax add orig_rsi?,%rax ... cmovbe 需要 2 uop,因为它们需要 CF 和 ZF。 (请参阅 What is a Partial Flag Stall? - 最近的英特尔没有部分标志停顿甚至合并,只有 CF 与其余部分(SPAZO),如果需要,微指令会分别读取两个输入。)但没有明显的原因, cmovasetbe 也是 2 uop (https://uops.info) - 也许英特尔从未更新 setcc uop 格式以用作 3 输入 uop(包括它们合并到低字节)。有趣的事实:这导致 seta 通常只能在“复杂”解码器中解码,如果您的代码不在 uop 缓存中:Can the simple decoders in recent Intel microarchitectures handle all 1-µop instructions?

多亏了 setcc,Intel 上的循环体总共有 7 个 uops。 6 在 AMD。

比较没有此技巧的简单标量搜索循环与 4 uops 的比较。这可以在 Intel 上以每次迭代 1 个周期的速度运行(Haswell 和更高版本可以在每个时钟运行 2 个分支,只要最多采用 1 个)。我认为也在 AMD Zen 上。所以我们每个周期搜索大约 8 个字节,大约是我们可以用 SIMD 做的一半。但启动和结束开销较低。

cmov

如果您将负数组索引计数到零,则可以避免循环中同时包含 L(loop): # do { mov (%rdi),%rax # 1 uop test %rax,%rax jnz L(found) # 1 uop (macro-fused) add $8,%rdi # 1 uop cmp %rdi,%rdx jbe L(loop) # }while(p < endp) # 1 uops (macro-fused) L(done): add:使用通过添加设置的 FLAGS。 (或者对于简单版本,避免 CMP,让

你需要一个 sub 或类似的东西,但 Haswell 和更高版本可以保持索引寻址模式微融合作为具有 RW 目标的 2 操作数指令的一部分:Micro fusion and addressing modes)


or (%r10,%r11,8),%rax 不方便,因为它只适用于 8 位寄存器。但是如果你有一个像 RDX 这样的归零寄存器,你可以setcc r/m8 / setbe dl。即使您不想使用 SIMD,在 shlx %rsi,%rdx,%rcx / test 之前将其 OR 到 RAX 似乎也不值得。

正确预测的未采取测试/jnz 是一个单一的 uop,比您为创建 RAX 值所做的所有工作都便宜。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res