【问题标题】:Good way of popping the least signifigant bit and returning the index?弹出最低有效位并返回索引的好方法?
【发布时间】:2022-05-22 22:59:01
【问题描述】:

我是 C++ 新手,我正在尝试编写一个函数来弹出最低有效位,然后返回该位的索引。有没有办法在不创建临时变量的情况下做到这一点?

现在我有一个查找索引的功能和一个弹出位的功能,但我想将两者结合起来。

inline int LSB(uint64_t b) {
return int(_tzcnt_u64(b));
}

inline uint64_t popLSB(uint64_t b, int index) {
    return (b ^ (1ui64 << LSB(b)));
}

我唯一的想法需要一个临时索引变量,感觉很糟糕。

int bestIdea(uint64_t *b) {
    int index = int(_tzcnt_u64(*b));
    *b ^= (1ui64 << index);
    return index;
}

有没有更好的方法来做到这一点?如果此代码有任何其他不必要或愚蠢的地方,我很乐意接受建议,这是我的第一个项目,我几乎不确定任何部分。

  • 为什么这让你感觉不好?具有易于阅读的实现是有好处的。任何没有临时的方法都可能导致代码的可读性降低。如果您担心性能:编译器可能会在优化这样的简单代码方面做得很好。
  • 此外考虑使用引用而不是指针。如果您选择int bestIdea(uint64_t&amp; b) { ... },则将检查 null 的责任转移给调用者,如果 null 完全有可能,而不是仅仅假设没有人会想到将 null 作为参数传递...
  • 谢谢!我会改变它,因为检查 null 是我无论如何都要让调用者做的事情。
  • bestIdea(0) 是什么?

标签: c++ bit-manipulation bitwise-operators


【解决方案1】:

使用临时变量没有任何问题。然而,现代架构是superscalarout-of-order,因此为了更好地适应它们,您应该只将其用于返回值,并且不要使用它来清除最低有效位以避免不必要的dependency chain

int popLsbAndReturnIndex(uint64_t *b) {
    int index = int(_tzcnt_u64(*b));
    *b &= *b - 1; // Not depend on the previous line
    return index;
}

现在我们有了更好的instruction-level parallelism 并且正文中的 2 行可以并行运行

当然,一些非常聪明的编译器可以识别模式并将您的原始代码编译为没有依赖关系的版本,但没有人能保证


还有一些建议:

  • 如果您有 C++20 或更高版本,请使用 std::countr_zero 而不是 _tzcnt_u64
  • 使用引用而不是指针

结果是这样的

int bestIdea(uint64_t &b) {
    auto index = std::countr_zero(*b);
    b &= b - 1;
    return index;
}

【讨论】:

  • 我建议使用两者都不参考也不指针。反而,按值传递.由于输入是标量,因此按值传递的效率永远不会降低,而且很可能是更多的效率,取决于编译器是否能够完全内联它。
  • @CodyGray 是的,但在这种情况下, b 不仅是一个输入,它也是一个输出......
猜你喜欢
  • 1970-01-01
  • 2017-02-26
  • 2010-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-07
相关资源
最近更新 更多