Live data from Hacker News

My failed attempt to shrink all NPM packages by 5%

evanhahn.com

231–240 of 253 posts

Re: My failed attempt to shrink all NPM packages by 5%

#231

zopfli is the wrong thing to use here. If you want an example of where these things go badly: the standard compression level for rpms on redhat distros is zstd level 19. This has all the downsides of other algorithms - it's super slow - often 75-100x slower than the default level of zstd[1]. It achieves a few percent more compression for that speed. compared to even level 10, it's like 0-1% higher compression, but 20…

> the standard compression level for rpms on redhat distros is zstd level 19 > The only reason to use zstd is because you want fairly good compression, but fast I would think having fast decompression is desirable, too, especially for rpms on redhat distros, which get decompressed a lot more often than they get compressed, and where the CPUs doing decompression may be a lot slower than the CPUs doing the compression.…

Let me start by reiterating - zstd is a great option. I think zstd level 5-10 would have been an awesome choice. I love zstd - it is a great algorithm that really hits the sweet spot for most users between really fast and good compression, and very very fast decompression. I use it all the time.

In this case, yes, zstd has faster decompression , but xz decompression speed is quite fast, even before you start using threads. I have no idea why their data found it so slow.

Here's an example of this: https://web.archive.org/web/20231218003530/https://catchchal...

Even on large compressed archives, xz decompression times does not go into minutes - even on a mid range 16 year old intel CPU like being used in this test. Assuming the redhat data on this single rpm is correct, i would bet it's more related to some weird buffering or chunking issue in the rpm's compressor library usage than actual xz decompression times. But nobody seems to have bothered look at why the times seemed ridiculous, at all, they just sort of accepted them as is.

They also based this particular change on the idea that they would get similar compression ratio to xz - they don't, as i showed.

Anyway, my point really wasn't "use xz", but that choosing zstd level 19 is probably the wrong choice no matter what.

There own table, which gives you data on 1 whole rpm, shows that zstd level 15 gave them compression comparable to xz (on that RPM, it's wrong in general), at compression speed similar to xz (also wrong in general, it's much slower than that).

It also showed that level 19 was 3x slower than that for no benefit.

Result: Let's use level 19.

