How can I improve PySerial read speed

I realize that this is an old thread, but it has been viewed 3000 times as of this writing and I would hate for someone to be turned off of pySerial on just this encounter.

I believe the most likely culprit for the author's problem is the implicit parsing that is going on between reads:

incomingData = serialPort.readline().decode('ascii')

The readline() method is telling pyserial to parse to the next line. You are also doing a decode() in the middle of your receive cycle. All of this is occurring right in the middle of your stream.

A better approach might look like this:

waiting = port.in_waiting  # find num of bytes currently waiting in hardware
buffer += [chr(c) for c in port.read(waiting)] # read them, convert to ascii

# ...keep accumulating the buffer for as long as is reasonable...

processSerialData(buffer)  # whatever processing needs to happen, split your
                           # lines, log, or whatever else *after* you get
                           # your data

I've switched from PySerial to PyTTY, which solves my problem. Just plugging it into this code (with some small changes, like replacing serialPort.inWaiting() == 0 by serialPort.peek() == b'' for example) makes my code able to handle the datastream and not get above 50% CPU usage, which means it is at least 10x as fast. I'm still using PySerial to set the DTR lines though.

So, I guess the answer to the question is that indeed PySerial is indeed poorly optimised.


A very good solution to this can be found here.

The author states:

The code below gives me 790 kB/sec while replacing the code with pyserial's readline method gives me just 170kB/sec.

There is no statement about the baud rate set for this comparison. The value of 9600 baud in the example below is only for testing.

This solution also avoids having 100 % CPU usage.

class ReadLine:
    def __init__(self, s):
        self.buf = bytearray()
        self.s = s
    
    def readline(self):
        i = self.buf.find(b"\n")
        if i >= 0:
            r = self.buf[:i+1]
            self.buf = self.buf[i+1:]
            return r
        while True:
            i = max(1, min(2048, self.s.in_waiting))
            data = self.s.read(i)
            i = data.find(b"\n")
            if i >= 0:
                r = self.buf + data[:i+1]
                self.buf[0:] = data[i+1:]
                return r
            else:
                self.buf.extend(data)

ser = serial.Serial('COM7', 9600)
rl = ReadLine(ser)

while True:

    print(rl.readline())