0

I have a large python script with a thread that listens to a serial port and puts new data to a queue whenever it's received. I've been trying to improve the performance of the script, as right now even when nothing is happening it's using ~ 12% of my Ryzen 3600 CPU. That seems very excessive.

Here's the serial listener thread:

def listen(self):
    """
    Main listener
    """
    while self.doListen:
        # wait for a message
        if self.bus.ser.in_waiting:
            # Read rest of message
            msg = self.bus.read(self.bus.ser.in_waiting)
            # Put into queue
            self.msgQueue.put_nowait(msg)

I profiled the script using yappi and found that the serial.in_waiting call seems to be hogging the majority of the cycles. See the KCachegrind screenshot below:

KCacheGrind Screenshot

I tried the trick suggested in this question, doing a blocking read(1) call to wait for data. However read(1) just continuously returns empty data and never actually blocks (and yes, I've made sure my pyserial timeout is set to None)

Is there a more elegant and CPU-friendly way to wait for data on the serial bus? My messages are of variable length, so doing a blocking read(n) wouldn't work. They also don't end in newlines or any specific terminators, so readline() wouldn't work either.

Patrick
  • 355
  • 2
  • 11
  • you're busy-looping on `in_waiting`. I'd suggest trying out [`pySerial-asyncio`](https://pyserial-asyncio.readthedocs.io/en/latest/) (linux & MacOS only unfortunately) – Aaron Aug 27 '21 at 00:34
  • 2
    given the speed of modern processors compared to common baudrates, it's not that bad to just put in a literal `time.sleep(.01)` which should drastically free up cpu time for other threads. – Aaron Aug 27 '21 at 00:40

1 Answers1

0

Aaron's suggestion was great. A simple time.sleep(0.01) in my serial thread dramatically cut down on CPU usage. So far it looks like I'm not missing any messages either, which was my big fear with adding in sleeps.

The new listener thread:

def listen(self):
    """
    Main listener
    """
    while self.doListen:
        # wait for a message
        if self.bus.ser.in_waiting:
            # Read rest of message
            msg = self.bus.read(self.bus.ser.in_waiting)
            # Put into queue
            self.msgQueue.put_nowait(msg)
        # give the CPU a break
        time.sleep(0.01)
Patrick
  • 355
  • 2
  • 11