-
Notifications
You must be signed in to change notification settings - Fork 383
feat(encoding): improve hex conversion performance #1712
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
base: main
Are you sure you want to change the base?
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.
Sounds good. I just wonder if we can avoid creating the Array<number>
in the first line which would be quite some overhead if not optimized away.
Would it be an option to do something like data.values().map((byte) => ...)
? Do you have tests to measure the performance?
You're right about the Array.from() overhead - thank you. I could use the spread syntax instead: export function toHex(data: Uint8Array): string {
return [...data].map((byte) => byte.toString(16).padStart(2, "0")).join("");
} Or for maximum efficiency, reduce without intermediate arrays: export function toHex(data: Uint8Array): string {
return Array.prototype.reduce.call(
data,
(str, byte) => str + byte.toString(16).padStart(2, "0"),
""
);
} I don't have benchmarks yet, but I can run some tests if you'd like to see which approach performs best with different data sizes. What do you think? |
Getting some benchmarks in place would be very helpful to ensure we don't accidentally slow things down.
|
if (!Iterator.prototype.map) {
Iterator.prototype.map = function* (f) {
for (const value of this) {
yield f(value);
}
};
} |
Co-authored-by: dynst <[email protected]>
Implemented both suggestions - using |
Sorry, I didn't think that one through. The global You just need a helper function for function* map<T, U>(iterable: Iterable<T>, fn: (value: T) => U): IterableIterator<U> {
for (const value of iterable) {
yield fn(value);
}
} |
what tool do you use for formatting? |
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.
Tests pass locally, only whitespace changes needed by eslint (and those can be done automatically post-merge)
Co-authored-by: dynst <[email protected]>
done! |
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.
Implementation looks nice now. We have the Array<string>
with 2-char string elements in order to do the joining but no accumulator string anymore.
However, we are still missing benchmarks that show if this change is useful.
What was to motination for touching this function?
Benchmarks would make sense, we don't know what magic optimizations the v8 javascript engine is doing under the hood that might make |
Replace string concatenation in toHex() with Array.from().map().join() pattern for better performance with large arrays. This approach avoids creating intermediate string objects during concatenation, resulting in better memory usage and execution speed, especially when processing large byte arrays