Benchmark results
Measured on a standard developer laptop (Intel i7, .NET 10). BenchmarkDotNet, Release build.
| Scenario | Mean | Peak Memory | Target |
|---|---|---|---|
| Simple page (h1 + paragraph) | 13 µs | 20 KB | < 50 ms |
| Invoice (table + styles) | 86 µs | 85 KB | < 100 ms |
| Large table (100 rows) | 1.2 ms | 939 KB | < 5 s |
All scenarios beat their targets by orders of magnitude.
Why it's fast
- Pure C#, no marshalling — no browser process, no native interop overhead. All rendering is managed code running directly in your process.
- Streaming output — the PDF writer outputs pages as they are rendered; no full-document buffering required.
- Font subsetting at pack time — only used glyphs are embedded, reducing both memory usage and output file size.
-
No GC pressure —
ArrayPool<T>is used for temporary buffers on hot paths. No large object heap allocations for normal documents. - Single-pass layout — the layout engine makes one forward pass through the document per page. There is no re-layout or second-pass measurement.
Performance tips
-
Reuse
HtmlToPdfacross requests — the static class lazily initializes font discovery once per process. No converter object to pool, but avoid triggering font loading on every request by calling your first render at startup (warm-up). -
Stream output for HTTP responses — use
HtmlToPdf.RenderAsync(html, response.Body)instead of getting abyte[]and copying it. This saves one allocation and one copy per request. -
Keep HTML lean — the layout engine is fast, but complex CSS selectors and deeply nested DOM trees still have a cost. Avoid thousands of unique CSS rules or excessive selector specificity chains.
-
Use system fonts — loading a font from a file is fast, but font discovery (finding the right file on disk) happens once per font name per process. Use font names that map directly to installed fonts to avoid discovery overhead on the first render.
Thread safety
HtmlToPdf is a static class. Each RenderAsync call creates its own
independent pipeline — DOM tree, CSS cascade, layout tree — with no shared mutable state.
You can call HtmlToPdf.RenderAsync from as many threads as you want simultaneously
without any locking or queuing.