在不使用任何空间的情况下将Python长整型放入内存中

3
我想将许多大型长整数放入内存中,并且它们之间没有任何空格。如何在Linux中使用Python 2.7代码实现?这些大型长整数都使用相同的位数,总共有约4GB的数据。在每个长整数之间留下几个位的空格,以使每个长整数在内存中使用8位的倍数是可以的。我后来想对它们进行按位操作。目前,我正在使用Python列表。但我不确定它是否在整数之间不留下内存空间。ctypes能帮助吗?谢谢。旧代码使用了bitarray (https://pypi.python.org/pypi/bitarray/0.8.1)。
import bitarray
data = bitarray.bitarray()
with open('data.bin', 'rb') as f:
    data.fromfile(f)
result = data[:750000] & data[750000:750000*2]

这个方案可行,bitarray在内存中没有间隙。但是,在计算机上,bitarray的按位与运算速度比本地python长整型的按位操作慢了约6倍。在旧代码中对bitarray进行切片和在新代码中访问列表中的元素使用大致相同的时间。

新代码:

import cPickle as pickle

with open('data.pickle', 'rb') as f:
    data = pickle.load(f)
# data is a list of python's (long) integers
result = data[0] & data[1]

Numpy: 在上述代码中,result = data [0]&data [1]会创建一个新的长整型。 Numpy对于numpy.bitwise_and有一个"out"选项。这将避免创建新的numpy数组。然而,numpy的布尔数组似乎每个布尔值使用一个字节而不是一个位。将布尔数组转换为numpy.uint8数组可避免此问题,但计算设置位的数量太慢。
Python的原生数组无法处理大的长整型:
import array
xstr = ''
for i in xrange(750000):
    xstr += '1'
x = int(xstr, 2)

ar = array.array('l',[x,x,x])
# OverflowError: Python int too large to convert to C long

你有你想要实现的一些示例代码吗? - gerosalesc
2
你为什么关心内存布局?你计划与另一个进程共享那些内存吗? - das-g
嗯,我猜列表比位数组的内存效率低。有很多数据。实际程序使用了多进程库,并且不修改数据...程序的第一个版本多次从硬盘读取数据。那个版本非常慢。 - rxu
你不能对数据进行滑动窗口处理吗?例如 data1 = bytearray(93750); data2 = bytearray(93750); f.readinto(data1); f.readinto(data2); result = map(operator._and, data1, data2)。这假设位被打包进文件中。 - Eryk Sun
2个回答

3
您可以使用数组模块,例如:
import array
ar = array('l', [25L, 26L, 27L])
ar[1]  # 26L

我尝试了,但在我的情况下长整数太大了。谢谢你的建议。 - rxu

2
Python长整型的内部结构是空间效率低下的主要原因。在64位平台上,Python只使用30个比特位来存储一个值,而gmpy2库提供了访问GMP(GNU多精度算术库)的方法。gmpy2.mpz类型的内部结构利用了所有可用的比特位。以下是存储750000比特值时两者之间的大小差异。
>>> import gmpy2
>>> import sys
>>> a=long('1'*750000, 2)
>>> sys.getsizeof(a)
100024
>>> sys.getsizeof(gmpy2.mpz(a))
93792

使用 `gmpy2.mpz` 的 `&` 运算也显著更快。
$ python -m timeit -s "a=long('A'*93750,16);b=long('7'*93750)" "c=a & b"
100000 loops, best of 3: 7.78 usec per loop
$ python -m timeit -s "import gmpy2;a=gmpy2.mpz('A'*93750,16);b=gmpy2.mpz('7'*93750)" "c=a & b"
100000 loops, best of 3: 4.44 usec per loop

如果您的所有操作都是原地操作,gmpy2.xmpz 类型允许在不创建新实例的情况下更改实例的内部值。只要所有操作都是立即执行的,它就会更快。
$ python -m timeit -s "import gmpy2;a=gmpy2.xmpz('A'*93750,16);b=gmpy2.xmpz('7'*93750)" "a &= b"
100000 loops, best of 3: 3.31 usec per loop

免责声明:我维护gmpy2库。

很好。我想我会改变代码来使用gmpy2。是否可以在多个进程(多进程库)之间共享gmpy.mpz而不需要在内存中复制? - rxu
在进程之间共享Python对象是困难的。您可能想阅读https://dev59.com/IXM_5IYBdhLWcg3wslbs - casevh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接