From 8930e259d78cba4041b550cc51a7f40bc91d7c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 30 May 2017 10:01:54 +0300 Subject: [PATCH] hugolib: Add site building benchmarks To run a subset of these benchmark, one can do something like this: ``` go test -run="NONE" -bench="BenchmarkSiteBuilding/tags_per_page=0.*shortcodes=true.*render=false" -test.benchmem=true ./hugolib ``` Which will run without any tags, with shortcodes, but will skip rendering. Fixes #3535 --- hugolib/site_benchmark_test.go | 182 +++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 hugolib/site_benchmark_test.go diff --git a/hugolib/site_benchmark_test.go b/hugolib/site_benchmark_test.go new file mode 100644 index 000000000..1bb1f9206 --- /dev/null +++ b/hugolib/site_benchmark_test.go @@ -0,0 +1,182 @@ +// Copyright 2017-present The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hugolib + +import ( + "fmt" + "math/rand" + "path/filepath" + "strings" + "testing" + + "github.com/spf13/afero" +) + +type siteBuildingBenchmarkConfig struct { + NumPages int + RootSections int + Render bool + Shortcodes bool + TagsPerPage int +} + +func (s siteBuildingBenchmarkConfig) String() string { + return fmt.Sprintf("num_root_sections=%d|num_pages=%d|tags_per_page=%d|shortcodes=%t|render=%t", s.RootSections, s.NumPages, s.TagsPerPage, s.Shortcodes, s.Render) +} + +func BenchmarkSiteBuilding(b *testing.B) { + var conf siteBuildingBenchmarkConfig + for _, rootSections := range []int{1, 5} { + conf.RootSections = rootSections + for _, tagsPerPage := range []int{0, 1, 5, 20} { + conf.TagsPerPage = tagsPerPage + for _, numPages := range []int{10, 100, 500, 1000, 5000} { + conf.NumPages = numPages + for _, render := range []bool{false, true} { + conf.Render = render + for _, shortcodes := range []bool{false, true} { + conf.Shortcodes = shortcodes + doBenchMarkSiteBuilding(conf, b) + } + } + } + } + } +} + +func doBenchMarkSiteBuilding(conf siteBuildingBenchmarkConfig, b *testing.B) { + b.Run(conf.String(), func(b *testing.B) { + sites := createHugoBenchmarkSites(b, b.N, conf) + b.ResetTimer() + for i := 0; i < b.N; i++ { + h := sites[0] + + err := h.Build(BuildCfg{SkipRender: !conf.Render}) + if err != nil { + b.Fatal(err) + } + + // Try to help the GC + sites[0] = nil + sites = sites[1:len(sites)] + } + }) +} + +func createHugoBenchmarkSites(b *testing.B, count int, cfg siteBuildingBenchmarkConfig) []*HugoSites { + someMarkdown := ` +An h1 header +============ + +Paragraphs are separated by a blank line. + +2nd paragraph. *Italic* and **bold**. Itemized lists +look like: + + * this one + * that one + * the other one + +Note that --- not considering the asterisk --- the actual text +content starts at 4-columns in. + +> Block quotes are +> written like so. +> +> They can span multiple paragraphs, +> if you like. + +Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all +in chapters 12--14"). Three dots ... will be converted to an ellipsis. +Unicode is supported. ☺ +` + + someMarkdownWithShortCode := someMarkdown + ` + +{{< myShortcode >}} + +` + + pageTemplate := `+++ +title = "%s" +tags = %s ++++ +%s + +` + + siteConfig := ` +baseURL = "http://example.com/blog" + +paginate = 10 +defaultContentLanguage = "en" + +[Taxonomies] +tag = "tags" +category = "categories" +` + var ( + contentPagesContent [3]string + tags = make([]string, cfg.TagsPerPage) + ) + + for i := 0; i < len(tags); i++ { + tags[i] = fmt.Sprintf("Hugo %d", i) + } + + tagsStr := "[]" + if cfg.TagsPerPage > 0 { + tagsStr = strings.Replace(fmt.Sprintf("%q", tags[0:cfg.TagsPerPage]), " ", ", ", -1) + } + + if cfg.Shortcodes { + contentPagesContent = [3]string{ + someMarkdownWithShortCode, + strings.Repeat(someMarkdownWithShortCode, 2), + strings.Repeat(someMarkdownWithShortCode, 3), + } + } else { + contentPagesContent = [3]string{ + someMarkdown, + strings.Repeat(someMarkdown, 2), + strings.Repeat(someMarkdown, 3), + } + } + + sites := make([]*HugoSites, count) + for i := 0; i < count; i++ { + // Maybe consider reusing the Source fs + mf := afero.NewMemMapFs() + th, h := newTestSitesFromConfig(b, mf, siteConfig, + "layouts/_default/single.html", `Single HTML|{{ .Title }}|{{ .Content }}`, + "layouts/_default/list.html", `List HTML|{{ .Title }}|{{ .Content }}`, + "layouts/shortcodes/myShortcode.html", `

MyShortcode

`) + + fs := th.Fs + + pagesPerSection := cfg.NumPages / cfg.RootSections + + for i := 0; i < cfg.RootSections; i++ { + for j := 0; j < pagesPerSection; j++ { + content := fmt.Sprintf(pageTemplate, fmt.Sprintf("Title%d_%d", i, j), tagsStr, contentPagesContent[rand.Intn(3)]) + + writeSource(b, fs, filepath.Join("content", fmt.Sprintf("sect%d", i), fmt.Sprintf("page%d.md", j)), content) + } + } + + sites[i] = h + } + + return sites +}