Improve rendering time
authorNoah Campbell <noahcampbell@gmail.com>
Fri, 1 Nov 2013 05:14:11 +0000 (22:14 -0700)
committerNoah Campbell <noahcampbell@gmail.com>
Fri, 1 Nov 2013 16:59:57 +0000 (09:59 -0700)
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
hugolib/site.go
transform/absurl.go
transform/chain.go
transform/chain_test.go
transform/nav.go
transform/nav_test.go
transform/post.go
transform/posttrans_test.go

index 348ec937d06fcf51b3a4cd8f8fe333658556cb3e..e7eb9232f47781ca5ab8a1b076fb5a4cd1dcdfa5 100644 (file)
@@ -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
index 05561f85ca20287c0ce3140d49f335c379ec4e19..b95a2285380dfab943d6c679e1f140e58fd7dbad 100644 (file)
@@ -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()
index 087aba5b12de5408a8754034cb3de45e0365dcf1..f66edab9d55198b8654aef4f4003ad2063c2e5c7 100644 (file)
@@ -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 (t *AbsURL) Apply(w io.Writer, r io.Reader) (err error) {
-       var tr *htmltran.Transformer
+func AbsURL(absURL string) (trs []*htmltran.Transform, err error) {
+       var baseURL *url.URL
 
-       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
index 1d50e8f2f236396f392846506c68c64030f1798a..a4929b70d8d0da697d040a5b3da3945f9e562f2b 100644 (file)
@@ -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)
 }
index b479ac1e326aa4aaa432001278c781354b097600..594b5a5d41cb2a13a29b03012a72f9f620387b35 100644 (file)
@@ -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)
        }
index 1c9ab1fb64d514508913c695e7556f6421e5442e..7783b6175b33f6a1bbc437b976626186f7fc2f77 100644 (file)
@@ -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
 }
index 8ee46f1169aeab06007861efa6f7a8eda95c2414..372d3f5948939ac6f8627d455d88ba550bc02377 100644 (file)
@@ -31,25 +31,11 @@ const EXPECTED_HTML_WITH_NAV_1 = `<!DOCTYPE html><html><head></head>
 
 </body></html>`
 
-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()
        }
 }
index c9a292b56346dd3f1551c88433ccdeef864fe6a5..5796f91c6ec394a9c7e4834d4241e0cfc065dc4b 100644 (file)
@@ -1,9 +1 @@
 package transform
-
-import (
-       "io"
-)
-
-type Transformer interface {
-       Apply(io.Writer, io.Reader) error
-}
index 103e73a4ae977c914e1a7a7f26db6fed61e0f320..12f7df1f69194cc33555737042e4b8228f705778 100644 (file)
@@ -16,12 +16,9 @@ const H5_JS_CONTENT_ABS_URL = "<!DOCTYPE html><html><head><script src=\"http://u
 const CORRECT_OUTPUT_SRC_HREF = "<!DOCTYPE html><html><head><script src=\"http://base/foobar.js\"></script></head><body><nav><h1>title</h1></nav><article>content <a href=\"http://base/foobar\">foobar</a>. Follow up</article></body></html>"
 
 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))