-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
code robustness improvements, minor speedup, fx sliders bugfix #4463
Conversation
* make XY() and _setPixelColorXY_raw() const (minor speedup) * segment is a struct not a class: friend class Segment --> friend struct Segment * fix missing braces around two macros * use non-throwing "new" where possible * improve robustness of transition code
these are some great findings! |
... so we must use `int` instead of `unsigned`
these are reserved names and future compilers may reject them.
* changed some parameters to "pointer to const", so compiler can better optimize code size and performance - because data behind a const pointer will never be modified by the called function. * made setPixelColor `const` * fixed a few potentially uninitialized local vars (the may have random values if not initialized) * avoid shadowing "state" in handleSerial() * plus a few very minor improvements
I've gone through more recommendations from sonarcube - fixed a bug in the function that extracts sliders from effect meta-data, added some minor speedups, and corrected a few corner cases.
@DedeHai @willmmiles sorry the last changes may make it somewhat complicated for you to review - you might want to go commit-by-commit to have some more context. Everything compiles without new warnings, so I assume that the Edit: This PR is "final" now, meaning there are no additional tool findings I want to address. |
0.15.1 or 0.16? |
0.15.1 as it's not changing functionality, but fixes two bugs and improves code quality. |
I compiled this PR for ESP32 and for C3: Does not look like the compiler does a better optimization. Or is this due to the compiler doing more inlining or is there a good explanation for the extra code size? If its inlining of core functions that actually help to speed up the rendering, then I am ok with it, if it's just for "nicer code" I don't see the point. |
@DedeHai actually that's a bit unexpected 🤔 I'll check it commit-by-commit. |
@DedeHai thanks again for raising the question metrics, metrics, metrics ... So i've done some steps-by-step comparison to find out where the increase comes from: The results are supporting my first guess - the main increase is from introducing Now, what to do? Question for all maintainers: What do you think? |
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.
Good work.
so the issue is that edit: it is a bit of a pain having to weigh every byte of code... I really would love to see that boot-fallback partitioning, freeing up most of the OTA partition, giving us the liberty to add more features without dropping current ones. Looking at features waiting in the line like the pixel-magic tool, effect transitions, updated AR and the particle FX the problem will only get worse, not even speaking of updating to newer IDF. While I managed to free about 15k of flash by optimizing code, the particle system will eat all that plus another 15k on top. |
Unless we think the increase in size is going to tip a significant number of users over 100% then I'm ok with adding this to 0.15.1 if that's the consensus, but my personal preference would be for the use of new to be in main only As @DedeHai mentions, we really need to sort size issues before 0.16 release, but we have a long time period for this |
@DedeHai it's even more crazy:
As we don't catch exceptions inside WLED, any exception is leading to a crash (=default exception handler). To prevent this, a try - catch block is needed. It can be on a very hight level (like arduino #if __cpp_exceptions
try{
myObject = new object;
} catch(...) {
myObject = nullptr; // low heap memory -> handle error
DEBUG_PRINTF("out of memory\n");
}
#else // the compilation does not support exceptions, i.e. gcc -fno-exceptions
myObject = new object;
#endif
PS: i fully support your comment about Tasmota-style boot-fallback partitioning - this would give us enough flash space for years, plus we could have speed optimized builds (gcc -O2) instead of optimizing for size (gcc -Os). |
@softhack007 Edit: |
On purely theoretical level it is rather simple: create 2 app partitions (as we already have, but of unequal size), load OTA loader on one (smaller) and firmware on another (larger). When requesting OTA update, switch active partition and reboot device. If OTA is canceled it needs to switch active partition, otherwise proceed as normal. |
wled00/FX_2Dfcn.cpp
Outdated
@@ -276,8 +276,8 @@ void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) { | |||
if (!isActive()) return; // not active | |||
const unsigned cols = vWidth(); | |||
const unsigned rows = vHeight(); | |||
uint32_t lastnew; | |||
uint32_t last; | |||
uint32_t lastnew = BLACK; |
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.
These are actually not used when unintialised. if (x>0)
takes care of that. Similar in FX_fcn.cpp
I've checked all those EDIT: BusConfig is exception as well. |
On a side note, I've started the same process this morning unaware of this PR. 😄 |
good finding 👍 Normally there is a "general word of caution" to always use the matching deallocation method.
@blazoncek did you check this aspect, too? |
Yes, if you break it down like that its simple, but it gets complicated real quick. I wrote bootloaders before, its not rocket science but here it is a bit more involved:
I think this would need someone to take the initiative, create a new branch and do a proof of concept, maybe based on some arduino/IDF example, Not sure how to keep it small though: even my code for the ESPNow remote (Arduino IDE based) already uses over 500k of flash for just basic wifi libraries, so maybe the bootloader should be IDF based? |
Yes. AFAIK unless you instantiate a non-POD, EDIT: code reduction is about 100 bytes. Retaining std::nothrow where appropriate. EDIT2: malloc seems to be working just fine. |
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.
Good work! All positive steps.
Re allocation: new(std::nothrow)
is a positive step, even though yes, it'll make the compiler actually implement the null checks that it could leave out with new
. Better still would be moving to managed allocation, eg. std::unique_ptr<>
or std::vector<>
, but one step at a time. :)
I have some more tuning made, ready to push if you allow it @softhack007
|
@blazoncek thanks, I love too see how we get (somewhat) exited about code cleanup and improving code quality 😃 Maybe let me first add a commit (later today) to react on feedback, and tomorrow you can push your improvements to my PR? I'm just thinking about keeping things clearer for our poor code reviewers. |
I've implemented most of it already. |
Just give me a chance to finish my part - I may be slower but still good at doing it😉 |
* simplified transition bugfix * removed cast same type * isIp parameter changed to pass-by-reference, to avoid copy constructor
@blazoncek I've completed my part, ready to see your improvements 😃 please add them to this PR. |
@blazoncek something else you might be able to check: Trace (see the red numbers in the code) What do you think? |
- replaced POD new/delete with malloc/free - some more SEGLEN <= 1 - some gnu::pure - more const attributes - some static attributes
"const" was missing in the function implementation
This is purely a "clean code" thing, no impact on function - it helps to avoid confusion when reading the code. C++ allows declaration and implementation to use different variable names.
…into code_robustness
This line compares if current |
Question: What is better from a robustness POV, |
Well its almost the same, and might be a matter of personal preference ( Edit: Pointer parameters actually make it a bit easier to make a mistake like |
That was exactly what I thought. So if you want to modify parameter it is better to use pass by reference than pass by pointer (even though they are technically the same). I am thinking about functions in util.cpp that use pointers when in fact should be using reference. |
I see we have a few approvals, but not yet merged. What is outstanding or are we good to merge (and cherry pick to the release branch) ? |
IMO good to merge. |
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 would recommend to fix the speed bumps I mentioned. otherwise good to merge.
* removed unneeded initializations in blur() and blur2D() * remove check for _t in progress() * code readability: if (_t) --> if(isInTransition()) * add `isInTransition()` checks to currentBri() and currentMode() * added missing `_transitionprogress = 0xFFFFU` in stopTransition()
@@ -411,18 +412,18 @@ void Segment::beginDraw() { | |||
_vHeight = virtualHeight(); | |||
_vLength = virtualLength(); | |||
_segBri = currentBri(); | |||
unsigned prog = isInTransition() ? progress() : 0xFFFFU; // transition progress; 0xFFFFU = no transition active |
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.
the check in this line is redundant: handleTransition()
is called before beginDraw()
which will update progress() to 0xFFFF if isInTransition()
is false.
one exception: when streaming RGB data, not sure if progress is possible to be set in that 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.
since this is only called once per segment per frame, its ok to leave it like this, it may improve readability.
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.
exception: when streaming RGB data, not sure if progress is possible to be set in that case.
yeah, streaming pixels is a completely different path - I'd say better to have a bit of redundancy (once per segment frame) and be safe.
ready to merge? |
I ran some tests in conjunction with the PS: I definitely see some improvement! On the ESP32 I barely get any crashes. However, on the single-core C3 I still get very frequent crashes but they are somewhat random.
To fix this, segment data should only be manipulated in sync with service() and not when a UI request is received. I do not understand the code enough to trace it any further or make improvements. Please correct me if my reasoning is nonsensical. |
I had a chance to run sonarcube on the WLED codebase. This PR solves a few "real" findings reported by the tool.
const
-> minor speedupfriend class Segment
->friend struct Segment
_t
could benullptr
- so better-safe-than-sorry I've added a simple check.new
throws an exception when out of memory, however it won't returnnullptr
-> replaced withnew(std::nothrow)
for cases where the code has an explicit check fornullptr
after the call to new. Only relevant for arduino-esp32 v2.0.xx (S3,S2,C3), where a failed new always led to crash with unhandled exception.