yisibl / resvg-js

A high-performance SVG renderer and toolkit, powered by Rust based resvg and napi-rs.
https://resvg-js.vercel.app/
Mozilla Public License 2.0
1.54k stars 53 forks source link

sharp is faster for me when mass converting SVGs to HQ PNGs #145

Closed privatenumber closed 2 years ago

privatenumber commented 2 years ago

I was interested in using resvg-js in-place of sharp for its advertised performance benefits, but I'm finding that it's actually much slower. In addition, resvg-js crashes for me when processing more than ~400 SVGs at once, and the produced DPI is not accurate.

Please let me know if there's anything I can improve/fix in the configuration.

Reproduction

https://github.com/privatenumber/sharp-vs-resvgjs

Comparison evaluation

sharp is much faster

When converting 400 SVG icons from simple-icons to 2500 DPI 800px width PNGs, sharp is 3x faster than resvg-js.

resvg: { duration: '5472ms', icons: 400 }
sharp: { duration: '1569ms', icons: 400 }
sharp is faster by 3.49x

resvg-js crashes on too many icons

The number of icons is limited to 400 because when processing too many icons at once, resvg-js crashes the whole Node.js process:

$ pnpm benchmark

> node scripts/benchmark

thread '<unnamed>' panicked at 'the previous segment must be M/L/C', /Users/runner/.cargo/git/checkouts/resvg-4b7e4ee32ad6d954/cab0b15/usvg/src/pathdata.rs:160:17
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: failed to initiate panic, error 5

rsvg-js doesn't yield expected DPI

After benchmarking, the resulting PNGs are saved to pngs/. When comparing icons between sharp and rsvg, the DPI on sharp is 2400 (as expected), but the DPI on resvg-js is 72.

Zoom in to compare difference

resvg-js - 11.1 KB - 72 DPI sharp - 16.6 KB - 2400 DPI
500px svg 500px svg
Brooooooklyn commented 2 years ago

@privatenumber did you run the benchmark on the Arm64 device? resvg lacks simd optimization for non-x86 devices at present

yisibl commented 2 years ago

Thank you for your test.

Speed is only one aspect of resvg-js, it's just an added benefit, using different devices or different benchmarking methods can make a difference.

For example, on my device(MacBook Pro, Apple M1 Pro):

> node scripts/benchmark

resvg: { duration: '4132ms', icons: 400 }
sharp: { duration: '2174ms', icons: 400 }
sharp is faster by 1.90x

The gap was further reduced when I changed Sharp's rendering to be the same as in the resvg-js benchmark.

https://github.com/privatenumber/sharp-vs-resvgjs/blob/852c75497e39fae40a9a86e864a7205a8a2970c0/scripts/benchmark/sharp.js#L3

- export default svg => sharp(svg, { density: 2400 }).toBuffer();
+ export default svg => sharp(svg, { density: (72 * 24) / 800 }).resize(800).toBuffer();
resvg: { duration: '4132ms', icons: 400 }
sharp: { duration: '2174ms', icons: 400 }
sharp is faster by 1.21x

The first goal of resvg is to pursue correctness in SVG rendering (compliance with the SVG specification). For performance, upstream resvg does have a lot of room for improvement, the authors just haven't had the time to do it yet. https://github.com/RazrFalcon/resvg/pull/530#issuecomment-1207258586


resvg-js crashes on too many icons

I can reproduce this question, @Brooooooklyn PTAL. @privatenumber Can you submit a new issue?


rsvg-js doesn't yield expected DPI

This is caused by using the wrong parameters. In resvg-js, you only need to enter the final desired width or height via the fitTo parameter.

I submitted a PR to fix your test. https://github.com/privatenumber/sharp-vs-resvgjs/pull/1

CGQAQ commented 2 years ago

I also benched on my windows machine

image

here's the result image

privatenumber commented 2 years ago

@Brooooooklyn Here's my environment info:

  System:
    OS: macOS 12.4
    CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
  Binaries:
    Node: 16.14.2 - ~/.nvm/versions/node/v16.14.2/bin/node
    npm: 6.14.12 - ~/Library/pnpm/npm

@yisibl Thanks for the feedback and PR.

If sharp is also faster than resvg-js for you with this experiment, I'm curious which environments it's 25% faster in (as advertised on the README)?

resvg: { duration: '4132ms', icons: 400 }
sharp: { duration: '2174ms', icons: 400 }
sharp is faster by 1.21x

Seems like the second benchmark is incorrect (maybe you forgot to update the actual time values)? 4132/2174 = 1.90

This is caused by using the wrong parameters. In resvg-js, you only need to enter the final desired width or height via the fitTo parameter.

In that case, what is the dpi option for? And is there a way to increase the DPI?

I suppose an alternative method to improve PNG quality is to output a larger image, but this will further increase the performance gap between resvg-js and sharp.

zimond commented 2 years ago

sharp is using the more mature c++ bindings for node.js and it is proved to be stable and fast. napi-rs is quite new and I think the perf gap would be larger if you are using larger output size. So if you are indeed rasterizing large svgs to png, and performance is what you cares most, sticking to sharp is a good choice. We are certainly investigating this and try to tune the perf more. And will notify you if there's any progress.

Brooooooklyn commented 2 years ago

sharp is using the more mature c++ bindings for node.js and it is proved to be stable and fast. napi-rs is quite new and I think the perf gap would be larger if you are using larger output size.

I don't think so, the performance issue here is affected by resvg under the hood, it doesn't have any SIMD optimization at present (tiny-skia has only x86_64 SIMD optimization).

There are a lot of NAPI-RS packages faster than packages written in C++. like @napi-rs/bcrypt and @napi-rs/crc32

Compare to sharp, there is a package @napi-rs/image faster than sharp while encoding webp and avif. Both of them are using libwebp under the hood

zimond commented 2 years ago

@Brooooooklyn sorry and sincerely apology for the rushed response about napi-rs. I thought napi-rs did some buffer copy and I was wrong. So it is indeed not related to napi-rs.

My initial idea was that everything used in this library is pretty new and so it could be better to stick with sharp if the user wants a stable and fast solution. tiny-skia is far from a complete competitor with skia and it's not intended to be. resvg and tiny-skia focus to ship a portable and stable solution to cross-platform SVG rendering. And the author removed all perf results from resvg repo to prevent perf debates. Notably resvg passes more spec tests than most implementations. So let's just stop here.

And of course we will keep investigating the codebase and try to improve the performance as it hurts no one. For example there is the png crate which is under the hood doing PNG codecs, which we could investigate more about its performance.

Anyway, any advises about the performance of resvg should go to the resvg repo. I don't think it is appropriate to discuss it here and i will submit a patch to remove the perf advocates in readme.

yisibl commented 2 years ago

In that case, what is the dpi option for? And is there a way to increase the DPI?

Currently, modifying the DPI of png files is not supported. See: https://github.com/yisibl/resvg-js/commit/21d782dc9cf4ddc846f3a034fbccab8088448fa9

We still have a long way to go for performance and we will continue to optimize it. We're also working closely with our friends at vercel, who have tried to turn on SIMD optimization, and performance will improve significantly.