Skip to content

Commit

Permalink
Remove the WithWorkers option
Browse files Browse the repository at this point in the history
Situations that concurrent inline parsing is effective are very limited
due to goroutine overheads and a parse context sharing mutex.
  • Loading branch information
yuin committed Oct 31, 2019
1 parent 2184586 commit 16b6952
Show file tree
Hide file tree
Showing 7 changed files with 28 additions and 259 deletions.
32 changes: 10 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,14 @@ With options

```go
var buf bytes.Buffer
if err := goldmark.Convert(source, &buf, parser.WithWorkers(16)); err != nil {
if err := goldmark.Convert(source, &buf, parser.WithContext(ctx)); err != nil {
panic(err)
}
```

| Functional option | Type | Description |
| ----------------- | ---- | ----------- |
| `parser.WithContext` | A parser.Context | Context for the parsing phase. |
| parser.WithWorkers | int | Number of goroutines that execute concurrent inline element parsing. |

`parser.WithWorkers` may make performance better a little if markdown text
is relatively large. Otherwise, `parser.Workers` may cause performance degradation due to
goroutine overheads.

Custom parser and renderer
--------------------------
Expand Down Expand Up @@ -255,19 +250,15 @@ blackfriday v2 can not simply be compared with other Commonmark compliant librar
Though goldmark builds clean extensible AST structure and get full compliance with
Commonmark, it is resonably fast and less memory consumption.

This benchmark parses a relatively large markdown text. In such text, concurrent parsing
makes performance better a little.

```
goos: windows
goos: darwin
goarch: amd64
pkg: github.com/yuin/goldmark/_benchmark/go
BenchmarkMarkdown/Blackfriday-v2-4 200 6199986 ns/op 3320027 B/op 20050 allocs/op
BenchmarkMarkdown/GoldMark(workers=16)-4 300 5655736 ns/op 2700250 B/op 14494 allocs/op
BenchmarkMarkdown/GoldMark-4 200 6501805 ns/op 2594488 B/op 13861 allocs/op
BenchmarkMarkdown/CommonMark-4 200 7803784 ns/op 2752553 B/op 18826 allocs/op
BenchmarkMarkdown/Lute-4 200 6920985 ns/op 2984762 B/op 21270 allocs/op
BenchmarkMarkdown/GoMarkdown-4 10 171046030 ns/op 2195980 B/op 22174 allocs/op
BenchmarkMarkdown/Blackfriday-v2-12 337 3407336 ns/op 3261042 B/op 19862 allocs/op
BenchmarkMarkdown/GoldMark-12 302 3947527 ns/op 2574830 B/op 13853 allocs/op
BenchmarkMarkdown/CommonMark-12 249 4784221 ns/op 2739317 B/op 18824 allocs/op
BenchmarkMarkdown/Lute-12 285 4178276 ns/op 4639751 B/op 26665 allocs/op
BenchmarkMarkdown/GoMarkdown-12 9 114246204 ns/op 2175131 B/op 22172 allocs/op
```

