2014-10-22 4 views
2

스 트리머 측은 pickle.dumps를 사용하여 절편 처리 된 튜플에 함께 시간과 함께 2048 바이트의 사운드 샘플을 계속 전송 한 다음 수신자에게 UDP 패킷을 보낸 다음 수신자에게 그것을 unpickle하고 버퍼링 한 다음 사운드 샘플을 재생합니다.피클 프로토콜 2와 3 사이의 큰 크기 (바이트 단위)

모든 것이 파이썬 3을 사용하여 훌륭했지만 수신기의 비트/초 속도가 예상됩니다.

파이썬 2.7에서 스트리머를 실행하면 속도가 빨라졌습니다! 나는 힘든 파이썬 2가 어떻게 든 더 빨랐다.

그런 다음 수신자가 수신 한 UDP 패킷을 wireshark로 확인한 결과, 필요 이상으로 커졌습니다.

트리머 측 :

while True: 
    data = next(gen) 
    print("data:{}".format(len(data))) 
    stime +=1 
    msg = (stime,data) 
    payload = pickle.dumps(msg) 
    print("payload:{}".format(len(payload))) 
    bytes_sent = s.sendto(payload,addr) 
    time.sleep(INTERVAL) 

수신기 측 : I는 정수와 2048 바이트의 튜플 pickle.dumps 경우 파이썬 3.4

while True: 
    if stop_receiving.get(): 
     break 
    try: 
     (payload,addr) = self.sock.recvfrom(32767)  
     (t,data) = pickle.loads(payload,encoding="bytes")  
     if stime >= self.frame_time.get(): 
      self.frames.put((t,data)) 
    except socket.timeout:   
     pass 

내가 얻을 피클 형태 3을 사용하여 2063 바이트.

이상하게도 Python 2.7에서 피클 포맷 2를 사용하면 5933 바이트가 거의 3 배가됩니다.

이 차이가 너무 큰 이유는 무엇입니까?

그냥 프로토콜을 만들고 그 바이트를 대신 추가해야합니까? 내가 가질 수있는 것이지만 피클을 발견 한 후에는 효과가있을 것입니다.

파이썬 워드 프로세서는 크기를 줄이기 위해 압축 라이브러리를 사용할 수도 있다고 말합니다. 그러나 여분의 시간 오버 헤드가 보상되는지는 잘 모릅니다.

감사합니다.

+0

해당 5933 바이트를 재현 할 수 없습니다. 난 임의의 값으로 시도 할 때, 나는 3000 바이트 이상을 얻었고, 병적 인 것을주기 위해서도 (파이썬 2.7이나 3.4에서) 4K 이상을 얻을 수는 없다. 어쩌면 특별한 경우 라틴 -1 인코딩 BINUNICODE 이외의 것을 사용하고 있을까요? 어딘가에 바이트를 덤프 할 수 있다면 (또는'pickletools'으로 직접 보아라.) 나는 무엇이 다른지 궁금 할 것이다. – abarnert

+0

또한 피클 포맷 2로 3.4에서 얻은 것을 테스트 했습니까? 같은 절임 형식으로 2.7에 들어있는 것과 같은가요? ('bytearray'를 사용하면, 두 언어 모두 기본적으로 같은 타입입니다. 3.4에서는'bytes', 2.7에서는'str'과 반대가됩니다.) – abarnert

+0

