290 lines · cpp
1#include "llvm/Support/Mustache.h"2#include "benchmark/benchmark.h"3#include "llvm/Support/JSON.h"4#include "llvm/Support/raw_ostream.h"5#include <string>6 7// A large, raw string with many characters that require HTML escaping.8static const std::string LongHtmlString = [] {9 std::string S;10 S.reserve(500000);11 for (int Idx = 0; Idx < 50000; ++Idx) {12 S += "<script>alert('xss');</script>";13 }14 return S;15}();16 17// A deep AND wide JSON object for testing traversal.18static const llvm::json::Value DeepJsonData = [] {19 llvm::json::Value Root = llvm::json::Object();20 llvm::json::Object *Current = Root.getAsObject();21 for (int i = 0; i < 50; ++i) { // 50 levels deep22 for (int j = 0; j < 100; ++j) {23 (*Current)["sibling_" + std::to_string(j)] = llvm::json::Value("noise");24 }25 std::string Key = "level_" + std::to_string(i);26 (*Current)[Key] = llvm::json::Object();27 Current = (*Current)[Key].getAsObject();28 }29 (*Current)["final_value"] = llvm::json::Value("Success!");30 31 llvm::json::Array Arr;32 for (int i = 0; i < 5000; ++i) { // 5,000 iterations33 Arr.push_back(llvm::json::Value(i));34 }35 36 llvm::json::Object NewRoot;37 NewRoot["deep_data"] = std::move(Root);38 NewRoot["loop_array"] = std::move(Arr);39 return llvm::json::Value(std::move(NewRoot));40}();41 42// A huge array for testing iteration performance.43static const llvm::json::Value HugeArrayData = [] {44 llvm::json::Array Arr;45 for (int i = 0; i < 100000; ++i) { // 100,000 array items46 Arr.push_back(llvm::json::Object(47 {{"id", llvm::json::Value(static_cast<long long>(i))},48 {"is_even", llvm::json::Value(i % 2 == 0)},49 {"data", llvm::json::Value("Item data for " + std::to_string(i))}}));50 }51 return llvm::json::Object({{"items", std::move(Arr)}});52}();53 54// The main template that includes a partial within a loop.55static const std::string ComplexPartialTemplate =56 "Header\n"57 "{{#items}}{{> item_partial}}{{/items}}\n"58 "Footer";59 60// The partial template is now more complex, rendering multiple fields and a61// conditional section.62static const std::string ItemPartialTemplate =63 "<div class=\"item\" id=\"{{id}}\">\n"64 " <p>{{data}}</p>\n"65 " {{#is_even}}<span>(Even)</span>{{/is_even}}\n"66 "</div>\n";67 68// A single large string to stress the output buffer.69static const llvm::json::Value LargeOutputData = llvm::json::Object({70 {"long_string",71 llvm::json::Value(std::string(1024 * 1024, 'A'))} // 1MB string72});73 74// --- Static Data (Templates) ---75 76static const std::string BulkEscapingTemplate = "{{content}}";77static const std::string BulkUnescapedTemplate = "{{{content}}}";78static const std::string BulkUnescapedAmpersandTemplate = "{{& content}}";79 80static const std::string DeepTraversalTemplate = [] {81 std::string LongKey =82 "deep_data.level_0.level_1.level_2.level_3.level_4.level_5."83 "level_6.level_7.level_8.level_9."84 "level_10.level_11.level_12.level_13.level_14.level_"85 "15.level_16.level_17.level_18.level_19."86 "level_20.level_21.level_22.level_23.level_24.level_"87 "25.level_26.level_27.level_28.level_29."88 "level_30.level_31.level_32.level_33.level_34.level_"89 "35.level_36.level_37.level_38.level_39."90 "level_40.level_41.level_42.level_43.level_44.level_"91 "45.level_46.level_47.level_48.level_49.final_value";92 return "{{#loop_array}}{{" + LongKey + "}}{{/loop_array}}";93}();94 95static const std::string DeeplyNestedRenderingTemplate = [] {96 std::string NestedTemplate = "{{#deep_data}}";97 for (int i = 0; i < 50; ++i) {98 NestedTemplate += "{{#level_" + std::to_string(i) + "}}";99 }100 NestedTemplate += "{{final_value}}";101 for (int i = 49; i >= 0; --i) {102 NestedTemplate += "{{/level_" + std::to_string(i) + "}}";103 }104 NestedTemplate += "{{/deep_data}}";105 return NestedTemplate;106}();107 108static const std::string HugeArrayIterationTemplate =109 "{{#items}}ID: {{id}}.{{/items}}";110 111static const std::string ComplexTemplateParsingTemplate = [] {112 std::string LargeTemplate;113 LargeTemplate.reserve(100000);114 for (int i = 0; i < 1000; ++i) {115 LargeTemplate += "{{var_" + std::to_string(i) +116 "}}"117 "{{#section_" +118 std::to_string(i) + "}}Content{{/section_" +119 std::to_string(i) +120 "}}"121 "{{!comment_" +122 std::to_string(i) +123 "}}"124 "{{=<% %>=}}"125 "<%var_tag_changed_to_percent_sign_" +126 std::to_string(i) +127 "%>"128 "<%={{ }}=%>"129 "{{^inverted_" +130 std::to_string(i) + "}}Not Present{{/inverted_" +131 std::to_string(i) + "}}";132 }133 return LargeTemplate;134}();135 136static const std::string SmallTemplateParsingTemplate =137 "{{level_0.sibling_99}}\n"138 "{{level_0.level_1.level_2.level_3.level_4.level_5.sibling_50}}\n"139 "{{level_0.level_1.level_2.level_3.level_4.level_5."140 "level_6.level_7.level_8.level_9."141 "level_10.level_11.level_12.level_13.level_14.level_"142 "15.level_16.level_17.level_18.level_19."143 "level_20.level_21.level_22.level_23.level_24.level_"144 "25.level_26.level_27.level_28.level_29."145 "level_30.level_31.level_32.level_33.level_34.level_"146 "35.level_36.level_37.level_38.level_39."147 "level_40.level_41.level_42.level_43.level_44.level_"148 "45.level_46.level_47.level_48.level_49.final_value}}\n";149 150static const std::string LargeOutputStringTemplate = "{{long_string}}";151 152// Tests the performance of rendering a large string with various escaping153// syntaxes.154static void BM_Mustache_StringRendering(benchmark::State &state,155 const std::string &TplStr) {156 llvm::BumpPtrAllocator Allocator;157 llvm::StringSaver Saver(Allocator);158 llvm::mustache::MustacheContext Ctx(Allocator, Saver);159 160 llvm::mustache::Template Tpl(TplStr, Ctx);161 llvm::json::Value Data =162 llvm::json::Object({{"content", llvm::json::Value(LongHtmlString)}});163 for (auto _ : state) {164 std::string Result;165 llvm::raw_string_ostream OS(Result);166 Tpl.render(Data, OS);167 benchmark::DoNotOptimize(Result);168 }169}170BENCHMARK_CAPTURE(BM_Mustache_StringRendering, Escaped, BulkEscapingTemplate);171BENCHMARK_CAPTURE(BM_Mustache_StringRendering, Unescaped_Triple,172 BulkUnescapedTemplate);173BENCHMARK_CAPTURE(BM_Mustache_StringRendering, Unescaped_Ampersand,174 BulkUnescapedAmpersandTemplate);175 176// Tests the "hot render" cost of repeatedly traversing a deep and wide177// JSON object.178static void BM_Mustache_DeepTraversal(benchmark::State &state) {179 llvm::BumpPtrAllocator Allocator;180 llvm::StringSaver Saver(Allocator);181 llvm::mustache::MustacheContext Ctx(Allocator, Saver);182 183 llvm::mustache::Template Tpl(DeepTraversalTemplate, Ctx);184 for (auto _ : state) {185 std::string Result;186 llvm::raw_string_ostream OS(Result);187 Tpl.render(DeepJsonData, OS);188 benchmark::DoNotOptimize(Result);189 }190}191BENCHMARK(BM_Mustache_DeepTraversal);192 193// Tests the "hot render" cost of pushing and popping a deep context stack.194static void BM_Mustache_DeeplyNestedRendering(benchmark::State &state) {195 196 llvm::BumpPtrAllocator Allocator;197 llvm::StringSaver Saver(Allocator);198 llvm::mustache::MustacheContext Ctx(Allocator, Saver);199 200 llvm::mustache::Template Tpl(DeeplyNestedRenderingTemplate, Ctx);201 for (auto _ : state) {202 std::string Result;203 llvm::raw_string_ostream OS(Result);204 Tpl.render(DeepJsonData, OS);205 benchmark::DoNotOptimize(Result);206 }207}208BENCHMARK(BM_Mustache_DeeplyNestedRendering);209 210// Tests the performance of the loop logic when iterating over a huge number of211// items.212static void BM_Mustache_HugeArrayIteration(benchmark::State &state) {213 llvm::BumpPtrAllocator Allocator;214 llvm::StringSaver Saver(Allocator);215 llvm::mustache::MustacheContext Ctx(Allocator, Saver);216 217 llvm::mustache::Template Tpl(HugeArrayIterationTemplate, Ctx);218 for (auto _ : state) {219 std::string Result;220 llvm::raw_string_ostream OS(Result);221 Tpl.render(HugeArrayData, OS);222 benchmark::DoNotOptimize(Result);223 }224}225BENCHMARK(BM_Mustache_HugeArrayIteration);226 227// Tests the performance of the parser on a large, "wide" template.228static void BM_Mustache_ComplexTemplateParsing(benchmark::State &state) {229 llvm::BumpPtrAllocator Allocator;230 llvm::StringSaver Saver(Allocator);231 llvm::mustache::MustacheContext Ctx(Allocator, Saver);232 233 for (auto _ : state) {234 llvm::mustache::Template Tpl(ComplexTemplateParsingTemplate, Ctx);235 benchmark::DoNotOptimize(Tpl);236 }237}238BENCHMARK(BM_Mustache_ComplexTemplateParsing);239 240// Tests the performance of the parser on a small, "deep" template.241static void BM_Mustache_SmallTemplateParsing(benchmark::State &state) {242 llvm::BumpPtrAllocator Allocator;243 llvm::StringSaver Saver(Allocator);244 llvm::mustache::MustacheContext Ctx(Allocator, Saver);245 246 for (auto _ : state) {247 llvm::mustache::Template Tpl(SmallTemplateParsingTemplate, Ctx);248 benchmark::DoNotOptimize(Tpl);249 }250}251BENCHMARK(BM_Mustache_SmallTemplateParsing);252 253// Tests the performance of rendering a template that includes a partial.254static void BM_Mustache_PartialsRendering(benchmark::State &state) {255 llvm::BumpPtrAllocator Allocator;256 llvm::StringSaver Saver(Allocator);257 llvm::mustache::MustacheContext Ctx(Allocator, Saver);258 259 llvm::mustache::Template Tpl(ComplexPartialTemplate, Ctx);260 Tpl.registerPartial("item_partial", ItemPartialTemplate);261 llvm::json::Value Data = HugeArrayData;262 263 for (auto _ : state) {264 std::string Result;265 llvm::raw_string_ostream OS(Result);266 Tpl.render(Data, OS);267 benchmark::DoNotOptimize(Result);268 }269}270BENCHMARK(BM_Mustache_PartialsRendering);271 272// Tests the performance of the underlying buffer management when generating a273// very large output.274static void BM_Mustache_LargeOutputString(benchmark::State &state) {275 llvm::BumpPtrAllocator Allocator;276 llvm::StringSaver Saver(Allocator);277 llvm::mustache::MustacheContext Ctx(Allocator, Saver);278 279 llvm::mustache::Template Tpl(LargeOutputStringTemplate, Ctx);280 for (auto _ : state) {281 std::string Result;282 llvm::raw_string_ostream OS(Result);283 Tpl.render(LargeOutputData, OS);284 benchmark::DoNotOptimize(Result);285 }286}287BENCHMARK(BM_Mustache_LargeOutputString);288 289BENCHMARK_MAIN();290