Skip to content

Benchmarks

Every number on this page is read from one file, the raw results of the latest full run of make compare, the comparison anyone can rerun (how). The site’s build generates this page, the README’s headline numbers and the home page from it, and fails on a number that is not read from it. Each figure is the mean ± σ (n) over the blocks of the run, σ the sample standard deviation; two figures differ when their gap is more than 2σ = 2·√(σ₁²/n₁ + σ₂²/n₂).

When 2026-10-29 17:16 UTC, 151 minutes
Command make compare (tools/compare/compare.sh), the full mode
Code release 1.0.0, its published image quay.io/oxidestack/docx-to-pdf:1.0.0@sha256:a752ca8f27bf24fc704c1ab078afa22c3616d78cf8913e655939ae2a63d60609, built from commit 0f45e85403477f2e70138eae3055ae6dbc70c277
Host Intel(R) Xeon(R) Gold 6248R CPU @ 3.00GHz (x86_64), 8 vCPUs seen as 1 socket × 8 cores, 1 thread a core, 3000 MHz reported; caches L1d 32K, L1i 32K, L2 1024K, L3 36608K; 31.3 GiB; Ubuntu 24.04.5 LTS, kernel 6.8.0-147-generic, Docker 29.9.2, cgroup v2; a virtual machine under VMware, so the vCPUs’ pinning to the host’s cores is not visible from the guest
The speed cell 1 pinned vCPU, a pool of 1, 16 GiB and /tmp on the host’s disk a service; a closed load of the typical reports report-img30-10mb, report-img45-12mb, report-img60-15mb, report-img60-20mb; 52 conversions a cell after a warm-up; 3 blocks, each running every cell once in a shuffled order (seed 1793294188)
The torture cases each case at 12 GiB on vCPUs 2-3, each case waiting up to 300 s; soaks of 3000 conversions (1000 for moalhaddar/docx-to-pdf) alongside
oxidestack/docx-to-pdf Gotenberg, as shipped Gotenberg, no restarts moalhaddar/docx-to-pdf
conversions/s 0.942 ± 0.023 (3) 0.773 ± 0.030 (3) 0.824 ± 0.043 (3) 0.291 ± 0.005 (3)
CPU-s a conversion 1.008 ± 0.021 (3) 1.216 ± 0.025 (3) 1.143 ± 0.061 (3) 3.342 ± 0.079 (3)
p50 latency, s 3.12 ± 0.05 (3) 3.60 ± 0.09 (3) 3.52 ± 0.18 (3) 10.25 ± 0.27 (3)
p95 latency, s 3.42 ± 0.07 (3) 4.59 ± 0.12 (3) 3.93 ± 0.23 (3) 11.51 ± 0.27 (3)
p99 latency, s 3.51 ± 0.06 (3) 4.69 ± 0.18 (3) 4.01 ± 0.22 (3) 11.70 ± 0.30 (3)
container memory.peak, MiB 413 ± 0.3 (3) 683 ± 21 (3) 771 ± 32 (3) 938 ± 50 (3)
anonymous memory peak, MiB 324 ± 1 (3) 568 ± 55 (3) 640 ± 55 (3) 821 ± 41 (3)
written a conversion, MB 28.3 ± 0.2 (3) 28.3 ± 0.001 (3) 27.9 ± 0.2 (3) 29.9 ± 0.1 (3)
docker run to ready, s 0.50 ± 0.00 (3) 1.00 ± 0.01 (3) 1.01 ± 0.01 (3) 13.38 ± 0.93 (3)

conversions/soxidestack/docx-to-pdf0.942 ± 0.023Gotenberg, as shipped0.773 ± 0.030Gotenberg, no restarts0.824 ± 0.043moalhaddar/docx-to-pdf0.291 ± 0.005

CPU-s a conversionoxidestack/docx-to-pdf1.008 ± 0.021Gotenberg, as shipped1.216 ± 0.025Gotenberg, no restarts1.143 ± 0.061moalhaddar/docx-to-pdf3.342 ± 0.079

p99 latency, soxidestack/docx-to-pdf3.51 ± 0.06Gotenberg, as shipped4.69 ± 0.18Gotenberg, no restarts4.01 ± 0.22moalhaddar/docx-to-pdf11.70 ± 0.30

