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 pressureArrayPool<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

  1. Reuse HtmlToPdf across 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).

  2. Stream output for HTTP responses — use HtmlToPdf.RenderAsync(html, response.Body) instead of getting a byte[] and copying it. This saves one allocation and one copy per request.

  3. 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.

  4. 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.