-
Notifications
You must be signed in to change notification settings - Fork 3k
Add mutiple push and pop for circular buffer #13563
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Paul, that is a useful addition. Would it be possible to preserve the doc. It has been modified for bool pop(T& data)
.
I'm not convinced we need an array overload AND a span overload, the span should be enough.
@kjbracey-arm What do you think of these additions ? I'm still not sure about the return of the pop(Span<T>&)
.
@paul-szczepanek-arm, thank you for your changes. |
Can you please comment on the line that is wrong and point out the error? The old docs had grammar mistakes and just plain incorrect text presumably based on the original implementation.
You're the one who asked for it not 10 minutes ago. What do you want it to return instead? |
* @return True if the buffer is empty, false if not | ||
* @return The span with the size set to number of elements popped using the buffer passed in as the parameter. | ||
*/ | ||
mbed::Span<T> pop(mbed::Span<T>& dest) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's usually no reason to pass spans by reference rather than value - I've seen doing so mess with code generation in practice. If you did want to pass by reference, this should be const span &
- the lack of const would be getting in the way of the automatic conversion from array to span, I think.
Return type seems reasonable to me, but ARM ABI does cause crummy code generation sometimes because of it, even with inlining. So returning just popped
might be more efficient, but a span return is probably more idiomatic. No strong feelings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's already const, I don't mind what is returned by pop either so unless either of you has opinions I'm leaving it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, it wasn't const before the span commit, must've been a change that didn't get saved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback @kjbracey-arm .
If a span is returned then I believe it can be a const span &
in input. The container in input is not altered and we return the content from the function as a result.
For compiler optimizations, I suppose one can use the non span function if it matters.
I initially asked Paul to return a span
to allow writing code like this:
uint8_t chunk[50];
auto popped_chunk = queue.pop(chunk);
for (auto element : popped_chunk) {
// do something with the element
}
// or directly
for (auto element : queue.pop(chunk)) {
// do something with the element
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'm passing it by value, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Certainly want to pass spans in by value normally. Doing it by reference isn't quite as hard to read as int * const &ptr
, but it's just as weird, and does generate worse code.
I'm fine with span return.
@paul-szczepanek-arm Thanks for the PR. We will need tests and documentation for the new functionality. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @paul-szczepanek-arm, would you be able to amend the unit tests for the circular buffer class?
https://github.com/ARMmbed/mbed-os/blob/master/platform/tests/UNITTESTS/CircularBuffer/test_CircularBuffer.cpp (if we can call these "tests" 😅)
Ah sorry @evedon hadn't seen you were already on the case :) |
af0e32b
to
f5d2d7f
Compare
Pull request has been modified.
Unit tests added, documentation in doxygen and example expanded to cover multiple push ARMmbed/mbed-os-examples-docs_only#120 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the %
isn't acceptable. Had so many slow device issues with slow division in critical places.
This would be somewhat alleviated by #13289, but it's still something to consider. (Although I note that may not be coming - hadn't checked it for a while - thought it had been merged).
|
||
CounterType incrementCounter(CounterType val) | ||
{ | ||
return (++val) % BufferSize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If BufferSize isn't a power of two this could be very inefficient on M0-type devices - using an actual software division routine - which would be bad in an interrupt context. You're better off doing a comparison. You know val can't be greater than BufferSize to start with - the compiler doesn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to force a power of two for the size?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be a backwards-incompatible API change.
} | ||
|
||
_head += written; | ||
_head %= BufferSize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as for the other %
.
} | ||
|
||
_tail += data_popped; | ||
_tail %= BufferSize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
mbed::Span<T> pop(mbed::Span<T> dest) | ||
{ | ||
CounterType popped = pop(dest.data(), dest.size()); | ||
return mbed::make_Span(dest.data(), popped); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a random note that as of current C++ version you can do return {dest.data(), popped};
.
Don't know what our style view on that is. I'm conflicted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's great to push good C++ coding standard.
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md uses return {dest.data(), popped};
so I think it's good to stick with that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a more specific reference? That's quite a big page, and can't see anything specifically discussing this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They don't discuss, they use the construct in code examples for std::pair
here -- they don't use std::make_pair
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then it's not really a specific style judgment on how explicit or not to be, which is what I'd like to see.
And there's something of a difference - a pair is a pair, so it's "just" a tuple-type container. You're effectively filling in an aggregate from a list, which is the oldest-school use of {}
.
Here, you're invoking a constructor. I always feel a bit icky when using {}
on its own to invoke a constructor. Maybe I'm too old-fashioned. At least it's the most "direct" constructor. Doing return { start_ptr, end_ptr };
to use the iterator constructor would feel weirder.
Anyway, I'm broadly in favour of the more concise style; I don't like excess clutter. I think the case for it is even stronger in function parameter context, where the span could just be one of several arguments. Whereas on return
you do usually have space to spare for that little type reminder.
(C++17 will change the situation again, because you no longer have to use make_Span
, you can have the deduction guides for the constructor letting you do just Span(...)
, so the clutter of explicitness is reduced further.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed explanations :) (your comments are always great learning material for me)
One side question (also asked here, what's the difference between mbed::Span
and std::span
? Which one should I use in my application code? and why the S
for Span
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mbed::Span
is based on an old revision of the std::span
proposal, the only difference - API wise - with the one in the (next!) standard is the type used to return the size of the span: it is signed in mbed, not signed on std. I believe we should make the switch to unsigned
at some point. Another difference is the debug capability of the iterator, it is very limited in mbed whereas libc++ returns fat iterators in debug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just seen Vincent's answer - this partly repeats it...
std::span
is C++20, so not supported by all toolchains we support, so can't use it directly. We're also in C++14 mode, and it may not be accessible in that mode.
mbed::Span
was written and added to mbed while C++20 was in draft, so has some minor differences compared to the final revision of std::span
- in particular its use of ptrdiff_t
instead of size_t
. The capital S
was to fit mbed style guidelines.
Now that C++20 has been finalised, and following other work done in this style pending full std
availability, I would suggest implementing and migrating to a new mstd::span
that directly matches std::span
alongside the other mstd
stuff in the cxxsupport directory.
The mstd
types like mstd::mutex
and mstd::atomic
are close enough matches to std
that they're drop-in replacements, possibly just needing to change 1 using
directive and 1 include. (And a mstd::
identifier can be made a straight using
map to a std::
one if available now or later in a toolchain).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you both (again) for the detailed answers :)
I was not aware of the mstd
! This is great stuff!
* | ||
* @return The number of elements popped. | ||
*/ | ||
CounterType pop(T* dest, CounterType len) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Astyle should be nagging you to make this T *dest
. Isn't it?
Thank you, @kjbracey-arm, I've changed the counter calculation to avoid % |
@@ -129,23 +131,21 @@ class CircularBuffer { | |||
CounterType written = len; | |||
|
|||
/* on first pass we write as much as we can to the right of head */ | |||
if ((_head + len) > BufferSize) { | |||
if ((_head + written) > BufferSize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the same, just changed for readability
Pull request has been modified.
return (++val) % BufferSize; | ||
val += increment; | ||
|
||
MBED_ASSERT(val <= BufferSize); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a private function so we can guarantee we will never exceed buffersize even after increment so we can simplify calculation
Pull request has been modified.
864952f
to
4ca8f99
Compare
CI started |
Typo in the commit msg for the first commit. Lets fix it after CI run. |
Or wait, we got something in the queue, faster is if I abort now. aborting the CI |
4ca8f99
to
3d827e8
Compare
fixed the typos in the commit message (I had two!) |
Pull request has been modified.
Jenkins CI Test : ❌ FAILEDBuild Number: 2 | 🔒 Jenkins CI Job | 🌐 Logs & ArtifactsCLICK for Detailed Summary
|
I restarted tests |
One general thought on the class - this extension preserves the existing "silently overwrite existing data when pushing" API, which I've yet to see a use for. Over on a customer branch I added When that gets merged, we'd want |
Jenkins CI Test : ❌ FAILEDBuild Number: 3 | 🔒 Jenkins CI Job | 🌐 Logs & ArtifactsCLICK for Detailed Summary
|
Pull request has been modified.
CI restarted |
Jenkins CI Test : ❌ FAILEDBuild Number: 4 | 🔒 Jenkins CI Job | 🌐 Logs & ArtifactsCLICK for Detailed Summary
|
I had to restart entire pipeline |
Jenkins CI Test : ✔️ SUCCESSBuild Number: 5 | 🔒 Jenkins CI Job | 🌐 Logs & ArtifactsCLICK for Detailed Summary
|
Summary of changes
Add functions to circular buffer to allow pushing and popping multiple items at a time.
Impact of changes
Migration actions required
Documentation
None
Pull request type
Test results
Reviewers
@pan-