是否有类似std::bitset的非拥有引用来为其他容器中的数据提供按位运算和计数?

问题描述 投票:0回答:1

我正在尝试为 ORB 功能实现 C++17 可移植且高效的汉明距离函数,希望在为 x86 和 ARM 编译时自动使用 SIMD。

问题
std::bitset

std::bitset<N>
提供了按位运算和计数的标准方式,并且它也优于
__builtin_popcount
。然而,它是一种拥有数据的容器类型,并且不容易从存储为
cv::Mat
(由
cv::ORB::detectAndCompute
计算)的 256 位向量转换。

此线程要求将

cv::Mat
转换为
std::bitset<256>
。我不认为
memcpy
的答案是正确的,因为我在
https://en.cppreference.com/w/cpp/utility/bitset
中没有找到 std::bitset 的内存布局。此外,
std::bitset
构造函数不支持初始化超过
sizeof(unsigned long long)
位。

我的实施有问题

为了避免复制,我当前的实现使用类似 span 的类:

struct ORB_view {
  inline static constexpr int length = 256 / sizeof(uint32_t) / 8;
  const uint32_t* data;
};

但是,这不能直接使用按位运算和 popcount,并且会导致在实现中出现显式的 SIMD 指令,我希望通过使用

std::bitset<N>::count()
来避免这种情况。

我所期待的

因此,在这种情况下,提供按位运算和计数的非拥有引用将非常有帮助。所需的功能是:

  • 无数据复制,
  • 固定长度为
    std::bitset
  • 按位异或运算和popcount
  • 没有适用于 ARM 或 x86 的显式 SIMD 代码

据我所知标准库没有提供。

c++ c++17 bitwise-operators simd std-bitset
1个回答
0
投票

首先,如果性能很重要,则在大多数情况下您应该远离

std::bitset
。它会在无效输入上引发异常,并且这些运行时检查对于高性能应用程序来说不是最佳选择。

其次,如果你被允许使用C++20,你可以使用

std::popcount
。在 C++17 之前,您可以使用
__builtin_popcnt
。您已经指出了可移植性问题,但这些问题可以通过条件编译来克服:

#if __cpp_lib_bitops >= 201907L // C++20 std::popcount
#include <bit>
using std::popcount;
#elif defined(__GNUC__) // GCC and clang/LLVM
inline int popcount(unsigned char x) noexcept {
    return __builtin_popcount(x);
}
inline int popcount(unsigned short x) noexcept {
    return __builtin_popcount(x);
}
inline int popcount(unsigned int x) noexcept {
    return __builtin_popcount(x);
}
inline int popcount(unsigned long x) noexcept {
    return __builtin_popcountl(x);
}
inline int popcount(unsigned long long x) noexcept {
    return __builtin_popcountll(x);
}
#elif defined(_MSC_VER) // MSVC
#include <intrin.h>
__forceinline int popcount(std::uint8_t x) noexcept {
    return static_cast<int>(__popcnt16(x));
}
__forceinline int popcount(std::uint16_t x) noexcept {
    return static_cast<int>(__popcnt16(x));
}
__forceinline int popcount(std::uint32_t x) noexcept {
    return static_cast<int>(__popcnt(x));
}
__forceinline int popcount(std::uint64_t x) noexcept {
    return static_cast<int>(__popcnt64(x));
}
#endif

请参阅编译器资源管理器中的实时示例

使用此包装器(取自

bitmanip
),您可以为 GCC、MSVC 和 clang 安全地调用
popcount
。 Android 原生编译器是基于 LLVM 的,所以它也应该可以工作。

创建一个包装器非常容易,例如:

int popcount_256(const std::uint64_t nums[4]) {
    int sum = 0;
    for (std::size_t i = 0; i < 4; ++i) {
        sum += popcount(nums[i]);
    }
    return sum;
}

显然,这也可能是您的

ORB_view
的成员函数。尽管 clang 自动向量化了此代码,但没有显式的 SIMD 代码。

© www.soinside.com 2019 - 2024. All rights reserved.