2017-10-03 21 views
8

Tôi có đối tượng "byte" và mặt nạ "int", tôi muốn thực hiện xor trên tất cả các byte bằng mặt nạ của mình. Tôi thực hiện hành động này nhiều lần trên các đối tượng "byte" lớn (~ 4096 KB).Python - "xor" ing từng byte theo "byte" theo cách hiệu quả nhất

Đây là mã tôi có mà không làm việc tốt, chỉ có nó là rất CPU chuyên sâu và chậm kịch bản của tôi:

# 'data' is bytes and 'mask' is int 
bmask = struct.pack('!I', mask) # converting the "int" mask to "bytes" of 4 bytes 
a = bytes(b^m for b, m in zip(data, itertools.cycle(bmask))) 

Điều tốt nhất tôi có thể đưa ra là thế này, đó là khoảng 20 lần nhanh hơn:

# 'data' is bytes and 'mask' is int 
# reversing the bytes of the mask 
bmask = struct.pack("<I", mask) 
mask = struct.unpack(">I", bmask)[0] 

# converting from bytes to array of "int"s 
arr = array.array("I", data) 

# looping over the "int"s 
for i in range(len(arr)): 
    arr[i] ^= mask 

# must return bytes 
a = bytes(arr) 

câu hỏi của tôi là:

  1. có cách nào hiệu quả hơn để làm điều này (CPU-wize)?
  2. Có cách nào "sạch hơn" để thực hiện điều này (mà không làm tổn thương hiệu suất) không?

P.S. nếu nó quan trọng, tôi đang sử dụng Python 3.5

+0

'Dữ liệu' là gì? Nó là một danh sách hoặc byte hoặc iterator hay ..? –

+0

Nếu nó là một nút cổ chai, nó có thể làm cho tinh thần để viết một hàm C được gọi là từ Python –

+0

"dữ liệu" là byte, tôi sẽ cập nhật các câu hỏi –

Trả lời

3

Tôi không nghĩ rằng bạn có thể nhanh hơn nhiều so với thuật toán của mình, sử dụng Python thuần túy. (Nhưng câu trả lời của Fabio Veronese cho thấy điều đó không đúng). Bạn có thể cạo bớt một chút thời gian bằng cách lặp vòng lặp trong danh sách, nhưng danh sách đó cần được chuyển đổi trở lại thành mảng và mảng phải được chuyển đổi thành byte, vì vậy nó sử dụng nhiều RAM hơn cho lợi ích không đáng kể .

Tuy nhiên, bạn có thể làm cho số này nhiều hơn nhanh hơn bằng cách sử dụng Numpy. Đây là một bản demo ngắn.

from time import perf_counter 
from random import randrange, seed 
import array 
import numpy as np 

seed(42) 

def timed(func): 
    ''' Timing decorator ''' 
    def wrapped(*args): 
     start = perf_counter() 
     result = func(*args) 
     stop = perf_counter() 
     print('{}: {:.6f} seconds'.format(func.__name__, stop - start)) 
     return result 
    wrapped.__name__ = func.__name__ 
    wrapped.__doc__ = func.__doc__ 
    return wrapped 

@timed 
def do_mask_arr1(data, mask): 
    arr = array.array("I", data) 
    # looping over the "int"s 
    for i in range(len(arr)): 
     arr[i] ^= mask 
    return arr.tobytes() 

@timed 
def do_mask_arr2(data, mask): 
    arr = array.array("I", data) 
    return array.array("I", [u^mask for u in arr]).tobytes() 

@timed 
def do_mask_numpy(data, mask): 
    return (np.fromstring(data, dtype=np.uint32)^mask).tobytes() 

@timed 
def make_data(datasize): 
    ''' Make some random bytes ''' 
    return bytes(randrange(256) for _ in range(datasize)) 

datasize = 100000 
mask = 0x12345678 
data = make_data(datasize) 

d1 = do_mask_arr1(data, mask) 
d2 = do_mask_arr2(data, mask) 
print(d1 == d2) 

d3 = do_mask_numpy(data, mask) 
print(d1 == d3) 

sản lượng tiêu biểu

make_data: 0.751557 seconds 
do_mask_arr1: 0.026865 seconds 
do_mask_arr2: 0.025110 seconds 
True 
do_mask_numpy: 0.000438 seconds 
True 

Tested sử dụng Python 3.6.0 trên một máy lõi đơn 32 bit 2GHz cũ chạy trên Linux.

Tôi vừa chạy với datasize = 4000000do_mask_numpy mất 0,0422 giây.

+0

Thực tế nó có thể đạt được một kết quả tốt hơn vẫn còn sử dụng Python, anyhow gumpy vẫn là nhanh nhất. –

2

Một sự thay thế trong trường hợp bạn không muốn sử dụng gọn gàng. Lợi thế xuất phát từ việc so sánh đơn, trong khi mở rộng kích thước mặt nạ thành cần thiết (tùy thuộc vào việc thu thập dữ liệu).

@timed 
def do_mask_int(data, mask): 
    intdata = int.from_bytes(data, byteorder='little', signed=False) 
    strmask = format(mask,'0x') 
    strmask = strmask * ((intdata.bit_length() + 31) // 32) 
    n = intdata^int(strmask, 16) 
    return n.to_bytes(((n.bit_length() + 7) // 8), 'little') or b'\0' 

Kết quả như nó sau:

make_data: 8.288754 seconds 
do_mask_arr1: 0.258530 seconds 
do_mask_arr2: 0.253095 seconds 
True 
do_mask_numpy: 0.010309 seconds 
True 
do_mask_int: 0.060408 seconds 
True 

Tuy nhiên các khoản tín dụng để NumPy cho là nhanh hơn, nhưng có lẽ người ta không muốn đưa nó vào trong môi trường sản xuất.

:] Tốt nhất

+0

Rất thông minh!Bạn có thể làm cho nó nhanh hơn bằng cách thay thế các bộ phận đó bằng cách thay đổi: 'a // 32' ->' a >> 5', 'a // 8' ->' a >> 3' –

+0

không thực sự là 'make_data: 8.696361 giây do_mask_arr1: 0,260604 giây do_mask_arr2: 0,207433 giây Đúng do_mask_numpy: 0,006003 giây Đúng do_mask_int: 0,050470 giây TRUE' –

Các vấn đề liên quan