[issue47010] Implement zero copy writes in SelectorSocketTransport in asyncio

Kumar Aditya report at bugs.python.org
Mon Mar 14 05:18:42 EDT 2022


New submission from Kumar Aditya <rahuladitya303 at gmail.com>:

Currently, _SelectorSocketTransport transport creates a copy of the data before sending which in case of large amount of data, can create multiple giga bytes copies of data before sending.

Script demonstrating current behavior:

-------------------------------------------------------------------------

import asyncio
import memory_profiler

@memory_profiler.profile
async def handle_echo(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
    data = b'x' * 1024 * 1024 * 1000 # 1000 MiB payload
    writer.write(data)
    await writer.drain()
    writer.close()

async def main():
    server = await asyncio.start_server(
        handle_echo, '127.0.0.1', 8888)

    addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
    print(f'Serving on {addrs}')

    async with server:
        asyncio.create_task(server.start_serving())
        reader, writer = await asyncio.open_connection('127.0.0.1', 8888)
        while True:
            data = await reader.read(1024 * 1024 * 100)
            if not data:
                break

asyncio.run(main())
-------------------------------------------------------------------------

Memory profile result:
------------------------------------------------------------------------
Filename: test.py

Line #    Mem usage    Increment  Occurrences   Line Contents
=============================================================
     4     17.7 MiB     17.7 MiB           1   @memory_profiler.profile
     5                                         async def handle_echo(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
     6   1017.8 MiB   1000.1 MiB           1       data = b'x' * 1024 * 1024 * 1000 # 1000 MiB payload
     7   2015.3 MiB    997.5 MiB           1       writer.write(data)
     8   2015.3 MiB   -988.1 MiB           2       await writer.drain()
     9   1027.1 MiB   -988.1 MiB           1       writer.close()

------------------------------------------------------------------------

To make it zero copy, python's buffer protocol can be used and use memory views of data to save RAM. The writelines method currently joins all the data before sending whereas it can use `socket.sendmsg` to make it more memory efficient.


Links:

- writelines - https://github.com/python/cpython/blob/2153daf0a02a598ed5df93f2f224c1ab2a2cca0d/Lib/asyncio/transports.py#L116

- socket.sendmsg - https://docs.python.org/3/library/socket.html#socket.socket.sendmsg

- memory_profiler -
https://pypi.org/project/memory-profiler/

----------
components: asyncio
messages: 415124
nosy: asvetlov, gvanrossum, kumaraditya303, yselivanov
priority: normal
severity: normal
status: open
title: Implement zero copy writes in SelectorSocketTransport in asyncio
type: resource usage
versions: Python 3.11

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue47010>
_______________________________________


More information about the Python-bugs-list mailing list