Skip to content

Cleanup base64::encode functions #6607

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

Merged
merged 4 commits into from
Oct 31, 2019
Merged

Conversation

dirkmueller
Copy link
Contributor

The implementation choice here using libb64 is generally good as it
is a relatively fast implementation, however the adaptation to
use PROGMEM for the translation function was a bad choice, as reading
randomly PROGMEM with byte-wide access is very very very slow.

Doing a naive if-snake is between 10% and 50% faster and uses less
flash (about 120 bytes less) and also for reasons I don't understand
8 bytes less data RAM (maybe the removal of static?).

@dirkmueller
Copy link
Contributor Author

I'll play with NRVO a bit, but need to rebuild my test setup first before pushing the change.

@dirkmueller dirkmueller changed the title Cleanup base64::encode functions [WIP] Cleanup base64::encode functions Oct 6, 2019
@earlephilhower
Copy link
Collaborator

Could we just use the ROM base64 functions with a tiny wrapper, instead?

earle@server:~/Arduino/hardware/esp8266com/esp8266/bootloaders/eboot$ grep base64 ../../tools/sdk/ld/eagle.rom.addr.v6.ld 
PROVIDE ( base64_decode = 0x40009648 );
PROVIDE ( base64_encode = 0x400094fc );

@dirkmueller
Copy link
Contributor Author

I've seen that as well, but I couldn't find a header definition with the calling convention or any reference to it in the espressif nonos sdk documentation. Any idea where to find this?

And can we remove libb64/ ? Otherwise this won't remove code size.. there was conflicting information about whether or not it is part of the api guarantees.

@earlephilhower
Copy link
Collaborator

Looking at the disassembly, I think it's safe to ignore my suggestion, @dirkmueller . I see it does some math and uses the ROM malloc implementation, which is definitely a no-no for our apps.

@dirkmueller dirkmueller force-pushed the base64_opt branch 2 times, most recently from d6737b5 to e7e5577 Compare October 7, 2019 19:36
@dirkmueller dirkmueller requested a review from devyte October 7, 2019 19:37
@dirkmueller
Copy link
Contributor Author

@devyte please reevaluate the 3.0 tagging. I think this can be merged any time now.

String base64 = String(buffer);
free(buffer);
return base64;
int len = base64_encode_block((const char *) &data[0], length, (char*)base64.begin(), &_state);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this, can we have base64_encode_block take a String instead and use the String += <char> way of setting it. That keeps base64 out of String's innards, while only requiring the single allocation of the block w/String::reserve.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, thats the question. in the discussion earlier it was said that libb64/ is part of the API and can't be changed, so I don't want to create yet another wrapper around it.

if we stop considering libb64/ being part of the API then I'd be happy to fold this into base64.cpp and clean it up and make it use String() directly.

Copy link
Collaborator

@devyte devyte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert the friend thing. Your previous code was better, just needed a few minor tweaks.

@dirkmueller dirkmueller force-pushed the base64_opt branch 2 times, most recently from 340af13 to da2ebaf Compare October 19, 2019 20:15
The implementation choice here using libb64 is generally good as it
is a relatively fast implementation, however the adaptation to
use PROGMEM for the translation function was a bad choice, as reading
randomly PROGMEM with byte-wide access is very very very slow.

Doing a naive if-snake is between 20% and 55% faster and uses less
flash (about 120 bytes less) and also for reasons I don't understand
8 bytes less data RAM (maybe the removal of static?).

In addition the base64::encode function was allocating for larger
input a huge amount of memory (twice the total size). we can reduce
that by doing a chunk-wise conversation to base64.
Rather than first creating a string with newlines and then
stripping it away in the fast path of constructing the query,
we can call the right method and trust that the result does
not have newlines anymore.
@dirkmueller
Copy link
Contributor Author

@devyte please also reconsider the "target 3.0.0" labeling here, there is no API change anymore, so this should be suitable for any release.

@devyte devyte modified the milestones: 3.0.0, 2.6.0 Oct 19, 2019
@devyte
Copy link
Collaborator

devyte commented Oct 19, 2019

Done.

@dirkmueller dirkmueller changed the title [WIP] Cleanup base64::encode functions Cleanup base64::encode functions Oct 27, 2019
Copy link
Collaborator

@earlephilhower earlephilhower left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with the note to @devyte. At worst, this isn't any worse than what was there, but at best it could be a great improvement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants