2

파이썬 네트워킹 이 느림입니다.파이썬 네트워킹 속도를 높이려면 어떻게해야합니까?

나는 (C로 쓰여진) 서버가있다. 내 클라이언트 (파이썬)와 함께 그것을 테스트했다. 2MB/s에 도달 할 수 있습니다. nc -l 0.0.0.0 9999 | pv > /dev/null

내가 1백20메가바이트/S (1GB)의 주위에 뭔가에 도달 :

host1의 (클라이언트) : cat some_big_file | nc host2 9999

호스트 2 (서버) 그것은 나를 내가이 체크 걱정.

서버가 병목 현상이 아니므로 프로덕션에서 사용하며 더 많은 것을 처리 할 수 ​​있습니다. 그러나 나는 확실히 파이썬 gevent 서버를 테스트 용으로 복사했다. 그것은 다음과 같습니다

#!/usr/bin/env python 
    from gevent.server import StreamServer 
    from gevent.pool import Pool 

    def handle(socket, address): 
     while True: 
      print socket.recv(1024) 

    pool = Pool(20000) 
    server = StreamServer(('0.0.0.0', 9999), handle, spawn=pool) 
    server.serve_forever() 

다음 조치는 nc (host1)에서 gserver (host2)에 보내는 것입니다.

이 host1 : cat some_big_file | nc host2 9999 호스트 2 : ./gserver.py | pv > /dev/null

host2에 출력 : [ 101MB/s]. 나쁘지 않다.

그러나 여전히 파이썬 클라이언트를 사용할 때 느립니다. 나는 클라이언트를 gevent으로 바꿨다. 나는 몇 개의 그린렛을 시도했다. 1, 10, 100, 1000 - 그다지 도움이되지 못했지만 하나의 파이썬 프로세스로 20MB/s에 도달하거나 2, 3, 4, 5 개의 개별 파이썬 프로세스에 대해 ~30MB/s에 도달 할 수있었습니다. 여전히 느립니다. 나는 다음과 같이 클라이언트가 바보로 다시 작성했습니다

#!/usr/bin/env python 
import sys 
import socket 

c = socket.create_connection((sys.argv[1], sys.argv[2])) 
while 1: 
     c.send('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n') 

을 내가 10MB/s에 도달 할 수이 방법. 나는 또한 큰 전체 2GB 파일을 메모리로 읽어서 비슷한 결과를 보내 접근법을 시도했다.

또한 파이썬 스크립트를 별도의 프로세스 (tmux 사용)로 실행하려고했습니다. 1 프로세스를 사용하면 10MB/s, 2 프로세스 20MB/s, 3 prcesses 23MB/s, 4, 5, 6 프로세스는 아무것도 변경하지 않았습니다 (gevent 버전 및 간단한 것으로 테스트 됨).

세부 : 파이썬 2.7.3 데비안 7 - 표준 설치가 기계는 AWS의 인스턴스, 클라이언트는 c1.medium이며, 서버는 c3.xlarge입니다. nc와 iperf는 기계간에 1Gb/s를 측정했습니다.

질문 :

  1. 이유는 파이썬 서버 (gevent 서버) 만 사용하여 데이터를 신속의 많은 같은 속도도 C 프로그램이 할 수있는 경우로 보낼 수 없습니다 를받을 수 있습니다.
  2. 왜 프로세스를 두 배로 늘리면 전송 속도가 한도로 증가하지 않고 어느 정도만 증가합니다.
  3. 소켓을 사용하여 Python으로 데이터를 빠르게 보낼 수있는 방법이 있습니까?
+1

''나는 느린 파이썬 네트워킹을 찾고있다. 아니요, * 응용 프로그램 * (파이썬으로 작성되는 경우)이 느립니다. –

+1

@JonathonReinhart 예, 당신 말이 맞아요.하지만 그 바로 가기가 다른 접근법을 시도했습니다. – spinus

답변

6

문제는 네트워킹이 느리다는 것이 아닙니다. 파이썬 함수 호출은 많은 오버 헤드가 있습니다. connection.send을 자주 호출하면 함수 호출에 낭비되는 CPU 시간이 많이 걸릴 것입니다.

내 컴퓨터의 프로그램 평균은 약 35MB/s입니다. 간단한 수정을하면 450MB/s가됩니다.

#... 
c.send('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'*10+'\n') 

더 많은 데이터를 한 번에 보내면 1GB/s 이상의 속도에 도달 할 수 있습니다.

처리량을 최대화하려면 가능한 한 많은 데이터를 send으로 한 번에 보내야합니다. 이를 수행하는 간단한 방법은 최종 결과를 보내기 전에 여러 문자열을 연결하는 것입니다. 이렇게하면 파이썬 문자열이 변하지 않으므로 연속 문자열 연결 (큰 문자열 포함)이 느립니다. 대신 bytearray을 사용하는 것이 좋습니다.

+0

C (또는 심지어 어셈블리)에서 한 번에 몇 바이트의 물건을 보내려고해도 * syscall * 오버 헤드가 데이터를 커널에 전달할 수있는 속도를 심각하게 제한하기 때문에 여전히 느립니다. – ArtemB

+0

@goncalopp, 좋은 설명과 bytearray 팁 주셔서 감사합니다. – spinus

+0

또 다른 포인트는'Buffer Size'이고, RTL8168은 8/16KB 온칩 메모리 (L1, L2, L3 캐시처럼)를 가지고 있으며,'Ethernet chips Internal Buff Size'를 통해 패킷을 전송하지 않습니다. DMA를 사용하는 이더넷 칩은 전체 내장 메모리를 차지하지 않습니다. 내 의견 max size는'1600 byte'이고,'Data Interrupt'는 어렵다. 통신 데이터 패턴을 세 심하게 설정할 필요가있다. 나는 40microsecond (send + receive) packet time에 도달했고 2 converter (FIBER to CAT6A)를 사용했다. 모든 칩이 더 많은 ns (나노초) 셋업 시간을 갖기 때문에 버퍼 크기가 중요합니다. 아마도 작은 패킷은 빠르지 만, 여러분의 칩은 send + receive에서 더 많은 대기 시간을 갖습니다. 계속 ... >> – dsgdfg

관련 문제