Skip to content

Instantly share code, notes, and snippets.

@squeaky-pl
Last active May 22, 2016 10:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save squeaky-pl/5b32b8495d5b9d97f468fc532901d422 to your computer and use it in GitHub Desktop.
Save squeaky-pl/5b32b8495d5b9d97f468fc532901d422 to your computer and use it in GitHub Desktop.
PyPy + asyncio benchmarks from uvloop project
# Copied with minimal modifications from curio
# https://github.com/dabeaz/curio
from concurrent.futures import ProcessPoolExecutor
import argparse
from socket import *
import time
import sys
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--msize', default=1000, type=int,
help='message size in bytes')
parser.add_argument('--num', default=200000, type=int,
help='number of messages')
parser.add_argument('--times', default=1, type=int,
help='number of times to run the test')
parser.add_argument('--workers', default=3, type=int,
help='number of workers')
parser.add_argument('--addr', default='127.0.0.1:25000', type=str,
help='number of workers')
args = parser.parse_args()
unix = False
if args.addr.startswith('file:'):
unix = True
addr = args.addr[5:]
else:
addr = args.addr.split(':')
addr[1] = int(addr[1])
addr = tuple(addr)
print('will connect to: {}'.format(addr))
MSGSIZE = args.msize
msg = b'x'*MSGSIZE
def run_test(n):
print('Sending', NMESSAGES, 'messages')
if unix:
sock = socket(AF_UNIX, SOCK_STREAM)
else:
sock = socket(AF_INET, SOCK_STREAM)
sock.connect(addr)
while n > 0:
sock.sendall(msg)
nrecv = 0
while nrecv < MSGSIZE:
resp = sock.recv(MSGSIZE)
if not resp:
raise SystemExit()
nrecv += len(resp)
n -= 1
TIMES = args.times
N = args.workers
NMESSAGES = args.num
start = time.time()
for _ in range(TIMES):
with ProcessPoolExecutor(max_workers=N) as e:
for _ in range(N):
e.submit(run_test, NMESSAGES)
end = time.time()
duration = end-start
print(NMESSAGES*N*TIMES,'in', duration)
print(NMESSAGES*N*TIMES/duration, 'requests/sec')
http://magic.io/blog/uvloop-blazing-fast-python-networking/
I edited the benchmarks from https://github.com/MagicStack/uvloop/tree/master/examples/bench
to not use `async` and `await` keywords
1. Get the latest nightly of pypy3
2. If you are running a system that is not exactly like Ubuntu 14.04 LTS
* `ldd bin/pypy3` to lookup broken symbols and libraries,
* you can get openssl from http://packages.ubuntu.com/trusty/amd64/libssl1.0.0/download,
unpack it and drop libssl.so.1.0.0 and libcrypto.so.1.0.0 into `bin` where `pypy3` binary resides
* everything else works with symlinking in your `/usr/lib64`
3. Get the develop branch of virtualenv https://github.com/pypa/virtualenv/archive/develop.zip and unzip it
4. Given your pypy3 tarball was unpacked to `pypy-c-jit-84568-d463dd98e6a6-linux64`:
```
LD_LIBRARY_PATH=pypy-c-jit-84568-d463dd98e6a6-linux64/bin virtualenv-develop/virtualenv.py -p pypy-c-jit-84568-d463dd98e6a6-linux64/bin/pypy3 venv-pypy3
```
5. Fix virtualenv by coping `libssl.so.1.0.0` and `libcrypto.so.1.0.0` into `venv-pypy3/bin`
if you had problems with openssl
6. Activate virtualenv and `pip install asyncio`
6a. Fix missing `time.get_clock_info` in PyPy by hardcoding your monotonic clock resolution
in `asyncio/base_events.py`, I got my resolution from CPython 3.5
Substitute
```
self._clock_resolution = time.get_clock_info('monotonic').resolution
```
with
```
self._clock_resolution = 1e-09
```
7. Run the attached server.py
```
python server.py --print --streams
```
For asyncio streams benchmark
```
python server.py --print
```
For asyncio "plain" benchmark
8. Run the client
```
python client.py --num 10000 --workers 5
```
9. Play with other options and see your results
import argparse
import asyncio
import gc
# import uvloop
import os.path
import socket as socket_module
from socket import *
PRINT = 0
@asyncio.coroutine
def echo_server(loop, address, unix):
if unix:
sock = socket(AF_UNIX, SOCK_STREAM)
else:
sock = socket(AF_INET, SOCK_STREAM)
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sock.bind(address)
sock.listen(5)
sock.setblocking(False)
if PRINT:
print('Server listening at', address)
with sock:
while True:
client, addr = yield from loop.sock_accept(sock)
if PRINT:
print('Connection from', addr)
loop.create_task(echo_client(loop, client))
@asyncio.coroutine
def echo_client(loop, client):
try:
client.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
except (OSError, NameError):
pass
with client:
while True:
data = yield from loop.sock_recv(client, 1000000)
if not data:
break
yield from loop.sock_sendall(client, data)
if PRINT:
print('Connection closed')
@asyncio.coroutine
def echo_client_streams(reader, writer):
sock = writer.get_extra_info('socket')
try:
sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
except (OSError, NameError):
pass
if PRINT:
print('Connection from', sock.getpeername())
while True:
data = yield from reader.read(1000000)
if not data:
break
writer.write(data)
if PRINT:
print('Connection closed')
writer.close()
class EchoProtocol(asyncio.Protocol):
def connection_made(self, transport):
self.transport = transport
def connection_lost(self, exc):
self.transport = None
def data_received(self, data):
self.transport.write(data)
@asyncio.coroutine
def print_debug(loop):
while True:
print(chr(27) + "[2J") # clear screen
loop.print_debug_info()
yield from asyncio.sleep(0.5, loop=loop)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--uvloop', default=False, action='store_true')
parser.add_argument('--streams', default=False, action='store_true')
parser.add_argument('--proto', default=False, action='store_true')
parser.add_argument('--addr', default='127.0.0.1:25000', type=str)
parser.add_argument('--print', default=False, action='store_true')
args = parser.parse_args()
if args.uvloop:
loop = uvloop.new_event_loop()
print('using UVLoop')
else:
loop = asyncio.new_event_loop()
print('using asyncio loop')
asyncio.set_event_loop(loop)
loop.set_debug(False)
if args.print:
PRINT = 1
if hasattr(loop, 'print_debug_info'):
loop.create_task(print_debug(loop))
PRINT = 0
unix = False
if args.addr.startswith('file:'):
unix = True
addr = args.addr[5:]
if os.path.exists(addr):
os.remove(addr)
else:
addr = args.addr.split(':')
addr[1] = int(addr[1])
addr = tuple(addr)
print('serving on: {}'.format(addr))
if args.streams:
if args.proto:
print('cannot use --stream and --proto simultaneously')
exit(1)
print('using asyncio/streams')
if unix:
coro = asyncio.start_unix_server(echo_client_streams,
addr, loop=loop)
else:
coro = asyncio.start_server(echo_client_streams,
*addr, loop=loop)
srv = loop.run_until_complete(coro)
elif args.proto:
if args.streams:
print('cannot use --stream and --proto simultaneously')
exit(1)
print('using simple protocol')
if unix:
coro = loop.create_unix_server(EchoProtocol, addr)
else:
coro = loop.create_server(EchoProtocol, *addr)
srv = loop.run_until_complete(coro)
else:
print('using sock_recv/sock_sendall')
loop.create_task(echo_server(loop, addr, unix))
try:
loop.run_forever()
finally:
if hasattr(loop, 'print_debug_info'):
gc.collect()
print(chr(27) + "[2J")
loop.print_debug_info()
loop.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment