Skip to content

Commit dba39bd

Browse files
committed
Optimize sock_recv & sock_sendall methods.
Echo-server with 100kb messages is now 100% faster.
1 parent b310767 commit dba39bd

File tree

2 files changed

+43
-12
lines changed

2 files changed

+43
-12
lines changed

tests/test_sockets.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,20 @@ def test_socket_blocking_error(self):
9090

9191
with sock:
9292
with self.assertRaisesRegex(ValueError, 'must be non-blocking'):
93-
self.loop.sock_recv(sock, 0)
93+
self.loop.run_until_complete(
94+
self.loop.sock_recv(sock, 0))
9495

9596
with self.assertRaisesRegex(ValueError, 'must be non-blocking'):
96-
self.loop.sock_sendall(sock, b'')
97+
self.loop.run_until_complete(
98+
self.loop.sock_sendall(sock, b''))
9799

98100
with self.assertRaisesRegex(ValueError, 'must be non-blocking'):
99-
self.loop.sock_accept(sock)
101+
self.loop.run_until_complete(
102+
self.loop.sock_accept(sock))
100103

101104
with self.assertRaisesRegex(ValueError, 'must be non-blocking'):
102-
self.loop.sock_connect(sock, (b'', 0))
105+
self.loop.run_until_complete(
106+
self.loop.sock_connect(sock, (b'', 0)))
103107

104108

105109
class TestUVSockets(_TestSockets, tb.UVTestCase):

uvloop/loop.pyx

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,7 +1616,7 @@ cdef class Loop:
16161616
"""Remove a writer callback."""
16171617
self._remove_writer(fd)
16181618

1619-
def sock_recv(self, sock, n):
1619+
async def sock_recv(self, sock, n):
16201620
"""Receive data from the socket.
16211621
16221622
The return value is a bytes object representing the data received.
@@ -1627,11 +1627,21 @@ cdef class Loop:
16271627
"""
16281628
if self._debug and sock.gettimeout() != 0:
16291629
raise ValueError("the socket must be non-blocking")
1630+
1631+
try:
1632+
data = sock.recv(n)
1633+
except (BlockingIOError, InterruptedError):
1634+
pass
1635+
else:
1636+
IF DEBUG:
1637+
self._sock_try_read_total += 1
1638+
return data
1639+
16301640
fut = self._new_future()
16311641
self._sock_recv(fut, 0, sock, n)
1632-
return fut
1642+
return await fut
16331643

1634-
def sock_sendall(self, sock, data):
1644+
async def sock_sendall(self, sock, data):
16351645
"""Send data to the socket.
16361646
16371647
The socket must be connected to a remote socket. This method continues
@@ -1644,12 +1654,29 @@ cdef class Loop:
16441654
"""
16451655
if self._debug and sock.gettimeout() != 0:
16461656
raise ValueError("the socket must be non-blocking")
1647-
fut = self._new_future()
1648-
if data:
1649-
self._sock_sendall(fut, 0, sock, data)
1657+
1658+
if not data:
1659+
return
1660+
1661+
try:
1662+
n = sock.send(data)
1663+
except (BlockingIOError, InterruptedError):
1664+
pass
16501665
else:
1651-
fut.set_result(None)
1652-
return fut
1666+
IF DEBUG:
1667+
# This can be a partial success, i.e. only part
1668+
# of the data was sent
1669+
self._sock_try_write_total += 1
1670+
1671+
if n == len(data):
1672+
return
1673+
if not isinstance(data, memoryview):
1674+
data = memoryview(data)
1675+
data = data[n:]
1676+
1677+
fut = self._new_future()
1678+
self._sock_sendall(fut, 0, sock, data)
1679+
return await fut
16531680

16541681
def sock_accept(self, sock):
16551682
"""Accept a connection.

0 commit comments

Comments
 (0)