Skip to content

TypeScript refactoring and tooling update including performance optimizations #29

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
Apr 16, 2025

Conversation

kyr0
Copy link
Contributor

@kyr0 kyr0 commented Jan 16, 2025

With this PR, I'm providing the source code, tests, docs, and benchs for the full refactoring of the library to TypeScript.

TL;DR: I know it's a huge PR, but I made sure that it's easy to compare line-by-line (!)

I'll keep this description short, as the code speaks for itself; here's a short list of changes including reasoning:

  • src is the standard for TypeScript projects. I moved the code there, added all types, fixed a few minor issues like grammar, added tsconfig.json matching the target language level, and removed the JSDoc parameter type annotations to prevent duplicate type definitions. Those doc types are inferred by typedoc, which is now used instead of jsdoc, produces similar output but supports TypeScript.
  • eslint was outdated. While updating, I also upgraded the configuration format to the new flat format, maintaining all configuration values "as is". For a well-typed experience, I chose typescript-eslint.
  • rollup has a critical security vulnerability (XSS); it was audit fixed alongside using pkgroll for packaging, which reduces the complexity of the build process. The "exports" standard in package.json is now in place and defines the build process with zero additional configuration.
  • sideEffects are set to false in package.json. This allows bundlers in user-land code that imports this packages code to tree-shake any symbol out that isn't used which helps reducing bundle size in end-user products.
  • as discussed in Shouldn't we use TypedArray.set instead of a for loop for copying arraybuffers? #22 loop unroll implementations have been integrated including original code in comments, for added readability.
  • snake_case functions got camelCase pendants where they didn't have any. All snake_case functions have been marked as deprecated though, for API consistency.
  • in folder bench, the benchmarks have been comitted.
  • server.js has been modernized and all deprecated APIs were replaced by modern solutions. engines in package.json were updated to 14.x, respective npm 8.x minimum.
  • the Makefile was updated to reflect the new reality, but maintains the traditional style.
  • because && traditionally leads to issues on non-unix systems, build has been separated in build and postbuild in package.json
  • the documentation in README.md has been updated and a new section has been added for Performance Benchmarks and the 0.4.0 changes.
  • a version bump to 0.4.0 seemed reasonable because of slight changes of implementation details, to prevent behaviour to change in downstream projects, which an update to 0.3.x would introduce.

Verification Steps:

  • Everything should work out-of-the-box after a single npm i to update local dependencies.
Bildschirmfoto 2025-01-16 um 19 12 38
  • I throughly tested:
    • the build process
Bildschirmfoto 2025-01-16 um 19 12 54
  • generating docs (typedoc)
Bildschirmfoto 2025-01-16 um 19 25 06 Bildschirmfoto 2025-01-16 um 19 25 13
  • unit testing (all green)

  • example testing (it works in all engines)

  • linting (all green)

Bildschirmfoto 2025-01-16 um 19 16 36

Note 1:
One change that pkgroll introduces is that there is no dist/index.es.js anymore as it is replaced by dist/index.mjs which serves its purpose (and is referenced in package.json as by the current community standard).

Note 2:
In the verification, I think I found a little bug. https://github.com/padenot/ringbuf.js/pull/29/files#diff-49be8ccb783567bb711d67edf34f746ae9bd8e7e5b1110a498344bf8e9969f6cR56 Would be cool if you could please double-check if the original code was intended as-is, and if my "fix" is fine or would lead to diverging verification behaviour.

I'm looking forward to your feedback!

p.s.: This PR replaces both other PRs which become automatically obsolete and IMHO fixes #22 - making this project 100% green for the time being.

kyr0 added 2 commits January 16, 2025 19:28
…ver.mjs, bundler, docs, implemented unroll optimizations, added benchs to verify the results and updated documentation plus package.json including version bump
…ecked that node:* and .js imports were stable with 14.x
@kyr0
Copy link
Contributor Author

kyr0 commented Jan 16, 2025

Netlify fails probably because pkgroll is not compatible with this ancient Node.js version:

8:00:56 PM: Downloading and installing node v12.18.0...
8:00:56 PM: Downloading https://nodejs.org/dist/v12.18.0/node-v12.18.0-linux-x64.tar.xz...
8:00:56 PM: Computing checksum with sha256sum
8:00:56 PM: Checksums matched!
8:00:58 PM: Now using node v12.18.0 (npm v6.14.4)

Min: >= 18
https://github.com/privatenumber/pkgroll/blob/master/package.json#L39C14-L39C15

Would have been the same with a modern version of rollup:
https://github.com/rollup/rollup/blob/master/package.json#L221

@padenot
Copy link
Owner

