OxiPNG: PNG optimizer written in Rust
github.com
github.com
Note in that thread that the author implies that the optimization levels between the two tools are not necessarily equivalent or stable.
$ optipng --version
OptiPNG version 0.7.6
$ oxipng --version
oxipng 0.16.3
Ran on a 2016 MBP, i7.Time:
opt-level oxipng optipng
2 148.63s 30.80s
3 9.56s 45.99s
4 23.82s 90.57s
Size of resulting image: opt-level
original 12M
oxipng optipng
2 12M 12M
3 12M "already optimized"
4 12M "already optimized"
TBH I don't really know how to interpret these results. But with the strange exception of `oxipng -o 2`, it does seem like the naive parallelism offers some real performance wins. Disclaimer: this is an incredibly amateur attempt to benchmark, I really don't have enough knowledge to produce something authoritative. oxipng optipng optimage
size 12 231 612B 12 231 612B 11 027 901B
time 23.82s 90.57s 50.8s
I keep on improving my single-threaded image compressor (http://getoptimage.com).Version 0.7.6 of OptiPNG was released 2016-apr-03.
Not super recent, but it seems reasonable, given the limited scope, that it would stabilize and not change much after a certain point
Absolutely agree with your point about reaching a stage where not so many big changes are necessary though.
Parallelizing the algorithm helps in the "single file" case, which is still important.
Not really. Both optipng and oxipng do "trials": pick a set of parameters, try to compress the image with them, repeat with the next set of parameters. Each trial is completely independent from the others, so it's trivial to run them all in parallel.
Most of the work of running the trials in parallel is done by the "rayon" crate; the oxipng author only had to do an .into_par_iter() to get a rayon parallel iterator, and then do a map/reduce on it.
The difficulty of doing that with optipng was global state. Quoting from https://www.reddit.com/r/rust/comments/48vkjy/oxipng_a_multi...:
"My initial plan as well was to fork optipng and implement multithreading on it, but optipng, like many C programs, takes pride in using global mutable shared state in every place possible, and I didn't want to try to untangle that spaghetti code to make it thread-safe."
Why?
> Worst case scenario, I'll need to add an instruction for Windows users to install the Visual C++ 2015 runtime. I'd prefer to avoid that though. This also likely won't be a problem with the GCC-compiled version, but this runtime is a dependency for all programs compiled by MSVC.
https://stackoverflow.com/questions/10166412/how-to-link-aga...
Microsoft doesn't seem to like this, and will say it's "officially not supported" (if I remember correctly, there's a very strongly worded post by Raymond Chen on the topic...), but this is how essentially all the apps that come with Windows are compiled, and I believe MingW does it too.
Asking your users to install some sketchy 'runtime' won't work. And not every app should need or want a specific install script to verify for the umpteenth time that something as basic as the C/C++ runtime library is available.
Right, that’s what MS tell.
What they don’t tell is these merge modules aren’t compatible enough. E.g. it’s impossible to install VC 2015 runtime DLLs (so-called Universal CRT) on a never updated Windows 7 SP1 machine.
So it got installed there somehow.
Specifically, PCs without KB2999226 installed.
Not all people update their Windows. Some just don’t care. Others people don’t want to connect PC to the internet. There’re also people who use mobile internet and pay for bandwidth.
When they’re unable to run the software they’ve paid money for, I don’t want to update Windows for them, I want my software to work. Hence, no dynamic CRT.
And sometimes Windows Update simply stops working. I've seen it happen more than once.
(you = whoever "doesn't care" about updates)
The software we’re offering doesn’t require to be online. It normally works unattended for hours, controlling some specialized industrial hardware. Not unlike embedded software. For this particular use case, being offline has it’s upsides: no downloads, no updates, no reboots.
The users who discovered that bug (initially I did include these CRT merge modules in my installer, as recommended by MS) didn’t even send us a screenshot. Instead, they took a photo of their PC’s screens with the error message, and sent us that. Then I was able to reproduce on an offline Win7 VmWare machine, and deliver a fixed version with statically linked CRT, which BTW worked OK even on a vanilla Windows 7 sp0 from 2009.
Personally, I would recommend updating Windows instead, and I do update the PCs I own. But I can, and do, support running my software on a never updated system.
Most developers grudgingly obeyed, bloating their apps either through static linking, including a copy of the set of runtime DLLs, or the .MSI installer in their own distribution.
But then there are those of us who saw through that ruse, gave MS the proverbial finger and linked with the "system MSVCRT.DLL". The result being tiny, highly portable binaries which don't require any installation or dependencies beyond what Windows already has.
[1] https://blogs.msdn.microsoft.com/vcblog/2014/06/10/the-great...
Which is one reason, when you look at Windows 3.x code, it was common to call Win16 APIs directly instead of ANSI C ones.
For example, ZeroMemory() instead of memset().
https://blogs.msdn.microsoft.com/vcblog/2015/03/03/introduci...
Also, please don't call them "MSVCRTs". That - MSVCRT.DLL - is the name of the binary of one particular version of the runtime, corresponding to VS 6.0. All further versions had different names. The proper generic term is CRT (as in, "C runtime").