对于min(ctz(x), ctz(y)),我们可以使用ctz(x | y)来获得更好的性能.但是max(ctz(x), ctz(y))呢?

ctz表示"计数尾随零".

C++版本(Compiler Explorer)

#include <algorithm>
#include <bit>
#include <cstdint>

int32_t test2(uint64_t x, uint64_t y) {
    return std::max(std::countr_zero(x), std::countr_zero(y));
}

铁 rust 版(Compiler Explorer)

pub fn test2(x: u64, y: u64) -> u32 {
    x.trailing_zeros().max(y.trailing_zeros())
}

推荐答案

它们是等效的:

  • max(ctz(a),ctz(b))
  • ctz((a|-a)&(b|-b))
  • ctz(a)+ctz(b)-ctz(a|b)

数学标识ctz(a)+ctz(b)-ctz(a|b)个需要6条CPU指令,可在3路超标量CPU上并行执行3个步骤:

  • 3×CTZ
  • 1×按位OR
  • 1×加法
  • 1×减法

位混搭ctz((a|-a)&(b|-b))个需要6个CPU指令,在2路超标量CPU上可并行到4个步骤:

  • 2×否定
  • 2×按位或
  • 1×比特化-与
  • 1×CTZ

朴素max(ctz(a),ctz(b))个需要5条CPU指令,可在2路超标量CPU上并行执行4个步骤:

  • 2×CTZ
  • 1×对比
  • 1×条件分支
  • 1次加载/移动(使"输出"始终在同一寄存器中)

...但请注意,分支指令可能非常昂贵.

如果您的CPU有条件加载/移动指令,这将减少到4条CPU指令,执行3个超标量步骤.

如果您的CPU有一条max指令(例如SSE4),这将减少到3条CPU指令,执行两个超标量步骤.

尽管如此,超标量运算的机会取决于您试图将哪些指令放在彼此之间.通常,通过并行放置不同的指令可以获得最大的yield ,因为它们同时使用CPU的不同部分.通常,与"CTZ"单元相比,"ADD"和"BITED OR"单元会更多,因此执行多个CTZ指令实际上可能是限制因素,尤其是对于"数学标识"版本.

如果"比较和分支"太昂贵,你可以在4条CPU指令中设置一个非分支的"最大值".假设A和B是正整数:

  1. C=A-B
  2. 从D本身减go 之前的进位加上D(无论它以前持有什么值,D现在都是0或-1)
  3. C&;=D(C现在是MIN(0,A-B))
  4. A-=C(A‘现在是max(A,B))

Rust相关问答推荐

为什么拥有的trait对象的相等运算符移动了正确的操作数?

Tauri tauri—apps/plugin—store + zustand

如何使用syn插入 comments ?

如何在Tauri中将变量从后端传递到前端

从Type::new()调用函数

为什么铁 rust S似乎有内在的易变性?

S,一般性状和联想型性状有什么不同?

为什么铁 rust S的默认排序功能比我对小数组的 Select 排序稍微慢一些?

零拷贝按步骤引用一段字节

允许 rust 迹 struct 条目具有多种类型

为什么我必须使用 PhantomData?在这种情况下它在做什么?

如何将一个矩阵的列分配给另一个矩阵,纳尔代数?

具有多个键的 HashMap

通过mem::transmute将数组展平安全吗?

如何在 Rust 中将 Vec> 转换为 Vec>?

如何在 Rust 中将 UTF-8 十六进制值转换为 char?

Rustlings 切片原语

如何在 nom 中构建负前瞻解析器?

为什么分配对变量的引用使我无法返回它

如何在 Rust 的泛型函​​数中同时使用非拥有迭代器和消费迭代器?