From c21452a1d23a7cf70fdd6b2c069d460c1a5a7be8 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Tue, 21 Apr 2020 13:18:55 -0700 Subject: [PATCH] extmod/uasyncio: Attempt to write immediately in Stream.write method. The main aim of this change is to reduce the number of heap allocations when writing data to a stream. This is done in two ways: 1. Eliminate appending of data when .write() is called multiple times before calling .drain(). With this commit, the data is written out immediately if the underlying stream is not blocked, so there is no accumulation of the data in a temporary buffer. 2. Eliminate copying of non-bytes objects passed to .write(). Prior to this commit, passing a bytearray or memoryview to .write() would always result in a copy of it being made and turned into a bytes object. That won't happen now if the underlying stream is not blocked. Also, this change makes .write () more closely implement the CPython documented semantics: "The method attempts to write the data to the underlying socket immediately. If that fails, the data is queued in an internal write buffer until it can be sent." --- extmod/uasyncio/stream.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/extmod/uasyncio/stream.py b/extmod/uasyncio/stream.py index af3b8feab3..750d8e9740 100644 --- a/extmod/uasyncio/stream.py +++ b/extmod/uasyncio/stream.py @@ -56,9 +56,19 @@ class Stream: return l def write(self, buf): + if not self.out_buf: + # Try to write immediately to the underlying stream. + ret = self.s.write(buf) + if ret == len(buf): + return + if ret is not None: + buf = buf[ret:] self.out_buf += buf async def drain(self): + if not self.out_buf: + # Drain must always yield, so a tight loop of write+drain can't block the scheduler. + return await core.sleep_ms(0) mv = memoryview(self.out_buf) off = 0 while off < len(mv):