SerialUSB1 faster than Stream*

ithinu

Active member
Hi,

Why using SerialUSB1 directly with available(), readBytes(), and write() gives three times faster transmission (12 Mbytes vs 4 Mbytes) compared to Stream* s_port = &SerialUSB1 and then calling s_port->available() and so on? Apart from waiting in a tight loop for SerialUSB1.available(), maybe only three or four such calls are used per 4kByte of data read.
 
I wonder if it’s because the function calls need to go through virtual lookup each time, for Serial*?
 
To test it, after each SerialUSB1.method() (version with no pointers) I added a serial->available(); statement and the effect was almost none, still 12 MBytes.
 
IIRC there were issues at one point with optional interface methods either not being implemented or not matching the correct signatures, so sending/receiving blocks of data caused a fallback to the default Stream methods that read/write one byte at a time.
 
Could indeed be a deficiency in the core library. Could also be an artifact of how the code or test works. Or maybe both.

Can you share a test case to reproduce the problem?
 
I am rewriting the source and it does not work now. I will post it later.
 
Last edited:
Note: The Print Class (which Stream ... derives from),
has the method:
Code:
virtual size_t write(const uint8_t *buffer, size_t size);

I don't believe that the class:
Code:
class usb_serial2_class : public Stream
...
extern usb_serial2_class SerialUSB1;
Implements that version of write, so it will simply use the default print version:

Code:
size_t Print::write(const uint8_t *buffer, size_t size)
{
    if (buffer == nullptr) return 0;
    size_t count = 0;
    while (size--) count += write(*buffer++);
    return count;
}

I don't believe any of the readBytes methods are virtual, so you would always get the Stream class version.

So I am guessing something else is going on. Like maybe calling readBytes() with more than is available at the time,
so the code will wait for some time (setTimeout)...
 
I attach both versions. With host.c on a PC:
device_serial: 1150 Hz
device_stream: 460 Hz
 

Attachments

  • device_serial.ino
    2 KB · Views: 6
  • device_stream.ino
    2 KB · Views: 6
  • host.c
    4.5 KB · Views: 7
Note: The Print Class (which Stream ... derives from),
has the method:
Code:
virtual size_t write(const uint8_t *buffer, size_t size);

I don't believe that the class:
Code:
class usb_serial2_class : public Stream
...
extern usb_serial2_class SerialUSB1;
Implements that version of write, so it will simply use the default print version:
The usb_serial_classX classes definitely have their own override. It's right there in the usb_serial.h header:
Code:
virtual size_t write(const uint8_t *buffer, size_t size) { return usb_serial_write(buffer, size); }

So that's why using the serial class directly is faster; using the Stream interface only writes one byte at a time instead of the whole buffer.
write(uint8_t c) for usb_serial_class is implemented as write(&c, 1);

But regardless of which interface is used... the same functions should be used. So something funny is going on, either a function signature doesn't match or a bad optimization is being made by the compiler.
 
Back
Top