@abarnert 그것의 라틴 -1 인코딩 된 BINUNICODE를 사용합니다. 내 데이터는 간단한 정수 (2 바이트) [int (math.sin)에서 생성]의 샘플 스트림입니다 (http://puu.sh/cn9bB/6a28d0d469.png). 나는 각 패킷에 1024 샘플 (2048 바이트)의 덩어리를 보낸다. 2.7에서 스크립트를 실행할 때 모든'b ""바이트 리터럴을 남겨 두었습니다. 실제로 2.7에서 str으로 변환됩니다. 나는 2.7 바이트에 해당하는 3.4 바이트가'bytearray'라는 것을 알지 못했다. 3.4에서 중요한 성능 저하없이 'bytearray'를 사용할 수 있다는 의미입니까? 각면에서 호환 가능한 코드를 사용하는 것이 좋을 것입니다. –

답변

4

첫째, 일반적으로 프로토콜, 라이브러리 등의 주요 새로운 버전이 크게 개선 된 것이 놀랄만 한 것은 아닙니다. 그렇지 않으면 왜 누군가가 그들을 창조하기 위해 모든 일을 성가 시게 했습니까?

하지만 당신은 아마 구체적인 것을 찾고 있습니다.


우리가 무엇에 들어가기 전에 큰 문제는 pickletools.dumps 덤프에서하지 비교 프로토콜이 프로토콜 3, 당신이 비교하는 프로토콜 0 및 프로토콜 3.주의 마지막 줄이라는 것이다 아래 : highest protocol among opcodes = 2. 2 대신 0이 표시되면 프로토콜 0을 사용하고 있음을 의미합니다. 프로토콜 0은 인간의 가독성 (물론 최소한 pickletools과 같은 라이브러리가없는 사람의 디버깅 가능성)을 위해 설계되었지만 컴팩트하지는 않습니다. 특히 인쇄 할 수없는 ASCII 바이트를 백 슬래시 이스케이프 처리하여 대부분을 4 자로 확장합니다.

그래서 왜 2 대신 0이 나옵니까? 역 호환성 때문에 가장 높은 프로토콜이 기본값이 아니기 때문입니다. 기본값은 2.x에서는 0이고 3.x에서는 3입니다. 2.73.4에 대한 문서를 참조하십시오.

pickle.dumps(msg, protocol=pickle.HIGHEST_PROTOCOL) (또는 단지 protocol=-1)으로 코드를 변경하면 0과 3 대신 2와 4가됩니다. 2.x는 여전히 3.x보다 클 것입니다. 이유는 다음과 같습니다 , 그러나 당신이 지금보고있는 것과 동일한 척도 근처에 아무 것도 없습니다.

패리티를 원한다면 프로토콜 -2 결과가 충분히 압축되어 있으면 protocol=2을 명시 적으로 사용할 수 있습니다.

2 또는 3으로 명시 적으로 가고 싶다면 은 직접 글쓰기 방법이 없지만 protocol=min(3, pickle.HIGHEST_PROTOCOL)은 그렇게 할 것입니다.


pickletools 모듈 (상기 문서에 링크 된 소스 코드에 주석)가 쉽게 차이를 탐구 할.

은 이제 쉽게 볼 수 있도록, 짧은 문자열을 사용하자 :

>>> t = (1, string.ascii_lowercase.encode('ascii')) 
>>> p2 = pickle.dumps(t, protocol=2) 
>>> p3 = pickle.dumps(t, protocol=3) 
>>> len(p2), len(p3) 
78, 38 

그래서, 명백한 차이가 아직도있다.

이제 피클의 내용을 살펴 보겠습니다. 프로토콜이 개 상점,

>>> pickletools.dis(p2) 
    0: \x80 PROTO  2 
    2: K BININT1 1 
    4: c GLOBAL  '_codecs encode' 
    20: q BINPUT  0 
    22: X BINUNICODE 'abcdefghijklmnopqrstuvwxyz' 
    53: q BINPUT  1 
    55: X BINUNICODE 'latin1' 
    66: q BINPUT  2 
    68: \x86 TUPLE2 
    69: q BINPUT  3 
    71: R REDUCE 
    72: q BINPUT  4 
    74: \x86 TUPLE2 
    75: q BINPUT  5 
    77: . STOP 
highest protocol among opcodes = 2 

당신이 볼 수 있듯이 (당신은 아마 자신의 통역에 pickletools.dis(p2, annotate=1)을 사용하고자 하겠지만, 화면의 가장자리 오프 정보 스크롤의 대부분 때문에, 그 ... 여기 유용 아니다) bytes은 유니 코드 문자열과 코덱으로 구성됩니다. 구체적으로 프로토콜 2.


에 존재하지 않았던 새로운 연산 코드를 사용하여 bytes 대상으로

>>> pickletools.dis(p3) 
    0: \x80 PROTO  3 
    2: K BININT1 1 
    4: C SHORT_BINBYTES b'abcdefghijklmnopqrstuvwxyz' 
    32: q BINPUT  0 
    34: \x86 TUPLE2 
    35: q BINPUT  1 
    37: . STOP 
highest protocol among opcodes = 3 

...하지만 프로토콜 3 저장합니다 :

BINUNICODE 가족 opcodes는 유니 코드 문자열을 취하여 길이 접두사 UTF-8로 저장합니다.

opcode의 BINBYTES 패밀리는 바이트 문자열을 가져 와서 길이 접두사로 저장합니다. 프로토콜 1, 2 BINBYTES, bytes이로 저장하지 않아도

때문에 효과의 인수로 b.decode('latin-1')u'latin-1'의 결과 _codecs.encode를 호출.

이렇게하면 4030 바이트의 고정 오버 헤드 (내 p2p3 사이의 차이를 설명 함)가 추가됩니다. 왜 라틴어 1을 선택해야할까요? 모든 바이트를 단일 유니 코드 문자로 매핑하는 가장 간단한 코덱이기 때문입니다.

더 중요한 것은 비 ASCII 바이트는 대부분 2 바이트의 UTF-8이 될 것입니다. 랜덤 바이트의 경우 총 오버 헤드가 약 51 %입니다.

BINBYTES 꽤 유사하다 나중에 BINSTRING 프로토콜 1을 입력하고,하지만 그것은 거의 결코 유용하지 않는 기본 인코딩, 바이트를 를 저장으로 정의되어 있음을

참고. 2.x에서는 실제로 차이를 만들지 않을 것입니다. 왜냐하면 여러분은 decode 이겠지만 str을 얻지는 않을 것입니다.하지만 제 생각에 2.6+는 3.x 호환성을 위해 사용하지 않을 것입니다.

문자열에 repr ASCII 인코딩을 저장하는 프로토콜 0으로 거슬러 올라간 STRING 유형도 있습니다. 나는 이것이 프로토콜 1 이상에서 사용 된 적이 없다고 생각합니다. 물론 이것은 인쇄 할 수없는 ASCII 바이트를 2 또는 4 바이트 백 슬래시 이스케이프로 날려 버릴 수 있습니다.

+0

약 2 바이트가 3 바이트 (또는 4 바이트)가 될 때까지 혼란스러워졌습니다. 나는 범위 (10)에있는 i에 대해 다음과 같이 자기 자신을 테스트했다 : counter [i] = 0'' for the range (32767) : counter [len (_codecs.encode (struct.pack (" h ", i) .decode ("latin1 "),"latin1 "))] + = 1'그리고 놀랍게도 모든 숫자는 2 바이트만을 사용했습니다. 어쩌면 파이썬 2.7에서 다른 결과를 얻을 것이다 –

+0

@RuiBotelho : 어쩌면 내가 잘 설명하지 못했을 것이다. 유니 코드로 디코딩 된 Latin-1 바이트는 항상 1 유니 코드 문자입니다. 하지만 Unicode로 디코딩 된 후 UTF-8로 인코딩 된 Latin-1 바이트는 2 문자가 될 수 있으며 프로토콜 2가 저장하는 바이트입니다. 'struct.pack ("h", i) .decode ("latin1"). encode ("utf-8")'을 시도하십시오. – abarnert

+0

@RuiBotelho : 또는 더 간단하게 :'len (bytearray (range) (256)) decode ('latin-1') encode ('utf-8'))'은 384입니다. , len (bytearray (range (256)). decode ('latin-1') encode ('unicode-escape'))'는 대략 프로토콜 0으로 얻는 것이기 때문에 734입니다 (예를 들어, 바이트 0x0A 는 '\ n'이되고, 바이트 0x0B는 ''\ x0b ''가된다. – abarnert

관련 문제