[ANN] replacer - fast and simple templating for go

575 views
Skip to first unread message

meta keule

unread,
Sep 26, 2013, 9:53:31 AM9/26/13
to golan...@googlegroups.com

Hi,

I have written a very simple but fast solution for the situation where you simply

want to replace placeholders in a template without escaping or logic.

For the typical scenario - your template never changes on runtime -,

replacer is faster than using (strings|bytes).Replace() or regexp.ReplaceAllStringFunc()

or the text/template package.

Runing benchmarks in the benchmark directory, I get the following results:

replacing 2 placeholders that occur 2500x in the template

BenchmarkNaive      500    6112228 ns/op  3,7x (strings.Replace)
BenchmarkReg         50   53740939 ns/op 32,8x (regexp.ReplaceAllStringFunc)
BenchmarkByte       500    4627244 ns/op  2,8x (bytes.Replace)
BenchmarkTemplate   100   13838150 ns/op  8,4x (template.Execute)
BenchmarkReplacer  1000    1640622 ns/op  1,0x (replacer.Replace)


replacing 5000 placeholders that occur 1x in the template

BenchmarkNaiveM        1   8663141464 ns/op 3941,1x (strings.Replace)
BenchmarkRegM         50     63944139 ns/op   29,1x (regexp.ReplaceAllStringFunc)
BenchmarkByteM         1   5955402986 ns/op 2709,3x (bytes.Replace)
BenchmarkTemplateM   100     13903383 ns/op    6,3x (template.Execute)
BenchmarkReplacerM  1000      2198166 ns/op    1,0x (replacer.Replace)
 

replacing 2 placeholders that occur 1x in the template, parsing template each time (you should not do this)

BenchmarkOnceNaive     500 ns/op 6145737      1,2x (strings.Replace)
BenchmarkOnceReg        50 ns/op 54311308    11,0x (regexp.ReplaceAllStringFunc)
BenchmarkOnceByte      500 ns/op 4950499      1,0x (bytes.Replace)
BenchmarkOnceTemplate    1 ns/op 2066008619 417,3x (template.Execute)
BenchmarkOnceReplacer  200 ns/op 8725035      1,8x (replacer.Replace)


- metakeule

meta keule

unread,
Sep 26, 2013, 9:55:49 AM9/26/13
to golan...@googlegroups.com

roger peppe

unread,
Sep 26, 2013, 11:06:49 AM9/26/13
to meta keule, golang-nuts
Are you aware of strings.Replacer?
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

meta keule

unread,
Sep 26, 2013, 12:17:40 PM9/26/13
to roger peppe, golang-nuts
As far as I can see there is not strings.Replacer, but only strings.Replace.
If you look at my announcement, there I benchmark against strings.Replace
which is a lot slower for the use case of a reused template.

Therefore the lib.


2013/9/26 roger peppe <rogp...@gmail.com>

Kamil Kisiel

unread,
Sep 26, 2013, 12:25:19 PM9/26/13
to golan...@googlegroups.com, marcre...@googlemail.com

meta keule

unread,
Sep 26, 2013, 12:47:25 PM9/26/13
to Kamil Kisiel, golang-nuts
Oh, yes thank... is was at the bottom of the page, so I did not see it...

Well if I get it right, the use case for strings.Replacer is completely inverse to what my lib does.

With 

     s := strings.NewReplacer("a", "A", "b", "B") 

you first define a set of strings that should always be replaced.
Then you can call

     s1 := s.Replace("a-b")
     s2 := s.Replace("bbbaaaa")
     ...

several times to do the replacements in different templates.

What my library does, is the following:

You define are template with different placeholders and parse it once:

     r := replacer.New()
     r.Parse([]byte(`@@firstname@@ @@lastname@@`)

Then you reuse the parsed template to replace the placeholders with different values each time:

    var b1, b2 bytes.Buffer
    r.Replace(map[string]string{"firstname": "Donald", "lastname": "Duck"}, &b1)
    r.Replace(map[string]string{"firstname": "Mickey", "lastname": "Mouse"}, &b2)
    ...

So strings.Replacer should not perform better than strings.Replace() in the benchmark.


2013/9/26 Kamil Kisiel <kamil....@gmail.com>

meta keule

unread,
Sep 26, 2013, 1:21:34 PM9/26/13
to Kamil Kisiel, golang-nuts

So, I did some new benchmarks (they on github as well).

To my surprise strings.Replacer is faster than strings.Replace everywhere,

but is still much slower than my new library...


replacing 2 placeholders that occur 2500x in the template

BenchmarkNaive      500    3035929 ns/op  3,8x (strings.Replace)
BenchmarkNaive2    1000    2595076 ns/op  3,2x (strings.Replacer)
BenchmarkReg        100   25882258 ns/op 32,3x (regexp.ReplaceAllStringFunc)
BenchmarkByte      1000    2210725 ns/op  2,8x (bytes.Replace)
BenchmarkTemplate   500    6373070 ns/op  7,9x (template.Execute)
BenchmarkReplacer  2000     802490 ns/op  1,0x (replacer.Replace)

replacing 5000 placeholders that occur 1x in the template

BenchmarkNaiveM        1   4317513185 ns/op 3929,6x (strings.Replace)
BenchmarkNaive2M     500      6329720 ns/op    5,8x (strings.Replacer)
BenchmarkRegM         50     31198202 ns/op   28,4x (regexp.ReplaceAllStringFunc)
BenchmarkByteM      1000      1475455 ns/op    1,3x (bytes.Replace)
BenchmarkTemplateM   500      6435381 ns/op    5,9x (template.Execute)
BenchmarkReplacerM  2000      1098709 ns/op    1,0x (replacer.Replace)




2013/9/26 Kamil Kisiel <kamil....@gmail.com>
Reply all
Reply to author
Forward
0 new messages