github.com/fighterlyt/hugo@v0.47.1/hugolib/site_benchmark_test.go (about) 1 // Copyright 2017-present The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package hugolib 15 16 import ( 17 "flag" 18 "fmt" 19 "math/rand" 20 "path/filepath" 21 "strings" 22 "testing" 23 24 "github.com/spf13/afero" 25 ) 26 27 type siteBuildingBenchmarkConfig struct { 28 Frontmatter string 29 NumPages int 30 NumLangs int 31 RootSections int 32 Render bool 33 Shortcodes bool 34 NumTags int 35 TagsPerPage int 36 } 37 38 func (s siteBuildingBenchmarkConfig) String() string { 39 // Make it comma separated with no spaces, so it is both Bash and regexp friendly. 40 // To make it a short as possible, we only shows bools when enabled and ints when >= 0 (RootSections > 1) 41 sep := "," 42 id := s.Frontmatter + sep 43 id += fmt.Sprintf("num_langs=%d%s", s.NumLangs, sep) 44 45 if s.RootSections > 1 { 46 id += fmt.Sprintf("num_root_sections=%d%s", s.RootSections, sep) 47 } 48 id += fmt.Sprintf("num_pages=%d%s", s.NumPages, sep) 49 50 if s.NumTags > 0 { 51 id += fmt.Sprintf("num_tags=%d%s", s.NumTags, sep) 52 } 53 54 if s.TagsPerPage > 0 { 55 id += fmt.Sprintf("tags_per_page=%d%s", s.TagsPerPage, sep) 56 } 57 58 if s.Shortcodes { 59 id += "shortcodes" + sep 60 } 61 62 if s.Render { 63 id += "render" + sep 64 } 65 66 return strings.TrimSuffix(id, sep) 67 } 68 69 var someLangs = []string{"en", "fr", "nn"} 70 71 func BenchmarkSiteBuilding(b *testing.B) { 72 var ( 73 // The below represents the full matrix of benchmarks. Big! 74 allFrontmatters = []string{"YAML", "TOML"} 75 allNumRootSections = []int{1, 5} 76 allNumTags = []int{0, 1, 10, 20, 50, 100, 500, 1000, 5000} 77 allTagsPerPage = []int{0, 1, 5, 20, 50, 80} 78 allNumPages = []int{1, 10, 100, 500, 1000, 5000, 10000} 79 allDoRender = []bool{false, true} 80 allDoShortCodes = []bool{false, true} 81 allNumLangs = []int{1, 3} 82 ) 83 84 var runDefault bool 85 86 visitor := func(a *flag.Flag) { 87 if a.Name == "test.bench" && len(a.Value.String()) < 40 { 88 // The full suite is too big, so fall back to some smaller default if no 89 // restriction is set. 90 runDefault = true 91 } 92 } 93 94 flag.Visit(visitor) 95 96 if runDefault { 97 allFrontmatters = allFrontmatters[1:] 98 allNumRootSections = allNumRootSections[0:2] 99 allNumTags = allNumTags[0:2] 100 allTagsPerPage = allTagsPerPage[2:3] 101 allNumPages = allNumPages[2:5] 102 allDoRender = allDoRender[1:2] 103 allDoShortCodes = allDoShortCodes[1:2] 104 } 105 106 var conf siteBuildingBenchmarkConfig 107 for _, numLangs := range allNumLangs { 108 conf.NumLangs = numLangs 109 for _, frontmatter := range allFrontmatters { 110 conf.Frontmatter = frontmatter 111 for _, rootSections := range allNumRootSections { 112 conf.RootSections = rootSections 113 for _, numTags := range allNumTags { 114 conf.NumTags = numTags 115 for _, tagsPerPage := range allTagsPerPage { 116 conf.TagsPerPage = tagsPerPage 117 for _, numPages := range allNumPages { 118 conf.NumPages = numPages 119 for _, render := range allDoRender { 120 conf.Render = render 121 for _, shortcodes := range allDoShortCodes { 122 conf.Shortcodes = shortcodes 123 doBenchMarkSiteBuilding(conf, b) 124 } 125 } 126 } 127 } 128 } 129 } 130 } 131 } 132 } 133 134 func doBenchMarkSiteBuilding(conf siteBuildingBenchmarkConfig, b *testing.B) { 135 b.Run(conf.String(), func(b *testing.B) { 136 b.StopTimer() 137 sites := createHugoBenchmarkSites(b, b.N, conf) 138 b.StartTimer() 139 for i := 0; i < b.N; i++ { 140 h := sites[0] 141 142 err := h.Build(BuildCfg{SkipRender: !conf.Render}) 143 if err != nil { 144 b.Fatal(err) 145 } 146 147 // Try to help the GC 148 sites[0] = nil 149 sites = sites[1:] 150 } 151 }) 152 } 153 154 func createHugoBenchmarkSites(b *testing.B, count int, cfg siteBuildingBenchmarkConfig) []*HugoSites { 155 someMarkdown := ` 156 An h1 header 157 ============ 158 159 Paragraphs are separated by a blank line. 160 161 2nd paragraph. *Italic* and **bold**. Itemized lists 162 look like: 163 164 * this one 165 * that one 166 * the other one 167 168 Note that --- not considering the asterisk --- the actual text 169 content starts at 4-columns in. 170 171 > Block quotes are 172 > written like so. 173 > 174 > They can span multiple paragraphs, 175 > if you like. 176 177 Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all 178 in chapters 12--14"). Three dots ... will be converted to an ellipsis. 179 Unicode is supported. ☺ 180 ` 181 182 someMarkdownWithShortCode := someMarkdown + ` 183 184 {{< myShortcode >}} 185 186 ` 187 188 pageTemplateTOML := `+++ 189 title = "%s" 190 tags = %s 191 +++ 192 %s 193 194 ` 195 196 pageTemplateYAML := `--- 197 title: "%s" 198 tags: 199 %s 200 --- 201 %s 202 203 ` 204 205 siteConfig := ` 206 baseURL = "http://example.com/blog" 207 208 paginate = 10 209 defaultContentLanguage = "en" 210 211 [outputs] 212 home = [ "HTML" ] 213 section = [ "HTML" ] 214 taxonomy = [ "HTML" ] 215 taxonomyTerm = [ "HTML" ] 216 page = [ "HTML" ] 217 218 [languages] 219 %s 220 221 [Taxonomies] 222 tag = "tags" 223 category = "categories" 224 ` 225 226 langConfigTemplate := ` 227 [languages.%s] 228 languageName = "Lang %s" 229 weight = %d 230 ` 231 232 langConfig := "" 233 234 for i := 0; i < cfg.NumLangs; i++ { 235 langCode := someLangs[i] 236 langConfig += fmt.Sprintf(langConfigTemplate, langCode, langCode, i+1) 237 } 238 239 siteConfig = fmt.Sprintf(siteConfig, langConfig) 240 241 numTags := cfg.NumTags 242 243 if cfg.TagsPerPage > numTags { 244 numTags = cfg.TagsPerPage 245 } 246 247 var ( 248 contentPagesContent [3]string 249 tags = make([]string, numTags) 250 pageTemplate string 251 ) 252 253 for i := 0; i < numTags; i++ { 254 tags[i] = fmt.Sprintf("Hugo %d", i+1) 255 } 256 257 var tagsStr string 258 259 if cfg.Shortcodes { 260 contentPagesContent = [3]string{ 261 someMarkdownWithShortCode, 262 strings.Repeat(someMarkdownWithShortCode, 2), 263 strings.Repeat(someMarkdownWithShortCode, 3), 264 } 265 } else { 266 contentPagesContent = [3]string{ 267 someMarkdown, 268 strings.Repeat(someMarkdown, 2), 269 strings.Repeat(someMarkdown, 3), 270 } 271 } 272 273 sites := make([]*HugoSites, count) 274 for i := 0; i < count; i++ { 275 // Maybe consider reusing the Source fs 276 mf := afero.NewMemMapFs() 277 th, h := newTestSitesFromConfig(b, mf, siteConfig, 278 "layouts/_default/single.html", `Single HTML|{{ .Title }}|{{ .Content }}|{{ partial "myPartial" . }}`, 279 "layouts/_default/list.html", `List HTML|{{ .Title }}|{{ .Content }}|GetPage: {{ with .Site.GetPage "page" "sect3/page3.md" }}{{ .Title }}{{ end }}`, 280 "layouts/partials/myPartial.html", `Partial: {{ "Hello **world**!" | markdownify }}`, 281 "layouts/shortcodes/myShortcode.html", `<p>MyShortcode</p>`) 282 283 fs := th.Fs 284 285 pagesPerSection := cfg.NumPages / cfg.RootSections / cfg.NumLangs 286 for li := 0; li < cfg.NumLangs; li++ { 287 fileLangCodeID := "" 288 if li > 0 { 289 fileLangCodeID = "." + someLangs[li] + "." 290 } 291 292 for i := 0; i < cfg.RootSections; i++ { 293 for j := 0; j < pagesPerSection; j++ { 294 var tagsSlice []string 295 296 if numTags > 0 { 297 tagsStart := rand.Intn(numTags) - cfg.TagsPerPage 298 if tagsStart < 0 { 299 tagsStart = 0 300 } 301 tagsSlice = tags[tagsStart : tagsStart+cfg.TagsPerPage] 302 } 303 304 if cfg.Frontmatter == "TOML" { 305 pageTemplate = pageTemplateTOML 306 tagsStr = "[]" 307 if cfg.TagsPerPage > 0 { 308 tagsStr = strings.Replace(fmt.Sprintf("%q", tagsSlice), " ", ", ", -1) 309 } 310 } else { 311 // YAML 312 pageTemplate = pageTemplateYAML 313 for _, tag := range tagsSlice { 314 tagsStr += "\n- " + tag 315 } 316 } 317 318 content := fmt.Sprintf(pageTemplate, fmt.Sprintf("Title%d_%d", i, j), tagsStr, contentPagesContent[rand.Intn(3)]) 319 320 contentFilename := fmt.Sprintf("page%d%s.md", j, fileLangCodeID) 321 322 writeSource(b, fs, filepath.Join("content", fmt.Sprintf("sect%d", i), contentFilename), content) 323 } 324 325 content := fmt.Sprintf(pageTemplate, fmt.Sprintf("Section %d", i), "[]", contentPagesContent[rand.Intn(3)]) 326 indexContentFilename := fmt.Sprintf("_index%s.md", fileLangCodeID) 327 writeSource(b, fs, filepath.Join("content", fmt.Sprintf("sect%d", i), indexContentFilename), content) 328 } 329 } 330 331 sites[i] = h 332 } 333 334 return sites 335 }