【问题标题】:What's a fast and efficient approach to large bitfields, with Python & numpy?使用 Python 和 numpy 处理大型位域的快速有效方法是什么?
【发布时间】:2020-10-29 11:19:52
【问题描述】:

我想在 Python 中使用紧凑且快速的大型位域,理想情况下,除了 numpy 之外没有或几乎没有依赖项。我想要的操作大致相当于:

bits = new_bitfield(3000000)  # 3 million bits
bits.set_bit(n, 1)
bits.set_bit(n, 0)
bits.get_bit(n)

我希望位的底层存储非常紧凑/开销低。 (理想情况下,bits 对象将只占用 366.21 Kibibytes 的头发宽度。)

我希望获取和设置非常快,并且类型检查/类型强制开销最小 - 在使用 Cython 或 Numba 特定代码(或它们各自的内联选项)时甚至可能特别快。

在尽可能保持 Pythonic 外观的同时,实现 C 速度/紧凑性的最佳方式是什么?

【问题讨论】:

  • “在尽可能保持 Pythonic 外观的同时,实现 C 速度/紧凑性的最佳方法是什么?”写一个 C 扩展
  • 这取决于你想用它们做什么。例如,在 Numba 中实现它应该很容易(只需更改 stackoverflow.com/a/30590727/4045774 的语法)。但这只有在您从一个也已编译的函数中调用它时才真正有意义。

标签: python numpy numba bit-fields


【解决方案1】:

自己推出应该不会太难,另一种选择是使用现有的实现。无论好坏,std::vector<bool> 正是您想要的:它对每个值仅使用 1 位(因此 bool-template 参数有些误导,因为 bool 至少有 1 个字节长)。

使用 Cython 可能看起来像这样(它被编译为 c++ 扩展):

%%cython -+
from libcpp.vector cimport vector
from libcpp cimport bool

cdef class Bitset:
    cdef vector[bool] bset;
    
    def __cinit__(self, size_t size):
        self.bset.resize(size, False);
        
    cpdef void set_bit(self, size_t pos, bint val) except *:        
        # self.bset[pos] = val would not check out of range
        # self.bset.at(pos) = val doesn't work with Cython
        if pos < self.bset.size():
            self.bset[pos] = val;
        else:
            raise IndexError("out of range access")    
            
    cpdef bint get_bit(self, size_t pos):
        return self.bset.at(pos)

可以用作

mybitset = Bitset(10)
mybitset.set_bit(2, True)
mybitset.get_bit(1), mybitset.get_bit(2) #returns (False, True)

还有

mybitset.set_bit(11, True) #throws
mybitset.get_bit(12, True) #throws

抛出并且不要以未定义的行为结束。

显然,为了保持开销最小,使用Bitset-cdef-class的代码也应该用Cython编写,因此cdef-部分接口可以使用,无需转换为Python 对象,接口的 Python 部分需要它(如上所示)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-16
    • 2011-05-29
    • 2019-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    相关资源
    最近更新 更多