我有一些bytearray
,其长度为2*n
:
a1 a2 b1 b2 c1 c2
我需要在每个2字节字中切换字节字节序,并使:
a2 a1 b2 b1 c2 c1
现在我使用下一种方法但是我的任务非常慢:
converted = bytearray([])
for i in range(int(len(chunk)/2)):
converted += bytearray([ chunk[i*2+1], chunk[i*2] ])
是否可以通过调用某个系统/ libc函数来切换bytearray
的字节序?
好的,感谢所有人,我提出了一些建议:
import timeit
test = [
"""
converted = bytearray([])
for i in range(int(len(chunk)/2)):
converted += bytearray([ chunk[i*2+1], chunk[i*2] ])
""",
"""
for i in range(0, len(chunk), 2):
chunk[i], chunk[i+1] = chunk[i+1], chunk[i]
""",
"""
byteswapped = bytearray([0]) * len(chunk)
byteswapped[0::2] = chunk[1::2]
byteswapped[1::2] = chunk[0::2]
""",
"""
chunk[0::2], chunk[1::2] = chunk[1::2], chunk[0::2]
"""
]
for t in test:
print(timeit.timeit(t, setup='chunk = bytearray([1]*10)'))
结果是:
$ python ti.py
11.6219761372
2.61883187294
3.47194099426
1.66421198845
现在,步进为2的步调切片分配速度最快。还要感谢F先生的详细解释,但由于numpy
答案 0 :(得分:5)
您可以使用步骤为2的切片分配:
byteswapped = bytearray(len(original))
byteswapped[0::2] = original[1::2]
byteswapped[1::2] = original[0::2]
或者如果你想在现场进行:
original[0::2], original[1::2] = original[1::2], original[0::2]
时序显示大规模切片优于大型数组的Python级循环:
>>> timeit.timeit('''
... for i in range(0, len(chunk), 2):
... chunk[i], chunk[i+1] = chunk[i+1], chunk[i]''',
... 'chunk=bytearray(1000)')
81.70195105159564
>>>
>>> timeit.timeit('''
... byteswapped = bytearray(len(original))
... byteswapped[0::2] = original[1::2]
... byteswapped[1::2] = original[0::2]''',
... 'original=bytearray(1000)')
2.1136113323948393
>>>
>>> timeit.timeit('chunk[0::2], chunk[1::2] = chunk[1::2], chunk[0::2]', 'chunk=
bytearray(1000)')
1.79349659994989
对于小数组,切片仍然胜过显式循环,但区别并不大:
>>> timeit.timeit('''
... for i in range(0, len(chunk), 2):
... chunk[i], chunk[i+1] = chunk[i+1], chunk[i]''',
... 'chunk=bytearray(10)')
1.2503637694328518
>>>
>>> timeit.timeit('''
... byteswapped = bytearray(len(original))
... byteswapped[0::2] = original[1::2]
... byteswapped[1::2] = original[0::2]''',
... 'original=bytearray(10)')
0.8973060929306484
>>>
>>> timeit.timeit('chunk[0::2], chunk[1::2] = chunk[1::2], chunk[0::2]', 'chunk=
bytearray(10)')
0.6282232971918802
答案 1 :(得分:2)
如果你使用numpy的frombuffer
函数,你可以构造一个实际上共享ndarray
的物理内存的numpy bytearray
,然后交换操作可以在 - 而不是副本。
您可以直接在byt
上执行相同的索引编制,例如
byt[i] = byt[i+1]
但是使用numpy中的显式类型来获取缓冲数组的句柄通常会让你做得更多,尤其是bytearray
,它本身非常有限。
但要小心。即使在Python级别,bytearray
表示无符号的8位整数值(0-255),bytearrayobject.h
中的实际底层C实现使用普通char
作为字节值( see here for more info)。如果为此使用numpy,您可能希望使用dtype
为frombuffer
指定可选的dtype=numpy.ubyte
参数。
import numpy as np
byt = bytearray(range(256))
npbyt = np.frombuffer(byt, dtype=np.ubyte)
for i in range(0, len(npbyt)-1, 2):
temp = npbyt[i]
npbyt[i] = npbyt[i+1]
npbyt[i+1] = temp
print(list(byt))
打印
[1,
0,
3,
2,
5,
4,
...
255,
254]
我在处理名为buffersort的小型项目时遇到了其中一些问题,该项目可以对实现可写缓冲区协议的Python对象执行就地排序,如bytearray
所做的那样。
如果您对从那里抓取Cython源感兴趣,可以使用简单的帮助函数_swap
,这样可以轻松地执行您想要的操作。但是,对于您的用例,它可能非常过分。
答案 2 :(得分:1)
你也可以在适当的位置交换它们并使用原始数组。
chunk = bytearray([1,2,3,4])
for i in range(0, len(chunk), 2):
chunk[i], chunk[i+1] = chunk[i+1], chunk[i]
答案 3 :(得分:1)
我可以重复@user2357112支持Monica的就地切片方法的结果。但是如果数据大小是原来的十倍,那么它比 array.byteswap() 慢两倍:
>>> timeit.timeit('chunk[0::2], chunk[1::2] = chunk[1::2], chunk[0::2]', 'chunk=bytearray(10000)')
12.54664899999625
>>> timeit.timeit('a=array.array("H",chunk);a.byteswap();chunk=a.tobytes()', 'import array;chunk=bytearray(10000)')
6.398380300001008
答案 4 :(得分:0)
从How to byte-swap a 32-bit integer in python?的一些答案中获得
import struct
def htonl_slice(data):
byteswapped = bytearray(4)
byteswapped[0::4] = data[3::4]
byteswapped[1::4] = data[2::4]
byteswapped[2::4] = data[1::4]
byteswapped[3::4] = data[0::4]
return byteswapped
def htonl_slice_2(data):
byteswapped = bytearray(len(data))
byteswapped[0::4] = data[3::4]
byteswapped[1::4] = data[2::4]
byteswapped[2::4] = data[1::4]
byteswapped[3::4] = data[0::4]
return byteswapped
def htonl_struct(data):
return struct.pack(">L", struct.unpack("<L", data)[0])
def swap32(data):
return [struct.unpack("<I", struct.pack(">I", i))[0] for i in data]
def htonl_shift(x):
return (((x << 24) & 0xFF000000) |
((x << 8) & 0x00FF0000) |
((x >> 8) & 0x0000FF00) |
((x >> 24) & 0x000000FF))
def test(self):
data = [struct.pack('<L', i) for i in range(1000000)]
start = time.time()
for d in data:
x = htonl_slice(d)
end = time.time()
print("htonl_slice %f" % (end - start))
start = time.time()
for d in data:
x = htonl_struct(d)
end = time.time()
print("htonl_struct %f" % (end - start))
data = [i for i in range(1000000)]
start = time.time()
for d in data:
x = htonl_shift(d)
end = time.time()
print("htonl_shift %f" % (end - start))
start = time.time()
x = swap32(data)
end = time.time()
print("swap32 %f" % (end - start))
data = bytearray()
for i in range(1000000):
data += struct.pack('<L', i)
start = time.time()
x = htonl_slice_2(data)
end = time.time()
print("htonl_slice_2 %f" % (end - start))
结果是:
htonl_slice 3.041000
htonl_struct 0.626000
htonl_shift 0.864000
swap32 0.533000
htonl_slice_2 0.025000
我知道htonl_shift
可以使用int
而不是字节数组。
有趣的是,swap32
相当快,但是对于4 MB的阵列,htonl_slice_2
最快。