Further the claim "Users that build their packages will experience slightly longer build times." is total nonsense - their own table shows this. If you had an RPM that was 1.6gb, but took 5 minutes to build (not uncommon, even for that size, since it's usually assets of some sort), you are now taking 30 minutes, and spending 24 of it on compression.

Before it took ... 3 minutes to do compression.

Calling this "slightly longer build times" is hilarious at best.

I'll make it concrete: their claim is based on building Firefox and compressing the result, and amusingly, even there it's still wrong. Firefox RPM build times on my machine are about 10-15 minutes. Before it took 3 minutes to compress the RPM. Now it takes 24.

This is not "slightly longer build times". Before it took 30% of the build time to compress the RPM.

Now it takes 24 minutes, or 2.5x the entire build time.

That is many things, but it is not a "slightly longer build time".

I'll just twist the knife a little more:

RPM supports using threading for the compressors, which is quite nice. It even supports basing it on the number of cpus you have set to use for builds. They give examples of how to do it, including for level 19:

  /usr/lib/rpm/macros:
  #                "w19T8.zstdio"  zstd level 19 using 8 threads
  #               "w7T0.zstdio"   zstd level 7 using %{getncpus} threads

The table with this single rpm even tested it with threads!

Despite this - they did not turn on threads in the result...

  /usr/lib/rpm/redhat/macros:%_binary_payload w19.zstdio

So they are doing all this single threaded for no particular reason - as far as i can tell, this is a bug in this well thought out change.

All this to say - i support NPM in being careful about this sort of change, because i've seen what happens when people aren't.

Re: My failed attempt to shrink all NPM packages by 5%

#232
Nice exercise in bureaucracy.

- I don't see the problem with adding a C dependency to a node project, native modules are one of the nicest things about node.js

- Longer publishing time for huge packages (eg. typescript) is a bigger problem but I don't think it should impact a default. Just give the option to use gzip for the few outliers.

Re: My failed attempt to shrink all NPM packages by 5%

#233
post #23
post #18

Earlier quoted context omitted.

50% size savings isn't important to the people who pay for it. They pay at most pennies for 100% savings (that is somehow all the functionality in zero bytes - not worth anything to those paying the bills)

Size savings translates to latency improvements which directly affects conversion rates. Smaller size isn’t about reducing costs but increased revenue. People care.

Who? anyone who is on a slow internet connection or who has a slow device can tell you they don't care. Or maybe they do but features are far more important. I guess if things are slow on a top of the line device withia fast connection they would care.

Re: My failed attempt to shrink all NPM packages by 5%

#234

We wouldn't have to worry about over-the-wire package size if the modern DevOps approach wasn't "nuke everything, download from the Internet" every build. Back in my Java days, most even small-time dev shops had a local Maven registry that would pass through and cache the big ones. A CI job, even if the "container" was nuked before each build, would create maybe a few kilobytes of Internet traffic, possibly none at a…

In every org I've worked with, we had a local dependency mirror in the GitOps architecture.

Re: My failed attempt to shrink all NPM packages by 5%

#235

Pulling on this thread, there are a few people who have looked at the ways zopfli is inefficient. Including this guy who forked it, and tried to contribute a couple improvements back to master: https://github.com/fhanau/Efficient-Compression-Tool These days if you’re going to iterate on a solution you’d better make it multithreaded. We have laptops where sequential code uses 8% of the available cpu.

> These days if you’re going to iterate on a solution you’d better make it multithreaded. Repetition eliminating compression tends to be inherently sequential. You'd probably need to change the file format to support chunks (or multiple streams) to do so. Because of LZ back references, you can't LZ compress different chunks separately on different cores and have only one compression stream. Statistics acquisition (hi…

There are gzip variants that break the file into blocks and run in parallel. They lose a couple of % by truncating the available history.

But zopfli appears to do a lot of backtracking to find the best permutations for matching runs that have several different solutions. There’s a couple of ways you could run those in parallel. Some with a lot of coordination overhead, others with a lot of redundant calculation.

Re: My failed attempt to shrink all NPM packages by 5%

#237

We wouldn't have to worry about over-the-wire package size if the modern DevOps approach wasn't "nuke everything, download from the Internet" every build. Back in my Java days, most even small-time dev shops had a local Maven registry that would pass through and cache the big ones. A CI job, even if the "container" was nuked before each build, would create maybe a few kilobytes of Internet traffic, possibly none at a…

I really don't want to go back to the old world where every part of your build is secretly stateful and fails in mysterious hard to reproduce ways.

You can and should have your own caching proxy for all your builds but local caches are evil.

Re: My failed attempt to shrink all NPM packages by 5%

#238
post #211

Earlier quoted context omitted.

The os should do file deduplication and compression and decompression faster than npm. But I guess the issue is npm cannot give the os hints to compress a folder or not?

Even if the file system does it that’s still per-file. You still need all the bookkeeping. And you’re not gonna get great compression if a lot of the files are tiny. The tar contains a whole bunch of related files that probably compress a lot better together, and it’s only one file on disk for the file system to keep track of. It’s gonna be a lot more efficient. When compiling you’re probably gonna touch all the sour…

> And you’re not gonna get great compression if a lot of the files are tiny.

The future of compression is likely shared dictionaries, basically recognize the file type and then use a dictionary that is optimized for that file type. E.g. JavaScript/TypeScript or HTML or Rust or C++, etc. That can offset the problems with small files to a large degree.

But again, this is a complex problem and it should be dealt with orthogonally to npm in my opinion.

I also checked and it should be possible for npm to enable file system compression at the OS level on both Windows and MacOS.

Also if it was dealt with orthogonally to npm, then it could be used by pip, and other package systems.

Re: My failed attempt to shrink all NPM packages by 5%

#239

Earlier quoted context omitted.

That's on the package publishers, not NPM. They give you an `.npmignore` that's trivially filled out to ensure your package isn't full of garbage, so if someone doesn't bother using that: that's on them, not NPM. (And it's also a little on the folks who install dependencies: if the cruft in a specific library bothers you, hit up the repo and file an issue (or even MR/PR) to get that .npmignore file filled out. I've h…

It's much better to allowlist the files meant to be published using `files` in package.json because you never know what garbage the user has in their folder at the time of publish. On a typical project with a build step, only a `dist` folder would published.

Not a fan of that one myself (it's far easier to tell what doesn't belong in a package vs. what does belong in a package) but that option does exist, so as a maintainer you really have no excuse, and as a user you have multiple MR/PRs that you can file to help them fix their cruft.

> On a typical project with a build step, only a `dist` folder would published.

Sort of, but always include your docs (readme, changelog, license, and whatever true docs dir you have, if you have one). No one should need a connection for those.

Re: My failed attempt to shrink all NPM packages by 5%

#240

Earlier quoted context omitted.

>But oh boy was it slow to unpack those bzip2 packages! Since conda had good caching, if you build environments often at all you could be paying more in decompress time than you pay in compression time. For Paper, I'm planning to cache both the wheel archives (so that they're available without recompressing on demand) and unpacked versions (installing into new environments will generally use hard links to the unpacke…

I designed a system which was a lot like uv but written in Python and when I looked at the politics I decided not to go forward with it. (My system also had the problem that it had to be isolated from other Pythons so it would not get its environment trashed, with the ability for software developers to trash their environment I wasn't sure it was a problem that could be 100% solved. uv solved it by not being written…

Yes, well - if I still had reason to care about the politics I'd be in much the same position, I'm sure. As is, I'm going to just make the thing, write about it, and see who likes it.
Post reply on HN