Last active
May 22, 2016 10:17
-
-
Save squeaky-pl/5b32b8495d5b9d97f468fc532901d422 to your computer and use it in GitHub Desktop.
PyPy + asyncio benchmarks from uvloop project
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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