在python

时间:2016-03-19 00:12:15

标签: python bytearray swap endianness

我有一些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

,我还没有尝试过

5 个答案:

答案 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,您可能希望使用dtypefrombuffer指定可选的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最快。