From 9af47f07d3f9700e233a98d7e84d85c3a30f9cd5 Mon Sep 17 00:00:00 2001 From: Noah Campbell Date: Thu, 31 Oct 2013 22:14:11 -0700 Subject: [PATCH] Improve rendering time 50% speedup. Fix #91 to run the benchmark: go test -test.run=NONE -bench=".*" -test.benchmem=true ./transform/ > new.txt to compare the results: /usr/local/go/misc/benchcmp baseline.txt new.txt Speedup and memory improvements benchmark old ns/op new ns/op delta BenchmarkChain 101219 50453 -50.15% BenchmarkTransform 51625 45531 -11.80% benchmark old allocs new allocs delta BenchmarkChain 222 103 -53.60% BenchmarkTransform 135 106 -21.48% benchmark old bytes new bytes delta BenchmarkChain 23919 10998 -54.02% BenchmarkTransform 11858 10665 -10.06% --- baseline.txt | 6 +++--- hugolib/site.go | 10 +++++++--- transform/absurl.go | 29 +++++++++-------------------- transform/chain.go | 28 ++++++++++++---------------- transform/chain_test.go | 19 ++++++++++--------- transform/nav.go | 32 ++++---------------------------- transform/nav_test.go | 28 ++++++++-------------------- transform/post.go | 8 -------- transform/posttrans_test.go | 13 +++++-------- 9 files changed, 58 insertions(+), 115 deletions(-) diff --git a/baseline.txt b/baseline.txt index 348ec937d..e7eb9232f 100644 --- a/baseline.txt +++ b/baseline.txt @@ -1,4 +1,4 @@ PASS -BenchmarkChain 10000 101219 ns/op 23919 B/op 222 allocs/op -BenchmarkTransform 50000 51625 ns/op 11858 B/op 135 allocs/op -ok github.com/spf13/hugo/transform 4.172s +BenchmarkChain 50000 50453 ns/op 10998 B/op 103 allocs/op +BenchmarkTransform 50000 45531 ns/op 10665 B/op 106 allocs/op +ok github.com/spf13/hugo/transform 5.904s diff --git a/hugolib/site.go b/hugolib/site.go index 05561f85c..b95a22853 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -64,7 +64,6 @@ type Site struct { Info SiteInfo Shortcodes map[string]ShortcodeFunc timer *nitro.B - Transformer transform.Transformer Target target.Output Alias target.AliasPublisher Completed chan bool @@ -581,9 +580,14 @@ func (s *Site) render(d interface{}, out string, layouts ...string) (err error) section, _ = page.RelPermalink() } + + absURL, err := transform.AbsURL(s.Config.BaseUrl) + if err != nil { + return + } + transformer := transform.NewChain( - &transform.AbsURL{BaseURL: s.Config.BaseUrl}, - &transform.NavActive{Section: section}, + append(absURL, transform.NavActive(section, "hugo-nav")...)... ) renderReader, renderWriter := io.Pipe() diff --git a/transform/absurl.go b/transform/absurl.go index 087aba5b1..f66edab9d 100644 --- a/transform/absurl.go +++ b/transform/absurl.go @@ -2,38 +2,28 @@ package transform import ( htmltran "code.google.com/p/go-html-transform/html/transform" - "io" "net/url" ) -type AbsURL struct { - BaseURL string -} +func AbsURL(absURL string) (trs []*htmltran.Transform, err error) { + var baseURL *url.URL -func (t *AbsURL) Apply(w io.Writer, r io.Reader) (err error) { - var tr *htmltran.Transformer - - if tr, err = htmltran.NewFromReader(r); err != nil { + if baseURL, err = url.Parse(absURL); err != nil { return } - if err = t.absUrlify(tr, elattr{"a", "href"}, elattr{"script", "src"}); err != nil { + if trs, err = absUrlify(baseURL, elattr{"a", "href"}, elattr{"script", "src"}); err != nil { return } - - return tr.Render(w) + return } type elattr struct { tag, attr string } -func (t *AbsURL) absUrlify(tr *htmltran.Transformer, selectors ...elattr) (err error) { - var baseURL, inURL *url.URL - - if baseURL, err = url.Parse(t.BaseURL); err != nil { - return - } +func absUrlify(baseURL *url.URL, selectors ...elattr) (trs []*htmltran.Transform, err error) { + var inURL *url.URL replace := func(in string) string { if inURL, err = url.Parse(in); err != nil { @@ -46,9 +36,8 @@ func (t *AbsURL) absUrlify(tr *htmltran.Transformer, selectors ...elattr) (err e } for _, el := range selectors { - if err = tr.Apply(htmltran.TransformAttrib(el.attr, replace), el.tag); err != nil { - return - } + mt := htmltran.MustTrans(htmltran.TransformAttrib(el.attr, replace), el.tag) + trs = append(trs, mt) } return diff --git a/transform/chain.go b/transform/chain.go index 1d50e8f2f..a4929b70d 100644 --- a/transform/chain.go +++ b/transform/chain.go @@ -1,29 +1,25 @@ package transform import ( - "bytes" + htmltran "code.google.com/p/go-html-transform/html/transform" "io" ) -type chain struct { - transformers []Transformer -} +type chain []*htmltran.Transform -func NewChain(trs ...Transformer) Transformer { - return &chain{transformers: trs} +func NewChain(trs ...*htmltran.Transform) chain { + return trs } func (c *chain) Apply(w io.Writer, r io.Reader) (err error) { - in := r - for _, tr := range c.transformers { - out := new(bytes.Buffer) - err = tr.Apply(out, in) - if err != nil { - return - } - in = bytes.NewBuffer(out.Bytes()) + + var tr *htmltran.Transformer + + if tr, err = htmltran.NewFromReader(r); err != nil { + return } - _, err = io.Copy(w, in) - return + tr.ApplyAll(*c...) + + return tr.Render(w) } diff --git a/transform/chain_test.go b/transform/chain_test.go index b479ac1e3..594b5a5d4 100644 --- a/transform/chain_test.go +++ b/transform/chain_test.go @@ -15,7 +15,8 @@ func TestChainZeroTransformers(t *testing.T) { } func TestChainOneTransformer(t *testing.T) { - tr := NewChain(&AbsURL{BaseURL: "http://base"}) + absURL, _ := AbsURL("http://base") + tr := NewChain(absURL...) apply(t.Errorf, tr, abs_url_tests) } @@ -28,19 +29,19 @@ var two_chain_tests = []test{ } func TestChainTwoTransformer(t *testing.T) { - tr := NewChain( - &AbsURL{BaseURL: "http://two"}, - &NavActive{Section: "section_1"}, - ) + absURL, _ := AbsURL("http://two") + nav := NavActive("section_1", "hugo-nav") + tr := NewChain(append(absURL, nav...)...) apply(t.Errorf, tr, two_chain_tests) } func BenchmarkChain(b *testing.B) { - tr := NewChain( - &AbsURL{BaseURL: "http://two"}, - &NavActive{Section: "section_1"}, - ) + absURL, _ := AbsURL("http://two") + nav := NavActive("section_1", "hugo-nav") + tr := NewChain(append(absURL, nav...)...) + + b.ResetTimer() for i := 0; i < b.N; i++ { apply(b.Errorf, tr, two_chain_tests) } diff --git a/transform/nav.go b/transform/nav.go index 1c9ab1fb6..7783b6175 100644 --- a/transform/nav.go +++ b/transform/nav.go @@ -3,34 +3,10 @@ package transform import ( htmltran "code.google.com/p/go-html-transform/html/transform" "fmt" - "io" ) -type NavActive struct { - Section string - AttrName string -} - -func (n *NavActive) Apply(w io.Writer, r io.Reader) (err error) { - var tr *htmltran.Transformer - - if n.Section == "" { - _, err = io.Copy(w, r) - return - } - - if tr, err = htmltran.NewFromReader(r); err != nil { - return - } - - if n.AttrName == "" { - n.AttrName = "hugo-nav" - } - - err = tr.Apply(htmltran.ModifyAttrib("class", "active"), fmt.Sprintf("li[%s=%s]", n.AttrName, n.Section)) - if err != nil { - return - } - - return tr.Render(w) +func NavActive(section, attrName string) (tr []*htmltran.Transform) { + ma := htmltran.MustTrans(htmltran.ModifyAttrib("class", "active"), fmt.Sprintf("li[%s=%s]", attrName, section)) + tr = append(tr, ma) + return } diff --git a/transform/nav_test.go b/transform/nav_test.go index 8ee46f116..372d3f594 100644 --- a/transform/nav_test.go +++ b/transform/nav_test.go @@ -31,25 +31,11 @@ const EXPECTED_HTML_WITH_NAV_1 = ` ` -func TestDegenerateNoSectionSet(t *testing.T) { - var ( - tr = new(NavActive) - out = new(bytes.Buffer) - ) - - if err := tr.Apply(out, strings.NewReader(HTML_WITH_NAV)); err != nil { - t.Errorf("Unexpected error in NavActive.Apply: %s", err) - } - - if out.String() != HTML_WITH_NAV { - t.Errorf("NavActive.Apply should simply pass along the buffer unmodified.") - } -} - func TestSetNav(t *testing.T) { - tr := &NavActive{Section: "section_2"} + trs := NavActive("section_2", "hugo-nav") + chain := NewChain(trs...) out := new(bytes.Buffer) - if err := tr.Apply(out, strings.NewReader(HTML_WITH_NAV)); err != nil { + if err := chain.Apply(out, strings.NewReader(HTML_WITH_NAV)); err != nil { t.Errorf("Unexpected error in Apply() for NavActive: %s", err) } @@ -60,11 +46,13 @@ func TestSetNav(t *testing.T) { } func BenchmarkTransform(b *testing.B) { + tr := NavActive("section_2", "hugo-nav") + chain := NewChain(tr...) + out := new(bytes.Buffer) for i := 0; i < b.N; i++ { - tr := &NavActive{Section: "section_2"} - out := new(bytes.Buffer) - if err := tr.Apply(out, strings.NewReader(HTML_WITH_NAV)); err != nil { + if err := chain.Apply(out, strings.NewReader(HTML_WITH_NAV)); err != nil { b.Errorf("Unexpected error in Apply() for NavActive: %s", err) } + out.Reset() } } diff --git a/transform/post.go b/transform/post.go index c9a292b56..5796f91c6 100644 --- a/transform/post.go +++ b/transform/post.go @@ -1,9 +1 @@ package transform - -import ( - "io" -) - -type Transformer interface { - Apply(io.Writer, io.Reader) error -} diff --git a/transform/posttrans_test.go b/transform/posttrans_test.go index 103e73a4a..12f7df1f6 100644 --- a/transform/posttrans_test.go +++ b/transform/posttrans_test.go @@ -16,12 +16,9 @@ const H5_JS_CONTENT_ABS_URL = "
content foobar. Follow up
" func TestAbsUrlify(t *testing.T) { - - tr := &AbsURL{ - BaseURL: "http://base", - } - - apply(t.Errorf, tr, abs_url_tests) + tr, _ := AbsURL("http://base") + chain := NewChain(tr...) + apply(t.Errorf, chain, abs_url_tests) } type test struct { @@ -35,9 +32,9 @@ var abs_url_tests = []test{ {H5_JS_CONTENT_ABS_URL, H5_JS_CONTENT_ABS_URL}, } -type errorf func (string, ...interface{}) +type errorf func(string, ...interface{}) -func apply(ef errorf, tr Transformer, tests []test) { +func apply(ef errorf, tr chain, tests []test) { for _, test := range tests { out := new(bytes.Buffer) err := tr.Apply(out, strings.NewReader(test.content))