Problem with writing fast UDP server
Jean-Paul Calderone
exarkun at divmod.com
Thu Nov 20 22:52:24 EST 2008
On Fri, 21 Nov 2008 00:20:49 -0200, Gabriel Genellina <gagsl-py2 at yahoo.com.ar> wrote:
>En Thu, 20 Nov 2008 14:24:20 -0200, Krzysztof Retel
><Krzysztof.Retel at googlemail.com> escribió:
>>On Nov 20, 4:00 pm, bieff... at gmail.com wrote:
>>>On 20 Nov, 16:03, Krzysztof Retel <Krzysztof.Re... at googlemail.com>
>>>wrote:
>>>
>>> > I am struggling writing fast UDP server. It has to handle around 10000
>>> > UDP packets per second. I started building that with non blocking
>>> > socket and threads. Unfortunately my approach does not work at all.
>>> > I wrote a simple case test: client and server. The client sends 2200
>>> > packets within 0.137447118759 secs. The tcpdump received 2189 packets,
>>> > which is not bad at all.
>>> > But the server only handles 700 -- 870 packets, when it is non-
>>> > blocking, and only 670 – 700 received with blocking sockets.
>>> > The client and the server are working within the same local network
>>> > and tcpdump shows pretty correct amount of packets received.
>>
>>I wonder if there is a kind of setting for socket to allow no delays?
>
>I've used this script to test sending UDP packets. I've not seen any
>delays.
>
><code>
> [snip]
></code>
>
>Start the server before the client.
>
If you want to try this program out on POSIX, make sure you change the
time.clock() calls to time.time() calls instead, otherwise the results
aren't very meaningful.
I gave this a try on an AMD64 3200+ running a 32 bit Linux installation.
Here's the results I got on the server:
Packet count 91426
Total bytes 8228340 bytes
Total time 8.4 secs
Avg size / packet 90 bytes
Max size / packet 90 bytes
Max time / packet 41070.9 us
Min time / packet 79.9 us
Avg time / packet 92.3 us
Max speed 1100.4 Kbytes/sec
Min speed 2.1 Kbytes/sec
Avg speed 952.0 Kbytes/sec
And on the client:
Packet count 91426
Total bytes 8228340 bytes
Total time 8.4 secs
Avg size / packet 90 bytes
Max size / packet 90 bytes
Max time / packet 40936.0 us
Min time / packet 78.9 us
Avg time / packet 92.3 us
Max speed 1113.7 Kbytes/sec
Min speed 2.1 Kbytes/sec
Avg speed 952.1 Kbytes/sec
Both processes ran on the same machine and communicated over localhost.
For comparison, I tried running the client against a Twisted-based UDP
server. Here are the results from that server:
Packet count 91426
Total bytes 8228340 bytes
Total time 11.8 secs
Avg size / packet 90 bytes
Max size / packet 90 bytes
Max time / packet 55393.9 us
Min time / packet 8.8 us
Avg time / packet 128.7 us
Max speed 9963.2 Kbytes/sec
Min speed 1.6 Kbytes/sec
Avg speed 682.7 Kbytes/sec
This seemed a bit low to me though, so I tried writing an alternate client
and re-ran the measurement. Here are the new server results:
Packet count 91426
Total bytes 8228340 bytes
Total time 2.9 secs
Avg size / packet 90 bytes
Max size / packet 90 bytes
Max time / packet 38193.0 us
Min time / packet 8.8 us
Avg time / packet 32.2 us
Max speed 9963.2 Kbytes/sec
Min speed 2.3 Kbytes/sec
Avg speed 2726.7 Kbytes/sec
And then tried the new client against the original server, with these
results:
Packet count 91426
Total bytes 8228340 bytes
Total time 3.8 secs
Avg size / packet 90 bytes
Max size / packet 90 bytes
Max time / packet 23675.0 us
Min time / packet 6.9 us
Avg time / packet 41.7 us
Max speed 12711.7 Kbytes/sec
Min speed 3.7 Kbytes/sec
Avg speed 2109.0 Kbytes/sec
So it does seem that handling 10k datagrams per second should be no
problem, assuming comparable hardware, at least if whatever work you
have to do to process each one doesn't take more than about 24 25ths
of a millisecond (leaving you the remaining 1 part out of 25 of every
millisecond to receive a packet).
For reference, here's the Twisted UDP client code:
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
from twisted.internet.task import LoopingCall
msg = 'xyxabc123' * 10
class EchoClientDatagramProtocol(DatagramProtocol):
def startProtocol(self):
self.transport.connect('127.0.0.1', 8000)
LoopingCall(self.sendDatagrams).start(0.00005)
def sendDatagrams(self):
for i in xrange(50):
self.transport.write(msg)
def main():
protocol = EchoClientDatagramProtocol()
t = reactor.listenUDP(0, protocol)
reactor.run()
if __name__ == '__main__':
main()
And here's the Twisted UDP server code:
from time import time as clock
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
class EchoUDP(DatagramProtocol):
history = []
t0 = clock()
def datagramReceived(self, datagram, address):
t1 = clock()
self.history.append((len(datagram), t1 - self.t0))
self.t0 = t1
if len(self.history) == 91427:
self.history.pop(0)
show_stats(self.history)
self.history = []
def main():
reactor.listenUDP(8000, EchoUDP())
reactor.run()
def show_stats(history):
npackets = len(history)
bytes_total = sum([item[0] for item in history])
bytes_avg = float(bytes_total) / npackets
bytes_max = max([item[0] for item in history])
time_total = sum([item[1] for item in history])
time_max = max([item[1] for item in history])
time_min = min([item[1] for item in history])
time_avg = float(time_total) / npackets
speed_max = max([item[0]/item[1] for item in history if item[1]>0])
speed_min = min([item[0]/item[1] for item in history if item[1]>0])
speed_avg = float(bytes_total) / time_total
print "Packet count %8d" % npackets
print "Total bytes %8d bytes" % bytes_total
print "Total time %8.1f secs" % time_total
print "Avg size / packet %8d bytes" % bytes_avg
print "Max size / packet %8d bytes" % bytes_max
print "Max time / packet %8.1f us" % (time_max*1e6)
print "Min time / packet %8.1f us" % (time_min*1e6)
print "Avg time / packet %8.1f us" % (time_avg*1e6)
print "Max speed %8.1f Kbytes/sec" % (speed_max/1024)
print "Min speed %8.1f Kbytes/sec" % (speed_min/1024)
print "Avg speed %8.1f Kbytes/sec" % (speed_avg/1024)
print
if __name__ == '__main__':
main()
More information about the Python-list
mailing list