blob: 6d24f5442e274d1a20e7af31d9d9acd97e463e0b [file] [log] [blame] [edit]
#include "llvm/Support/Mustache.h"
#include "benchmark/benchmark.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
// A large, raw string with many characters that require HTML escaping.
static const std::string LongHtmlString = [] {
std::string S;
S.reserve(500000);
for (int i = 0; i < 50000; ++i) {
S += "<script>alert('xss');</script>";
}
return S;
}();
// A deep AND wide JSON object for testing traversal.
static const llvm::json::Value DeepJsonData = [] {
llvm::json::Value Root = llvm::json::Object();
llvm::json::Object *Current = Root.getAsObject();
for (int i = 0; i < 50; ++i) { // 50 levels deep
for (int j = 0; j < 100; ++j) {
(*Current)["sibling_" + std::to_string(j)] = llvm::json::Value("noise");
}
std::string Key = "level_" + std::to_string(i);
(*Current)[Key] = llvm::json::Object();
Current = (*Current)[Key].getAsObject();
}
(*Current)["final_value"] = llvm::json::Value("Success!");
llvm::json::Array Arr;
for (int i = 0; i < 5000; ++i) { // 5,000 iterations
Arr.push_back(llvm::json::Value(i));
}
llvm::json::Object NewRoot;
NewRoot["deep_data"] = std::move(Root);
NewRoot["loop_array"] = std::move(Arr);
return llvm::json::Value(std::move(NewRoot));
}();
// A huge array for testing iteration performance.
static const llvm::json::Value HugeArrayData = [] {
llvm::json::Array Arr;
for (int i = 0; i < 100000; ++i) { // 100,000 array items
Arr.push_back(llvm::json::Object(
{{"id", llvm::json::Value(static_cast<long long>(i))},
{"is_even", llvm::json::Value(i % 2 == 0)},
{"data", llvm::json::Value("Item data for " + std::to_string(i))}}));
}
return llvm::json::Object({{"items", std::move(Arr)}});
}();
// The main template that includes a partial within a loop.
static const std::string ComplexPartialTemplate =
"Header\n"
"{{#items}}{{> item_partial}}{{/items}}\n"
"Footer";
// The partial template is now more complex, rendering multiple fields and a
// conditional section.
static const std::string ItemPartialTemplate =
"<div class=\"item\" id=\"{{id}}\">\n"
" <p>{{data}}</p>\n"
" {{#is_even}}<span>(Even)</span>{{/is_even}}\n"
"</div>\n";
// A single large string to stress the output buffer.
static const llvm::json::Value LargeOutputData = llvm::json::Object({
{"long_string",
llvm::json::Value(std::string(1024 * 1024, 'A'))} // 1MB string
});
// --- Static Data (Templates) ---
static const std::string BulkEscapingTemplate = "{{content}}";
static const std::string BulkUnescapedTemplate = "{{{content}}}";
static const std::string BulkUnescapedAmpersandTemplate = "{{& content}}";
static const std::string DeepTraversalTemplate = [] {
std::string LongKey =
"deep_data.level_0.level_1.level_2.level_3.level_4.level_5."
"level_6.level_7.level_8.level_9."
"level_10.level_11.level_12.level_13.level_14.level_"
"15.level_16.level_17.level_18.level_19."
"level_20.level_21.level_22.level_23.level_24.level_"
"25.level_26.level_27.level_28.level_29."
"level_30.level_31.level_32.level_33.level_34.level_"
"35.level_36.level_37.level_38.level_39."
"level_40.level_41.level_42.level_43.level_44.level_"
"45.level_46.level_47.level_48.level_49.final_value";
return "{{#loop_array}}{{" + LongKey + "}}{{/loop_array}}";
}();
static const std::string DeeplyNestedRenderingTemplate = [] {
std::string NestedTemplate = "{{#deep_data}}";
for (int i = 0; i < 50; ++i) {
NestedTemplate += "{{#level_" + std::to_string(i) + "}}";
}
NestedTemplate += "{{final_value}}";
for (int i = 49; i >= 0; --i) {
NestedTemplate += "{{/level_" + std::to_string(i) + "}}";
}
NestedTemplate += "{{/deep_data}}";
return NestedTemplate;
}();
static const std::string HugeArrayIterationTemplate =
"{{#items}}ID: {{id}}.{{/items}}";
static const std::string ComplexTemplateParsingTemplate = [] {
std::string LargeTemplate;
LargeTemplate.reserve(100000);
for (int i = 0; i < 1000; ++i) {
LargeTemplate += "{{var_" + std::to_string(i) +
"}}"
"{{#section_" +
std::to_string(i) + "}}Content{{/section_" +
std::to_string(i) +
"}}"
"{{!comment_" +
std::to_string(i) +
"}}"
"{{=<% %>=}}"
"<%var_tag_changed_to_percent_sign_" +
std::to_string(i) +
"%>"
"<%={{ }}=%>"
"{{^inverted_" +
std::to_string(i) + "}}Not Present{{/inverted_" +
std::to_string(i) + "}}";
}
return LargeTemplate;
}();
static const std::string SmallTemplateParsingTemplate =
"{{level_0.sibling_99}}\n"
"{{level_0.level_1.level_2.level_3.level_4.level_5.sibling_50}}\n"
"{{level_0.level_1.level_2.level_3.level_4.level_5."
"level_6.level_7.level_8.level_9."
"level_10.level_11.level_12.level_13.level_14.level_"
"15.level_16.level_17.level_18.level_19."
"level_20.level_21.level_22.level_23.level_24.level_"
"25.level_26.level_27.level_28.level_29."
"level_30.level_31.level_32.level_33.level_34.level_"
"35.level_36.level_37.level_38.level_39."
"level_40.level_41.level_42.level_43.level_44.level_"
"45.level_46.level_47.level_48.level_49.final_value}}\n";
static const std::string LargeOutputStringTemplate = "{{long_string}}";
// Tests the performance of rendering a large string with various escaping
// syntaxes.
static void BM_Mustache_StringRendering(benchmark::State &state,
const std::string &TplStr) {
llvm::mustache::Template Tpl(TplStr);
llvm::json::Value Data =
llvm::json::Object({{"content", llvm::json::Value(LongHtmlString)}});
for (auto _ : state) {
std::string Result;
llvm::raw_string_ostream OS(Result);
Tpl.render(Data, OS);
benchmark::DoNotOptimize(Result);
}
}
BENCHMARK_CAPTURE(BM_Mustache_StringRendering, Escaped, BulkEscapingTemplate);
BENCHMARK_CAPTURE(BM_Mustache_StringRendering, Unescaped_Triple,
BulkUnescapedTemplate);
BENCHMARK_CAPTURE(BM_Mustache_StringRendering, Unescaped_Ampersand,
BulkUnescapedAmpersandTemplate);
// Tests the "hot render" cost of repeatedly traversing a deep and wide
// JSON object.
static void BM_Mustache_DeepTraversal(benchmark::State &state) {
llvm::mustache::Template Tpl(DeepTraversalTemplate);
for (auto _ : state) {
std::string Result;
llvm::raw_string_ostream OS(Result);
Tpl.render(DeepJsonData, OS);
benchmark::DoNotOptimize(Result);
}
}
BENCHMARK(BM_Mustache_DeepTraversal);
// Tests the "hot render" cost of pushing and popping a deep context stack.
static void BM_Mustache_DeeplyNestedRendering(benchmark::State &state) {
llvm::mustache::Template Tpl(DeeplyNestedRenderingTemplate);
for (auto _ : state) {
std::string Result;
llvm::raw_string_ostream OS(Result);
Tpl.render(DeepJsonData, OS);
benchmark::DoNotOptimize(Result);
}
}
BENCHMARK(BM_Mustache_DeeplyNestedRendering);
// Tests the performance of the loop logic when iterating over a huge number of
// items.
static void BM_Mustache_HugeArrayIteration(benchmark::State &state) {
llvm::mustache::Template Tpl(HugeArrayIterationTemplate);
for (auto _ : state) {
std::string Result;
llvm::raw_string_ostream OS(Result);
Tpl.render(HugeArrayData, OS);
benchmark::DoNotOptimize(Result);
}
}
BENCHMARK(BM_Mustache_HugeArrayIteration);
// Tests the performance of the parser on a large, "wide" template.
static void BM_Mustache_ComplexTemplateParsing(benchmark::State &state) {
for (auto _ : state) {
llvm::mustache::Template Tpl(ComplexTemplateParsingTemplate);
benchmark::DoNotOptimize(Tpl);
}
}
BENCHMARK(BM_Mustache_ComplexTemplateParsing);
// Tests the performance of the parser on a small, "deep" template.
static void BM_Mustache_SmallTemplateParsing(benchmark::State &state) {
for (auto _ : state) {
llvm::mustache::Template Tpl(SmallTemplateParsingTemplate);
benchmark::DoNotOptimize(Tpl);
}
}
BENCHMARK(BM_Mustache_SmallTemplateParsing);
// Tests the performance of rendering a template that includes a partial.
static void BM_Mustache_PartialsRendering(benchmark::State &state) {
llvm::mustache::Template Tpl(ComplexPartialTemplate);
Tpl.registerPartial("item_partial", ItemPartialTemplate);
llvm::json::Value Data = HugeArrayData;
for (auto _ : state) {
std::string Result;
llvm::raw_string_ostream OS(Result);
Tpl.render(Data, OS);
benchmark::DoNotOptimize(Result);
}
}
BENCHMARK(BM_Mustache_PartialsRendering);
// Tests the performance of the underlying buffer management when generating a
// very large output.
static void BM_Mustache_LargeOutputString(benchmark::State &state) {
llvm::mustache::Template Tpl(LargeOutputStringTemplate);
for (auto _ : state) {
std::string Result;
llvm::raw_string_ostream OS(Result);
Tpl.render(LargeOutputData, OS);
benchmark::DoNotOptimize(Result);
}
}
BENCHMARK(BM_Mustache_LargeOutputString);
BENCHMARK_MAIN();