我需要对两个字节对象进行异或操作。我使用以下代码:
def bxor(b1, b2): # use xor for bytes
result = b""
for b1, b2 in zip(b1, b2):
result += bytes([b1 ^ b2])
return result
当字节对象很小的时候,它可以正常工作,但如果我对大型对象(几MB)进行异或运算,它会花费很长时间(几个小时)。我该如何使它更快?
我需要对两个字节对象进行异或操作。我使用以下代码:
def bxor(b1, b2): # use xor for bytes
result = b""
for b1, b2 in zip(b1, b2):
result += bytes([b1 ^ b2])
return result
当字节对象很小的时候,它可以正常工作,但如果我对大型对象(几MB)进行异或运算,它会花费很长时间(几个小时)。我该如何使它更快?
在另一个答案中,添加了以下内容,因为它是一个方法:
如果你想要比给出的“手动”方法更快的方式,那么总有Numpy:
import numpy
def bxor_numpy(b1, b2):
n_b1 = numpy.fromstring(b1, dtype='uint8')
n_b2 = numpy.fromstring(b2, dtype='uint8')
return (n_b1 ^ n_b2).tostring()
而且它很快:
first_random = urandom(100000)
second_random = urandom(100000)
min(Timer(partial(bxor_inplace, first_random, second_random)).repeat(10, 100))
#>>> 1.5381054869794752
min(Timer(partial(bxor_append, first_random, second_random)).repeat(10, 100))
#>>> 1.5624085619929247
min(Timer(partial(bxor_numpy, first_random, second_random)).repeat(10, 100))
#>>> 0.009930026979418471
因此,它比这里发布的最佳替代方案快150倍。
def bxor(b1, b2): # use xor for bytes
parts = []
for b1, b2 in zip(b1, b2):
parts.append(bytes([b1 ^ b2]))
return b''.join(parts)
另外,您可以使用一个可变的bytearray
,从而避免该问题。它还允许您在每次迭代时不必分配新的bytes
对象,只需附加字节/int
即可。
def bxor(b1, b2): # use xor for bytes
result = bytearray()
for b1, b2 in zip(b1, b2):
result.append(b1 ^ b2)
return result
return bytes(result)
如果你需要一个 bytes
对象。b''.join()
并不更快;在我的测试中,它实际上稍微慢一些。 - Martijn Pietersbytes.join()
在有更多字节的情况下有所改善。 - Martijn Pietersbytes(map(operator.xor, b1, b2))
的运行时间约为原始代码的一半。该代码用于按位异或两个字节对象并返回结果的字节对象。 - Eryk Sun使用 bytearray
已经快了很多:
def bxor(b1, b2):
result = bytearray(b1)
for i, b in enumerate(b2):
result[i] ^= b
return bytes(result)
timeit
比较:>>> import timeit
>>> b1, b2 = b'abcdefg' * 10, b'aaaaaaa' * 10
>>> timeit.timeit('it(b1, b2)', 'from __main__ import b1, b2, bxor as it', number=10000)
0.9230150280000089
>>> timeit.timeit('it(b1, b2)', 'from __main__ import b1, b2, bxor_ba as it', number=10000)
0.16270576599890774
bytes
对象。
b''.join()
方法由delnan提出并不比原始版本好多少:>>> timeit.timeit('it(b1, b2)', 'from __main__ import b1, b2, bxor_join as it', number=10000)
0.9936718749995634
再次运行,使用比特串大100倍的数据:
>>> b1, b2 = b'abcdefg' * 1000, b'aaaaaaa' * 1000
>>> timeit.timeit('it(b1, b2)', 'from __main__ import b1, b2, bxor as it', number=1000)
11.032563796999966
>>> timeit.timeit('it(b1, b2)', 'from __main__ import b1, b2, bxor_join as it', number=1000)
9.242204494001271
>>> timeit.timeit('it(b1, b2)', 'from __main__ import b1, b2, bxor_ba as it', number=1000)
1.762020197998936
为了展示bytes.join()
比重复连接更快。
最后一个700万字节的运行,仅使用bytearray
版本,重复10次,我对其他版本失去了耐心:
>>> b1, b2 = b'abcdefg' * 1000000, b'aaaaaaa' * 1000000
>>> timeit.timeit('it(b1, b2)', 'from __main__ import b1, b2, bxor_ba as it', number=10)
16.18445999799951
def bxor_add(b1, b2): # use xor for bytes
result = b""
for b1, b2 in zip(b1, b2):
result += bytes([b1 ^ b2])
return result
def bxor_inplace(b1, b2):
result = bytearray(b1)
for i, b in enumerate(b2):
result[i] ^= b
return bytes(result)
def bxor_join(b1, b2): # use xor for bytes
parts = []
for b1, b2 in zip(b1, b2):
parts.append(bytes([b1 ^ b2]))
return b''.join(parts)
def bxor_append(b1, b2): # use xor for bytes
result = bytearray()
for b1, b2 in zip(b1, b2):
result.append(b1 ^ b2)
return bytes(result)
#>>>
from os import urandom
from timeit import Timer
from functools import partial
first_random = urandom(200000)
second_random = urandom(200000)
Timer(partial(bxor_add, first_random, second_random)).timeit(1)
#>>> 1.3261873809969984
Timer(partial(bxor_inplace, first_random, second_random)).timeit(1)
#>>> 0.03055390200461261
Timer(partial(bxor_join, first_random, second_random)).timeit(1)
#>>> 0.15852201101370156
Timer(partial(bxor_append, first_random, second_random)).timeit(1)
#>>> 0.030534288001945242
first_random = urandom(10000000)
second_random = urandom(10000000)
Timer(partial(bxor_inplace, first_random, second_random)).timeit(1)
#>>> 1.5432947289955337
Timer(partial(bxor_join, first_random, second_random)).timeit(1)
#>>> 7.90503858300508
Timer(partial(bxor_append, first_random, second_random)).timeit(1)
#>>> 1.5145326450001448
为了清晰和速度,我建议使用append
版本。
为了澄清,我不认为append
方法比inplace
版本更快;我只是认为它更加简单明了一点。
尽管如此,因为有人提出了这个要求:
first_random = urandom(100000)
second_random = urandom(100000)
min(Timer(partial(bxor_inplace, first_random, second_random)).repeat(10, 100))
#>>> 1.5381054869794752
min(Timer(partial(bxor_append, first_random, second_random)).repeat(10, 100))
#>>> 1.5196998479950707
.timeit(1)
比玩弄time.time
要好一点,你需要更多的迭代来消除噪音。 - user395760timeit(N)
不会做任何事情;你需要使用 .repeat
。 - Veedracappend
版本以获得更快的速度”)。是的,你需要使用repeat()
。 - user395760bytearray.append()
在进行 10 次,每次处理 700 万字节的情况下是完全可比的;只要使用 bytearray
,你选择哪个并不完全重要。 - Martijn Pieters