container memory.peak, MiBoxidestack/docx-to-pdf413 ± 0.3Gotenberg, as shipped683 ± 21Gotenberg, no restarts771 ± 32moalhaddar/docx-to-pdf938 ± 50

Each service’s figure over oxidestack/docx-to-pdf’s, the ratio of the means ± its standard error, from the same interleaved run. A ratio measured inside one run holds from one host to the next better than an absolute figure.

Gotenberg, as shipped Gotenberg, no restarts moalhaddar/docx-to-pdf
conversions/s 0.82 ± 0.02× (oxidestack/docx-to-pdf ahead) 0.87 ± 0.03× (oxidestack/docx-to-pdf ahead) 0.31 ± 0.01× (oxidestack/docx-to-pdf ahead)
CPU-s a conversion 1.21 ± 0.02× (oxidestack/docx-to-pdf ahead) 1.13 ± 0.04× (oxidestack/docx-to-pdf ahead) 3.32 ± 0.06× (oxidestack/docx-to-pdf ahead)
p99 latency, s 1.34 ± 0.03× (oxidestack/docx-to-pdf ahead) 1.15 ± 0.04× (oxidestack/docx-to-pdf ahead) 3.34 ± 0.06× (oxidestack/docx-to-pdf ahead)
container memory.peak, MiB 1.65 ± 0.03× (oxidestack/docx-to-pdf ahead) 1.86 ± 0.04× (oxidestack/docx-to-pdf ahead) 2.27 ± 0.07× (oxidestack/docx-to-pdf ahead)

Gotenberg is the generalist, and on its own ground nothing here competes with it: HTML and URLs to PDF, merging and splitting PDFs, PDF/A and PDF/UA, webhooks, clients in many languages and years in production. A team that needs those should use it. In this run, the rows where it is ahead of oxidestack/docx-to-pdf:

  • Gotenberg, no restarts, written a conversion, MB: 27.9 ± 0.2 (3) against 28.3 ± 0.2 (3) (the row)
  • Gotenberg, png-g-32767 (the same answer, 200, and the same pictures): answered in 9.78 s against 11.34 s, one run a side (the row)
  • Gotenberg, as shipped, LibreOffice’s resident memory over the soak’s second half: 289 ± 61 MiB over 1350 conversions against 391 ± 1 MiB over 1500 conversions, one soak a side (the row)
  • Gotenberg, no restarts, LibreOffice’s resident memory over the soak’s second half: 347 ± 65 MiB over 1500 conversions against 391 ± 1 MiB over 1500 conversions, one soak a side (the row)

And the rows where neither is ahead at 2σ:

  • Gotenberg, as shipped, written a conversion, MB: 28.3 ± 0.001 (3) against 28.3 ± 0.2 (3) (the row)

Hostile and borderline documents, one run a side, each service as it ships: the answer, its time, the container’s memory peak and, for a picture, how many pictures reached the PDF. Judged by the answer: the times and peaks have no σ.

