Skip to content

Instantly share code, notes, and snippets.

@msoxzw
Created April 12, 2022 23:10
Show Gist options
  • Select an option

  • Save msoxzw/8ae5c488edbc2985d41563c4d9c9cc04 to your computer and use it in GitHub Desktop.

Select an option

Save msoxzw/8ae5c488edbc2985d41563c4d9c9cc04 to your computer and use it in GitHub Desktop.
asycnio udp benchmark
import argparse
import asyncio
import struct
_I = struct.Struct('!I')
class UDPClient(asyncio.DatagramProtocol):
def __init__(self, size, time):
self.loop = asyncio.get_running_loop()
self.size = size
self.time = time
def datagram_received(self, data, addr):
self.size -= len(data)
if not self.size:
self.time.set_result(self.loop.time())
class UDPServer(asyncio.DatagramProtocol):
def __init__(self, packet):
self.packet = packet
self.transport = None
def connection_made(self, transport):
self.transport = transport
def datagram_received(self, data, addr):
count, = _I.unpack(data)
for _ in range(count):
self.transport.sendto(self.packet, addr)
async def benchmark(count=65535, packet_size=1024, host='127.0.0.1', port=9999):
loop = asyncio.get_running_loop()
finish_time = loop.create_future()
size = count * packet_size
server, _ = await loop.create_datagram_endpoint(
lambda: UDPServer(bytes(packet_size)),
local_addr=(host, port),
)
client, _ = await loop.create_datagram_endpoint(
lambda: UDPClient(size, finish_time),
remote_addr=(host, port),
)
client.sendto(_I.pack(count), (host, port))
start_time = loop.time()
try:
print('Transfer speed: {:.3f} Mbps'.format(
size * 8 / 1000 / 1000 / (await finish_time - start_time)))
finally:
client.close()
server.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('count', type=int, nargs='?', default=65536)
args = parser.parse_args()
asyncio.run(benchmark(args.count))
@ichorid

ichorid commented Nov 2, 2022

Copy link
Copy Markdown

The test does not work on Ubuntu (and Debian) if trying to send over 93 packets: it gets stuck in the select loop or something like that... Am I doing something wrong?

@msoxzw

msoxzw commented Nov 2, 2022 via email

Copy link
Copy Markdown
Author

@ichorid

ichorid commented Nov 2, 2022

Copy link
Copy Markdown

I'm getting a nagging feeling that buffering does not work at all on Linux SelectorLoop with UDP πŸ€”

@ichorid

ichorid commented Nov 2, 2022

Copy link
Copy Markdown

Well, I'm just running it locally, like this:
python test.py
And then it sends packets on loopback. It does not try to connect anywhere. And I definitely use no firewall.

@ichorid

ichorid commented Nov 2, 2022

Copy link
Copy Markdown

When I stop it with the debugger, it is always stuck in EpollSelector._selector.poll(timeout, max_ev). Either this behaviour results from differences between socket usage semantics of Windows and Linux, or there is a horrible bug in SelectorLoop implementation.

@ichorid

ichorid commented Nov 2, 2022

Copy link
Copy Markdown

A quick Wireshark session shows that all the packets leave the "server side", but only 93 (in case of 1024-bytes) packets ever reach the "client side". Decreasing the size of the packet to 512 increases the number of received packets to 167. Or 4 packets of 64000 bytes. So, seems like receive buffer problem.

@msoxzw

msoxzw commented Nov 3, 2022 via email

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment