-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Possible performance improvement to HardwareSerial::Read #7474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Tagging @SuGlider. He has done great work on HardwareSerial and can likely help you. |
@wolfbert - Thanks for the suggestion. Indeed Arduino has only one HardwareSerial native function, which is But ESP32 Arduino is based on IDF, and IDF is a FreeRTOS environment, therefore an issue may happen when we use Tasks to read the same UART. There will be concurrency and a race condition may create a situation where a task thinks that there are X available bytes to read, then it is preempted, and then another task consumes the buffer and the scheduler returns to the previous task. All sorts of issues of this nature have to be taken into consideration when writing the code for Arduino HardwareSerial and UART HAL level code. |
By other hand, there is There are many little details. |
For most Arduino users, which is the main target of this project, improving the loop for an out of standard API function that reads a whole buffer won't really make a big difference. If the Arduino standard API needs to read more than a Byte, it will use |
Above, I just wrote some out loud thinking about the suggestion made in this issue.
Please feel free to contribute with a PR that improves the performance of the project! |
Thanks for your thoughts. I agree that Serial is a core functionality of Arduino and should not be changed without very good reason. Also the benefits may not be worth it, as likely nobody would notice anyway. I'm not so worried about the concurrency issues (though maybe I should be ...). That must be handled mostly within IDF (i.e. it must protect access to the hardware and ring buffer). The peek function should IMHO be implemented on IDF level, where code could actually look into the buffer without reading. But it can be handled, as it is now. Actually, I don't see how this can be thread safe:
If there occurs a task switch and the buffer is emptied in between, uartRead will return 0. It is also unnecessary, as Right now, I'm undecided if it's worth it to pursue this. |
Yes, that is true. I also think it is unlikely, but every HAL interface in the core assumes it may be used this way.
The Arduino Layer isn't thread safe. |
Let me think a bit more about how to add the uart_read_bytes(buffer) to the HAL layer. |
Another thing I noticed is that the timed read ( As for
|
Went down the rabbit hole ... and did a simple performance test, comparing EDIT: timings are in µs, not ms |
Replies to your comments
uart_read_bytes() can timeout and return 0 bytes.
True
True. uartPeek() and uartRead() should handle the negative value. @SuGlider
True. Caller should only call uartRead() if there are bytes available. Analysis The middle layer "esp-hal-uart" appears to add two features for a read: Discussion Currently to implement UART byte peek, esp-hal-uart.c has to read a byte and then save it until the next time uartRead() is called. IMHO dropping byte peek would simplify esp32-hal-uart.c, but I don't know who depends on that. |
Not necessarily. Serial/HardwareSerial::peek and ::read return an int (valid byte or negative error code). So do the buffered read functions. uartRead and uartPeek in between return uint8_t and therefore cannot communicate errors. If they returned int as well, error handling could be streamlined. @SuGlider
I tried to mix Arduino and IDF functions for uart, and as long as peek isn't used, it seems to work. The peek function can't be dropped, because it's in Arduinos Serial as well. It could just be moved to IDF, which would greatly simplify things. To sum up, we have a few potential areas for improvement:
Now, these are more quality topics, so if the decision is to not go forward with it, I respect that (never change a running system ;-). |
Some more thinking done. I'd suggest that
|
Maybe it's a niche problem and I'm not sure what it's caused by, but I just want to note that with the release of Arduino-esp32 2.0 and SerialPort refactoring, serial communication performance has dropped drastically. Currently, at 2,000,000 baud or faster (2Mb cp2104, 2Mb/4Mb CH9102x) there are problems with frames of approx. 900 bytes which ESP32 is not able to receive undamaged / complete (confirmed on various boards). In versions 1.x there was no problem even with 1800 byte frames. |
Please open a new issue with a good sketch that demonstrates this issue. |
@SuGlider Not sure whether your previous comment was addressed to me. Anyway, it's not a bug, so providing code would be tricky. If you take a look at uartRead/Peek/Available, you'll see it quickly. I'm happy to talk you through. |
@wolfbert - The previous comment was about the issue reported above. I've edited it to make it clear. |
I have created the PR #7525 that addresses the suggested improvement of this issue.
I think that (1), (2) and (4) are addressed with the PR. (3) can't be fixed, exactly because it is just an API, that is independent of HardwareSerial. |
This has been also done in implement this also in e072302 |
Related area
UART
Hardware specification
ESP32
Is your feature request related to a problem?
In trying to understand the inner workings of
HardwareSerial
and UART (i.e. code review), I noticed the following:uartRead
in turn calls uart_read_bytes for a single byteuart_read_bytes
could in fact read the whole buffer in one go, saving 2 calls per byte.Describe the solution you'd like
A possible solution would be to replace
uartRead
with a new functionuartReadBytes
with reversed logic. I.e. make reading a single byte the special case, instead of reading the buffer. Care would have to be taken to conditionally insert the "peek byte" at the start of the buffer. TheHardwareSerial
interface would remain unchanged, whetheruartRead
needs to be preserved is to be decided.I could draft the code, but have no means to perform extensive quality checks and tests (other than running examples on my ESP32).
Separately, and a minor thing, it seems that the checks following
uart_read_bytes
in uartRead and uartPeek are unnecessary. From what I can see, uart_read_bytes will not clobber the buffer if no bytes are returned, andc
has already been initialized.If we absolutely, positively don't trust the IDF, the check would have to be
if (len <= 0)
.uart_read_bytes
can return a (negative) error value. In any case, the caller ofuartRead
has no means of knowing whether the returned value is valid.Describe alternatives you've considered
No response
Additional context
No response
I have checked existing list of Feature requests and the Contribution Guide
The text was updated successfully, but these errors were encountered: