Skip to content

PerformanceΒΆ

Time to render Rust's INSTALL.md to HTML 1000 times, on Python 3.14 and an AMD Ryzen 9 9900X:

Library Version Written in Seconds Times slower than pyromark
marko 2.2.4 Python 12.675 367
markdown2 2.5.5 Python 6.050 175
mistletoe 1.6.0 Python 3.928 114
markdown 3.11.1 Python 3.925 114
markdown-it-py 4.2.0 Python 3.356 99
mistune 3.3.4 Python 2.951 86
cmarkgfm 2025.10.22 C 0.090 2.6
comrak 0.0.16 Rust 0.088 2.6
pyromark 0.10.0 Rust 0.034 1

Each library ran once per round, in random order, for 30 rounds. The table shows medians. Each ratio is taken within a round, which limits how much changing load from other programs skews it. markdown-it-pyrs is left out: it has no wheels for Python 3.13 and newer, and a build from source crashes on 3.14.

# pip install cmarkgfm comrak markdown markdown2 markdown-it-py marko mistletoe mistune pyromark requests
import random
import statistics
from importlib.metadata import version
from timeit import timeit

import cmarkgfm
import comrak
import markdown
import markdown2
import markdown_it
import marko
import mistletoe
import mistune
import pyromark
import requests

text = requests.get(
    "https://raw.githubusercontent.com/rust-lang/rust/refs/tags/1.82.0/INSTALL.md"
).text

renderers = {
    "marko": lambda: marko.convert(text),
    "markdown2": lambda: markdown2.markdown(text),
    "mistletoe": lambda: mistletoe.markdown(text),
    "markdown": lambda: markdown.markdown(text),
    "markdown-it-py": lambda: markdown_it.MarkdownIt().render(text),
    "mistune": lambda: mistune.html(text),
    "cmarkgfm": lambda: cmarkgfm.markdown_to_html(text),
    "comrak": lambda: comrak.render_markdown(text),
    "pyromark": lambda: pyromark.html(text),
}
written_in = {"cmarkgfm": "C", "comrak": "Rust", "pyromark": "Rust"}
times = {name: [] for name in renderers}
for _ in range(30):
    for name in random.sample(list(renderers), len(renderers)):
        times[name].append(timeit(renderers[name], number=1000))

for name, seconds in times.items():
    ratio = statistics.median(t / p for t, p in zip(seconds, times["pyromark"], strict=True))
    print(
        f"| {name} | {version(name)} | {written_in.get(name, 'Python')} "
        f"| {statistics.median(seconds):.3f} | {ratio:.1f} |"
    )