Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ if(SOURCEMETA_CORE_HTML)
list(APPEND BENCHMARK_SOURCES html.cc)
endif()

if(SOURCEMETA_CORE_MARKDOWN)
list(APPEND BENCHMARK_SOURCES markdown.cc)
endif()

if(SOURCEMETA_CORE_GZIP)
list(APPEND BENCHMARK_SOURCES gzip.cc)
endif()
Expand Down Expand Up @@ -102,6 +106,11 @@ if(BENCHMARK_SOURCES)
PRIVATE sourcemeta::core::html)
endif()

if(SOURCEMETA_CORE_MARKDOWN)
target_link_libraries(sourcemeta_core_benchmark
PRIVATE sourcemeta::core::markdown)
endif()

if(SOURCEMETA_CORE_GZIP)
target_link_libraries(sourcemeta_core_benchmark
PRIVATE sourcemeta::core::gzip sourcemeta::core::io)
Expand Down
103 changes: 103 additions & 0 deletions benchmark/markdown.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#include <benchmark/benchmark.h>

#include <sourcemeta/core/markdown.h>

#include <cstddef> // std::size_t
#include <string> // std::string, std::to_string

static auto repeat(const std::string &pattern, const std::size_t count)
-> std::string {
std::string result;
result.reserve(pattern.size() * count);
for (std::size_t index = 0; index < count; ++index) {
result.append(pattern);
}

return result;
}

static auto increasing_backtick_runs(const std::size_t count) -> std::string {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Markdown_To_HTML_Pathological duplicates the pathological fixture already maintained by test/markdown/markdown_pathological_test.cc, including these helper implementations. Move the shared fixture generators and cases into a common internal header so regression coverage and benchmark coverage cannot silently desynchronize.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At benchmark/markdown.cc, line 19:

<comment>`Markdown_To_HTML_Pathological` duplicates the pathological fixture already maintained by `test/markdown/markdown_pathological_test.cc`, including these helper implementations. Move the shared fixture generators and cases into a common internal header so regression coverage and benchmark coverage cannot silently desynchronize.</comment>

<file context>
@@ -0,0 +1,103 @@
+  return result;
+}
+
+static auto increasing_backtick_runs(const std::size_t count) -> std::string {
+  std::string result;
+  for (std::size_t length = 1; length <= count; ++length) {
</file context>

std::string result;
for (std::size_t length = 1; length <= count; ++length) {
result.push_back('x');
result.append(length, '`');
}

return result;
}

static auto staircase_list(const std::size_t depth) -> std::string {
std::string result;
for (std::size_t level = 0; level < depth; ++level) {
result.append(level * 2, ' ');
result.append("- level\n");
}

return result;
}

// NOLINTNEXTLINE(readability-identifier-naming)
static void Markdown_To_HTML_Realistic_Document(benchmark::State &state) {
std::string input;
for (std::size_t index = 0; index < 500; ++index) {
const auto number{std::to_string(index)};
input.append("# Section ").append(number).append("\n\n");
input.append("This paragraph has *emphasis*, **strong emphasis**, ")
.append("`inline code`, a [link](https://sourcemeta.com/")
.append(number)
.append(" \"Title\"), an autolink to www.sourcemeta.com, and ")
.append("~~deleted text~~ with an entity &copy; and a footnote[^")
.append(number)
.append("].\n\n");
input.append("> A block quote with a [reference link][ref")
.append(number)
.append("]\n> spanning two lines.\n\n");
input.append("- First item\n - Nested *item*\n - Another `item`\n"
"- [x] Completed task\n- [ ] Pending task\n\n");
input.append("1. One\n2. Two\n3. Three\n\n");
input.append("| Name | Value | Notes |\n| :--- | ---: | :---: |\n"
"| alpha | 1 | *first* |\n| beta | 2 | `second` |\n"
"| gamma | 3 | [third](/third) |\n\n");
input.append("```cpp\nauto main() -> int {\n return 0;\n}\n```\n\n");
input.append(" indented code line\n\n---\n\n");
input.append("[ref")
.append(number)
.append("]: https://sourcemeta.com/reference/")
.append(number)
.append("\n[^")
.append(number)
.append("]: Footnote ")
.append(number)
.append(".\n\n");
}

for (auto iteration : state) {
auto result{sourcemeta::core::markdown_to_html(input)};
benchmark::DoNotOptimize(result);
}
}

// Inputs that make naive delimiter, bracket, and container handling
// quadratic or worse, which a linear parser renders almost instantly
// NOLINTNEXTLINE(readability-identifier-naming)
static void Markdown_To_HTML_Pathological(benchmark::State &state) {
const std::string input{
repeat("_x **y ", 5000) + "z" + repeat(" y** x_", 5000) + "\n\n" +
repeat("x_. ", 10000) + "\n\n" + repeat("__y ", 10000) + "\n\n" +
repeat("]b", 10000) + "\n\n" + repeat("![b", 10000) + "\n\n" +
repeat("_b* ", 10000) + "\n\n" + "x***y" + repeat("z* ", 10000) + "\n\n" +
repeat("[ b*", 10000) + "\n\n" + repeat("[ x](", 10000) + "\n\n" +
repeat("![", 5000) + "x" + repeat("]", 5000) + "\n\n" +
repeat("![x](<y", 5000) + "\n\nx" + repeat("<![CDATA[", 5000) + "\n\nx" +
repeat("<?", 5000) + "\n\n" + increasing_backtick_runs(2000) + "\n\n" +
repeat("> - ", 50) + "deep\n\n" + staircase_list(200) + "\n" +
"| a | b | c |\n|---|---|---|\n" + repeat("| x | y | z |\n", 5000)};

for (auto iteration : state) {
auto result{sourcemeta::core::markdown_to_html(input)};
benchmark::DoNotOptimize(result);
}
}

BENCHMARK(Markdown_To_HTML_Realistic_Document);
BENCHMARK(Markdown_To_HTML_Pathological);
25 changes: 24 additions & 1 deletion test/markdown/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
sourcemeta_test(NAMESPACE sourcemeta PROJECT core NAME markdown
SOURCES markdown_test.cc)
SOURCES
markdown_autolinks_test.cc
markdown_blockquotes_test.cc
markdown_characters_test.cc
markdown_code_blocks_test.cc
markdown_code_spans_test.cc
markdown_concurrency_test.cc
markdown_emphasis_test.cc
markdown_entities_test.cc
markdown_escapes_test.cc
markdown_footnotes_test.cc
markdown_headings_test.cc
markdown_images_test.cc
markdown_line_breaks_test.cc
markdown_links_test.cc
markdown_lists_test.cc
markdown_paragraphs_test.cc
markdown_pathological_test.cc
markdown_safety_test.cc
markdown_strikethrough_test.cc
markdown_tables_test.cc
markdown_task_lists_test.cc
markdown_thematic_breaks_test.cc
markdown_unsafe_test.cc)

target_link_libraries(sourcemeta_core_markdown_unit
PRIVATE sourcemeta::core::markdown)
Loading