Case oxidestack/docx-to-pdf Gotenberg moalhaddar/docx-to-pdf
pages-500000p 413 in 0.01 s; peak 236 MiB; alongside none; first 200 after the answer: 0.77 s 503 in 30.0 s; peak 7 609 MiB; alongside 1 × 503; first 200 after the answer: 218.53 s no answer in 300.04 s; peak 9 373 MiB; alongside 1 × TimeoutError: timed out; first 200 after the answer: none
px-8x1024mpx 413 in 0.02 s; peak 237 MiB; alongside none; first 200 after the answer: 0.78 s 503 in 30.0 s; peak 8 076 MiB; alongside 1 × 503; first 200 after the answer: 35.43 s no answer in 300.11 s; peak 2 505 MiB; alongside 1 × TimeoutError: timed out; first 200 after the answer: 227.54 s
tables-10000 504 in 180.09 s; peak 1 401 MiB; alongside 2 × 503, 1 × 200; first 200 after the answer: 0.83 s 503 in 30.0 s; peak 1 850 MiB; alongside 1 × 503; first 200 after the answer: 218.42 s no answer in 300.1 s; peak 4 076 MiB; alongside 1 × TimeoutError: timed out; first 200 after the answer: 451.4 s
png-g-32767 200 in 11.34 s; peak 348 MiB; pictures in the PDF 1; docx-to-pdf-pictures: downscaled=1, replaced=0 200 in 9.78 s; peak 1 138 MiB; pictures in the PDF 1 200 in 93.6 s; peak 1 524 MiB; pictures in the PDF 1
png-g-32768 200 in 11.2 s; peak 347 MiB; pictures in the PDF 1; docx-to-pdf-pictures: downscaled=1, replaced=0 200 in 0.83 s; peak 108 MiB; pictures in the PDF 0 200 in 0.47 s; peak 322 MiB; pictures in the PDF 0
png-rgb-18918 200 in 4.59 s; peak 152 MiB; pictures in the PDF 1; docx-to-pdf-pictures: downscaled=1, replaced=0 200 in 11.44 s; peak 1 139 MiB; pictures in the PDF 1 200 in 18.75 s; peak 1 442 MiB; pictures in the PDF 1
png-rgb-18919 200 in 4.39 s; peak 152 MiB; pictures in the PDF 1; docx-to-pdf-pictures: downscaled=1, replaced=0 200 in 0.75 s; peak 108 MiB; pictures in the PDF 0 200 in 0.45 s; peak 409 MiB; pictures in the PDF 0
empty 415 in 0.0 s; peak 34 MiB 200 in 0.76 s; peak 102 MiB; pictures in the PDF 0 200 in 0.31 s; peak 339 MiB; pictures in the PDF 0
random-1mb 415 in 0.01 s; peak 37 MiB 500 in 0.63 s; peak 101 MiB no answer in 300.04 s; peak 447 MiB
random-10mb 415 in 0.02 s; peak 46 MiB 500 in 0.7 s; peak 138 MiB no answer in 300.15 s; peak 455 MiB
tdf38575 200 in 0.36 s; peak 91 MiB; pictures in the PDF 1 200 in 0.84 s; peak 107 MiB; pictures in the PDF 1 no answer in 300.1 s; peak 350 MiB
pause pauses: HTTP/1.1 200 OK in 7.04 s, HTTP/1.1 200 OK in 6.77 s, HTTP/1.1 200 OK in 6.76 s; stalled: HTTP/1.1 408 Request Timeout in 120.01 s pauses: HTTP/1.1 200 OK in 7.5 s, HTTP/1.1 200 OK in 6.93 s, HTTP/1.1 200 OK in 6.89 s; stalled: HTTP/1.1 500 Internal Server Error in 30.0 s pauses: HTTP/1.1 200 in 8.68 s, HTTP/1.1 200 in 8.44 s, HTTP/1.1 200 in 8.29 s; stalled: TimeoutError: timed out in 200.1 s
egress 68 documents: 68 × 200; connections the listener logged: 0 68 documents: 68 × 200; connections the listener logged: 0 68 documents: 51 × 200, 17 × TimeoutError: timed out; connections the listener logged: 36 (27 public, 9 private); 150 s a document

LibreOffice’s resident memory after each conversion of the typical reports, one soak a side: the first tenth, the second half, and the least-squares slope.

Service Soak
oxidestack/docx-to-pdf 3000, 0 errors; LibreOffice RSS 390 ± 1 MiB (300 requests) over the first tenth / 391 ± 1 MiB (1500 requests) over the second half; +0.6 ± 0.0 MiB per 1000 over the last nine tenths, +0.5 ± 0.1 over the second half; floor 3.9 MiB per 1000 (1 % of the level): does not grow
Gotenberg, as shipped 3000, 0 errors; LibreOffice RSS 287 ± 57 MiB (270 requests) over the first tenth / 289 ± 61 MiB (1350 requests) over the second half; +1.4 ± 1.6 MiB per 1000 over the last nine tenths, +6.0 ± 3.8 over the second half; floor 2.9 MiB per 1000 (1 % of the level): does not grow
Gotenberg, no restarts 3000, 0 errors; LibreOffice RSS 326 ± 64 MiB (300 requests) over the first tenth / 347 ± 65 MiB (1500 requests) over the second half; +24.6 ± 1.5 MiB per 1000 over the last nine tenths, +55.0 ± 3.6 over the second half; floor 3.5 MiB per 1000 (1 % of the level): grows
moalhaddar/docx-to-pdf 1000, 0 errors; LibreOffice RSS 304 ± 75 MiB (100 requests) over the first tenth / 329 ± 56 MiB (500 requests) over the second half; +30.3 ± 7.9 MiB per 1000 over the last nine tenths, -26.9 ± 17.4 over the second half; floor 3.3 MiB per 1000 (1 % of the level): levels off

