From 984cc3a6682434d021668c25abef12e33e460e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=96=87=E8=BD=A9?= Date: Sat, 25 Jul 2026 18:07:38 +0800 Subject: [PATCH] Optimize slcan serial read and buffer processing Refactor reading from serial to improve performance by using C level's bytearray.find() to replace `for` iteration. --- can/interfaces/slcan.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 2ec4df238..ae54f04b2 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -219,21 +219,28 @@ def _read(self, timeout: float | None) -> str | None: with error_check("Could not read from serial device"): while True: - # Due to accessing `serialPortOrig.in_waiting` too often will reduce - # the performance. We read the `serialPortOrig.in_waiting` only once here. - size = self.serialPortOrig.in_waiting or 1 - self._buffer.extend(self.serialPortOrig.read(size)) - - for i, byte in enumerate(self._buffer): - if byte in (self._OK[0], self._ERROR[0]): - string = self._buffer[: i + 1].decode() - del self._buffer[: i + 1] - return string - - if _timeout.expired(): - break - - return None + size = self.serialPortOrig.in_waiting or 1 # read the `serialPortOrig.in_waiting` only once. + self._buffer.extend(self.serialPortOrig.read(size)) # read serial at once + + i1 = self._buffer.find(self._OK) # bytearray.find() or byte.find() is in C level + i2 = self._buffer.find(self._ERROR) + + if i1 >= 0 and i2 >= 0: + i = i1 if i1 < i2 else i2 # python if/else branch is faster than min()/max() + elif i1 >= 0: + i = i1 + elif i2 >= 0: + i = i2 + else: + if _timeout.expired(): + break + continue + + string = self._buffer[:i + 1].decode() + del self._buffer[:i + 1] + return string + + return None def flush(self) -> None: self._buffer.clear()