### against cmark(A CommonMark reference implementation written in c)
Expand All @@ -276,15 +267,12 @@ BenchmarkMarkdown/GoMarkdown-4 10 171046030 ns/op
----------- cmark -----------
file: _data.md
iteration: 50
average: 0.0047014618 sec
average: 0.0037760639 sec
go run ./goldmark_benchmark.go
------- goldmark -------
file: _data.md
iteration: 50
average: 0.0052624750 sec
------- goldmark(workers=16) -------
file: _data.md
iteration: 50
average: 0.0044918780 sec
average: 0.0040964230 sec
```

As you can see, goldmark performs pretty much equally to the cmark.
Expand Down
15 changes: 0 additions & 15 deletions _benchmark/cmark/goldmark_benchmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"time"

"github.com/yuin/goldmark"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
)

Expand Down Expand Up @@ -43,18 +42,4 @@ func main() {
fmt.Printf("file: %s\n", file)
fmt.Printf("iteration: %d\n", n)
fmt.Printf("average: %.10f sec\n", float64((int64(sum)/int64(n)))/1000000000.0)

sum = time.Duration(0)
for i := 0; i < n; i++ {
start := time.Now()
out.Reset()
if err := markdown.Convert(source, &out, parser.WithWorkers(16)); err != nil {
panic(err)
}
sum += time.Since(start)
}
fmt.Printf("------- goldmark(workers=16) -------\n")
fmt.Printf("file: %s\n", file)
fmt.Printf("iteration: %d\n", n)
fmt.Printf("average: %.10f sec\n", float64((int64(sum)/int64(n)))/1000000000.0)
}
39 changes: 10 additions & 29 deletions _benchmark/go/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,16 @@ import (

gomarkdown "github.com/gomarkdown/markdown"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/util"
"gitlab.com/golang-commonmark/markdown"

bf1 "github.com/russross/blackfriday"
bf2 "gopkg.in/russross/blackfriday.v2"

"github.com/b3log/lute"
)

func BenchmarkMarkdown(b *testing.B) {
b.Run("Blackfriday-v1", func(b *testing.B) {
r := func(src []byte) ([]byte, error) {
out := bf1.MarkdownBasic(src)
return out, nil
}
doBenchmark(b, r)
})

b.Run("Blackfriday-v2", func(b *testing.B) {
r := func(src []byte) ([]byte, error) {
out := bf2.Run(src)
Expand All @@ -35,25 +25,13 @@ func BenchmarkMarkdown(b *testing.B) {
doBenchmark(b, r)
})

b.Run("GoldMark(workers=16)", func(b *testing.B) {
markdown := goldmark.New(
goldmark.WithRendererOptions(html.WithXHTML(), html.WithUnsafe()),
)
r := func(src []byte) ([]byte, error) {
var out bytes.Buffer
err := markdown.Convert(src, &out, parser.WithWorkers(16))
return out.Bytes(), err
}
doBenchmark(b, r)
})

b.Run("GoldMark", func(b *testing.B) {
markdown := goldmark.New(
goldmark.WithRendererOptions(html.WithXHTML(), html.WithUnsafe()),
)
r := func(src []byte) ([]byte, error) {
var out bytes.Buffer
err := markdown.Convert(src, &out, parser.WithWorkers(0))
err := markdown.Convert(src, &out)
return out.Bytes(), err
}
doBenchmark(b, r)
Expand All @@ -70,12 +48,15 @@ func BenchmarkMarkdown(b *testing.B) {
})

b.Run("Lute", func(b *testing.B) {
luteEngine := lute.New(
lute.GFM(false),
lute.CodeSyntaxHighlight(false),
lute.SoftBreak2HardBreak(false),
lute.AutoSpace(false),
lute.FixTermTypo(false))
luteEngine := lute.New()
luteEngine.SetGFMAutoLink(false)
luteEngine.SetGFMStrikethrough(false)
luteEngine.SetGFMTable(false)
luteEngine.SetGFMTaskListItem(false)
luteEngine.SetCodeSyntaxHighlight(false)
luteEngine.SetSoftBreak2HardBreak(false)
luteEngine.SetAutoSpace(false)
luteEngine.SetFixTermTypo(false)
r := func(src []byte) ([]byte, error) {
out, err := luteEngine.MarkdownStr("Benchmark", util.BytesToReadOnlyString(src))
return util.StringToReadOnlyBytes(out), err
Expand Down
2 changes: 1 addition & 1 deletion extension/footnote.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func (s *footnoteParser) Parse(parent gast.Node, block text.Reader, pc parser.Co
block.Advance(closes + 1)

var list *ast.FootnoteList
if tlist := pc.Root().Get(footnoteListKey); tlist != nil {
if tlist := pc.Get(footnoteListKey); tlist != nil {
list = tlist.(*ast.FootnoteList)
}
if list == nil {
Expand Down
4 changes: 2 additions & 2 deletions parser/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func (s *linkParser) Parse(parent ast.Node, block text.Reader, pc Context) ast.N
block.SetPosition(l, pos)
ssegment := text.NewSegment(last.Segment.Stop, segment.Start)
maybeReference := block.Value(ssegment)
ref, ok := pc.Root().Reference(util.ToLinkReference(maybeReference))
ref, ok := pc.Reference(util.ToLinkReference(maybeReference))
if !ok {
ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment)
return nil
Expand Down Expand Up @@ -243,7 +243,7 @@ func (s *linkParser) parseReferenceLink(parent ast.Node, last *linkLabelState, b
maybeReference = block.Value(ssegment)
}

ref, ok := pc.Root().Reference(util.ToLinkReference(maybeReference))
ref, ok := pc.Reference(util.ToLinkReference(maybeReference))
if !ok {
return nil, true
}
Expand Down
Loading

0 comments on commit 16b6952

Please sign in to comment.