padenot commented Jan 21, 2025

@kyro, just a note that I'm very very busy with high-priority work stuff, and while this is also work stuff and generally looks excellent, I haven't finished the review -- but your work is highly appreciated, and I hope to get to it this week.

@kyr0
Copy link
Contributor Author

kyr0 commented Jan 22, 2025

@kyro, just a note that I'm very very busy with high-priority work stuff, and while this is also work stuff and generally looks excellent, I haven't finished the review -- but your work is highly appreciated, and I hope to get to it this week.

Thank you for your response @padenot . No worries, I feel you. I know these kind of situations too well. Thank you for taking the time to work on this anyway. That's highly appreciated as well! I hope your stress level is not skyrocketing. Please take your time -- there's absolutely no need to hurry. I'm fine with my codebase and not blocked in any way.

Copy link

@FallingSnow FallingSnow left a comment

Choose a reason for hiding this comment

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

My review doesn't mean much, but at least you know one more person has looked over this.

Looks good overall. The line changes look deceptively large, this is actually a pretty easy review (although I did not look over the bench files, readme, or linting files changes). I didn't see anything that would explicitly prevent this from being merged as is.

Awesome work @kyr0.

Copy link
Owner

@padenot padenot left a comment

Choose a reason for hiding this comment

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

Looks perfect, I'll push an additional commit and publish it on npm. Thanks a ton for doing this, and sorry about the delay, I was super busy and completely forgot about it.

README.md Outdated
@@ -68,6 +68,18 @@ allows running the build step and copying the file to allow the example to work.

allows rebuilding the documentation.

## Performance Benchmarks

As of version `0.4.0`, the whole codebase has been ported to TypeScript, tooling has been modernized and dependencies were updated. The pakcage is now also marked as side-effect free, which allows bundlers to tree-shake the code for unused symbols when imported.
Copy link
Owner

Choose a reason for hiding this comment

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

Typo on package.

Copy link
Owner

Choose a reason for hiding this comment

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

Fixed it.

README.md Outdated

Alongside these updates, two performance optimizations were introduced:

- `_copy` has been optimized by a loop factor of 16. [`bench`](bench/deinterleave-bench.html)
Copy link
Owner

Choose a reason for hiding this comment

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

... optimized by unrolling with ...

Copy link
Owner

Choose a reason for hiding this comment

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

Also fixed that one.

@padenot padenot force-pushed the feat/kyr0-typescript-port branch from 4f97e22 to effbf98 Compare April 16, 2025 12:14
@padenot
Copy link
Owner

padenot commented Apr 16, 2025

I had to bump the node versions from an antediluvian version to the oldest supported both in GitHub Actions and netlify, and we're good, I'll go and publish the package now. Thanks a lot, again!

@padenot padenot merged commit 7996bf2 into padenot:main Apr 16, 2025
1 of 2 checks passed
@kyr0
Copy link
Contributor Author

kyr0 commented Apr 16, 2025

Wonderful! I'm so happy that you like the changes! :) I'll update my project and switch to the new version, providing you with an "in the wild" test result very soon!

And I'm glad you're not as stressed anymore!

@planted-mreinstein
Copy link

Looks like a nice refactor. The only note I have is it would be nice to understand when the fast path of _copy kicks in at the API user level.

@padenot
Copy link
Owner

padenot commented Apr 16, 2025

Yeah we can make this clearer. Essentially:

  • If you use the third parameter for push: never, always use the unrolled loop (which is 2x faster than the previous code)
  • If you don't use it (copy the full array), you can keep count, it will "fall back" to the 16x unrolled loop each time you loop around and cross the ring buffer in an operation.

E.g. if you're pushing 6 values at a time, and it's the full array, and your capacity is 28 (for the sake of the example), it will go:

  • fast (one write, next write index 6)
  • fast (one write, next write index 12)
  • fast (one write, next write index 18)
  • fast (one write, next write index 24)
  • not as fast (two writes: 24 to 27, 4 elements, then 0 to 1, two elements, next write index 2)
  • etc.

it follows that if you use a ring buffer capacity that is a multiple of your enqueue size, and those are fixed (which is often the case in audio), you hit the fastest path 100% of the time.

One can keep count externally, because the logic isn't particularly complex, but we could add a method that would return if the next write of n elements will be able to use set. It would have the same signature as push but would, but would tell you if it's going to use set(...) internally.

@planted-mreinstein
Copy link

we could add a method that would return

I would err on the side of keeping the library simple. Thanks for the information!

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

Successfully merging this pull request may close these issues.

Shouldn't we use TypedArray.set instead of a for loop for copying arraybuffers?
4 participants