The README and the comparison guide quote a ratio, a time or a peak as the range over this run and the earlier full runs of the same command on the same kind of machine, whose raw results are these:

Image
oxidestack/docx-to-pdf quay.io/oxidestack/docx-to-pdf:1.0.0@sha256:a752ca8f27bf24fc704c1ab078afa22c3616d78cf8913e655939ae2a63d60609@sha256:a752ca8f27bf24fc704c1ab078afa22c3616d78cf8913e655939ae2a63d60609
k6 grafana/k6:2.3.0@sha256:9c2dee7f8ed74d317e4027c06a10f169b625638189de8d4555d0b3486a5aeb34
bench tools docx-to-pdf-bench-tools:dev@sha256:dc17e88e7d905b0256e60246ae148c1a89d2e542c1e0b91112fa01dc96713ae8
torture driver python@sha256:f6a589d43c42b9e7f7dc67a12d37132491f362859a5d750607710cc56da3bc72
Gotenberg gotenberg/gotenberg:8.38.1@sha256:aa552cc115db488e0f0bcb07446aac13071ad51505b642c84bb894500907b42b
moalhaddar/docx-to-pdf docker.io/moalhaddar/docx-to-pdf@sha256:14497d940936fe346cf74ce9c65bfa1eaff71487371888bd864777fd63a386c3

Generated with fixed seeds, or fetched by commit; each one’s SHA-256:

Document SHA-256
report-img30-10mb 276d4381cac57c10c75b2652056fe489361594a0576ee13647039161d992d6d1
report-img45-12mb b5c57826505addc85686b17243f0e9364298a48b10d398604b8332c4b3909cd6
report-img60-15mb 059b0fb786d307e23dde3a817af64dff6086eb4eb1a948d9cc215be1fde68f3c
report-img60-20mb a7eb5a7a35f71291c1c1ce99d756022659207cd53d2d3ae482cbb1e23ce4cd31
report-text-2p 14cc062cc71317d906db044e13fa2373b1f3a7760023dbf2cb5227088ef95edd
report-text-5p 4b5bb5dbf8cc307d8226e6be1f12656f1affc17ff04dc3ac50de221416d00adc
report-text-20p 630e3075e50260601535459dfaa46f090c47b7d7ed18cb0836754a6610c38521
pages-500000p 436e472ddd6c164dd973fb2d71a273b756444d2842a3ae60e63ce63bcd6b79ec
px-8x1024mpx 5dbb3b0237bfc88864fbe7ed410b4a4e34705be997cbc2dd285e8d2b01214020
tables-10000 2c4747fdc298d205edc0776df7c518ed9b27f52ef2f3a82570931128b59d9b76
png-g-32767 dc15106876878c782bd7ad361a173c76f194dd21f1e5bd1988905fa54f38fa41
png-g-32768 82310258bb2128cfc6750ad481a13143f0e6960b78be1df6d28c78710c593c61
png-rgb-18918 3f8a9a612418348332562181b83adf0355a23d9f5ee575da99aff6f8ea4df662
png-rgb-18919 e7270a6451eeb6dc8e2bf45bfdfec11c1d960d6c3b163f6466051762ed9289e6
empty e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
random-1mb 8e5525eaf28c03c578860c70ee5dc5adc65a87ad451f05d2c09536b429dad983
random-10mb 9b73c366dd182d40010695058c2aedced35d262a136ff3c8c31280ac4d12e151
tdf38575 c6d022b650e67cb5281cfc71083dab16930d5515ef9ff78fe54d7c04ed9debaf

Docker is all it needs; a quick mode prints both tables in minutes on a laptop, and moalhaddar/docx-to-pdf, built for amd64 only, is skipped on another architecture:

Terminal window
git clone --recursive https://github.com/oxidestack/docx-to-pdf.git && cd docx-to-pdf
make compare QUICK=1 # minutes on a laptop
make compare # the full mode, as above

examples/compare/ says what each mode runs. The run writes the tables and a results.json like this one.