Skip to content

Commit efe0f5f

Browse files
committed
perf: switch to sync.Map-based pointer store
1 parent cf7be51 commit efe0f5f

File tree

6 files changed

+79
-27
lines changed

6 files changed

+79
-27
lines changed

builder.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,25 @@ extern lol_html_rewriter_directive_t callback_doc_end(lol_html_doc_end_t *doc_en
1313
import "C"
1414
import (
1515
"unsafe"
16-
17-
"github.com/mattn/go-pointer"
1816
)
1917

2018
// rewriterBuilder is used to build a rewriter.
2119
type rewriterBuilder struct {
2220
rb *C.lol_html_rewriter_builder_t
2321
pointers []unsafe.Pointer
22+
built bool // this builder has built at least one writer
2423
}
2524

2625
func newRewriterBuilder() *rewriterBuilder {
27-
return &rewriterBuilder{rb: C.lol_html_rewriter_builder_new(), pointers: nil}
26+
return &rewriterBuilder{rb: C.lol_html_rewriter_builder_new(), pointers: nil, built: false}
2827
}
2928

3029
func (rb *rewriterBuilder) Free() {
3130
if rb != nil {
3231
C.lol_html_rewriter_builder_free(rb.rb)
32+
if !rb.built {
33+
unrefPointers(rb.pointers)
34+
}
3335
}
3436
}
3537

@@ -52,10 +54,10 @@ func (rb *rewriterBuilder) AddDocumentContentHandlers(
5254
if documentEndHandler != nil {
5355
cCallbackDocumentEndPointer = (*[0]byte)(C.callback_doc_end)
5456
}
55-
doctypeHandlerPointer := pointer.Save(doctypeHandler)
56-
commentHandlerPointer := pointer.Save(commentHandler)
57-
textChunkHandlerPointer := pointer.Save(textChunkHandler)
58-
documentEndHandlerPointer := pointer.Save(documentEndHandler)
57+
doctypeHandlerPointer := savePointer(doctypeHandler)
58+
commentHandlerPointer := savePointer(commentHandler)
59+
textChunkHandlerPointer := savePointer(textChunkHandler)
60+
documentEndHandlerPointer := savePointer(documentEndHandler)
5961
C.lol_html_rewriter_builder_add_document_content_handlers(
6062
rb.rb,
6163
cCallbackDoctypePointer,
@@ -92,9 +94,9 @@ func (rb *rewriterBuilder) AddElementContentHandlers(
9294
if textChunkHandler != nil {
9395
cCallbackTextChunkPointer = (*[0]byte)(C.callback_text_chunk)
9496
}
95-
elementHandlerPointer := pointer.Save(elementHandler)
96-
commentHandlerPointer := pointer.Save(commentHandler)
97-
textChunkHandlerPointer := pointer.Save(textChunkHandler)
97+
elementHandlerPointer := savePointer(elementHandler)
98+
commentHandlerPointer := savePointer(commentHandler)
99+
textChunkHandlerPointer := savePointer(textChunkHandler)
98100
C.lol_html_rewriter_builder_add_element_content_handlers(
99101
rb.rb,
100102
(*C.lol_html_selector_t)(selector),
@@ -116,7 +118,7 @@ func (rb *rewriterBuilder) Build(sink OutputSink, config Config) (*rewriter, err
116118
preallocated_parsing_buffer_size: C.size_t(config.Memory.PreallocatedParsingBufferSize),
117119
max_allowed_memory_usage: C.size_t(config.Memory.MaxAllowedMemoryUsage),
118120
}
119-
p := pointer.Save(sink)
121+
p := savePointer(sink)
120122
r := C.lol_html_rewriter_build(
121123
rb.rb,
122124
encodingC,
@@ -127,6 +129,7 @@ func (rb *rewriterBuilder) Build(sink OutputSink, config Config) (*rewriter, err
127129
C.bool(config.Strict),
128130
)
129131
if r != nil {
132+
rb.built = true
130133
return &rewriter{rw: r, pointers: rb.pointers}, nil
131134
}
132135
return nil, getError()

config.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package lolhtml
55
*/
66
import "C"
77
import (
8-
"github.com/mattn/go-pointer"
98
"unsafe"
109
)
1110

@@ -63,36 +62,36 @@ type Handlers struct {
6362
//export callbackSink
6463
func callbackSink(chunk *C.char, chunkLen C.size_t, userData unsafe.Pointer) {
6564
c := C.GoBytes(unsafe.Pointer(chunk), C.int(chunkLen))
66-
cb := pointer.Restore(userData).(OutputSink)
65+
cb := restorePointer(userData).(OutputSink)
6766
cb(c)
6867
}
6968

7069
//export callbackDoctype
7170
func callbackDoctype(doctype *Doctype, userData unsafe.Pointer) RewriterDirective {
72-
cb := pointer.Restore(userData).(DoctypeHandlerFunc)
71+
cb := restorePointer(userData).(DoctypeHandlerFunc)
7372
return cb(doctype)
7473
}
7574

7675
//export callbackComment
7776
func callbackComment(comment *Comment, userData unsafe.Pointer) RewriterDirective {
78-
cb := pointer.Restore(userData).(CommentHandlerFunc)
77+
cb := restorePointer(userData).(CommentHandlerFunc)
7978
return cb(comment)
8079
}
8180

8281
//export callbackTextChunk
8382
func callbackTextChunk(textChunk *TextChunk, userData unsafe.Pointer) RewriterDirective {
84-
cb := pointer.Restore(userData).(TextChunkHandlerFunc)
83+
cb := restorePointer(userData).(TextChunkHandlerFunc)
8584
return cb(textChunk)
8685
}
8786

8887
//export callbackElement
8988
func callbackElement(element *Element, userData unsafe.Pointer) RewriterDirective {
90-
cb := pointer.Restore(userData).(ElementHandlerFunc)
89+
cb := restorePointer(userData).(ElementHandlerFunc)
9190
return cb(element)
9291
}
9392

9493
//export callbackDocumentEnd
9594
func callbackDocumentEnd(documentEnd *DocumentEnd, userData unsafe.Pointer) RewriterDirective {
96-
cb := pointer.Restore(userData).(DocumentEndHandlerFunc)
95+
cb := restorePointer(userData).(DocumentEndHandlerFunc)
9796
return cb(documentEnd)
9897
}

go.mod

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
module github.com/coolspring8/go-lolhtml
22

33
go 1.15
4-
5-
require github.com/mattn/go-pointer v0.0.1

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +0,0 @@
1-
github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
2-
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=

pointer.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package lolhtml
2+
3+
// Credit to https://github.com/mattn/go-pointer.
4+
5+
// #include <stdlib.h>
6+
import "C"
7+
import (
8+
"sync"
9+
"unsafe"
10+
)
11+
12+
// sync.Map documentation states that it is optimized for "when the entry for a given key is only
13+
// ever written once but read many times, as in caches that only grow". My benchmarks show that sync.Map
14+
// version rewriter is slower in single-goroutine calls, but faster when used in multiple goroutines
15+
// (and personally I think the latter is more important).
16+
var store sync.Map
17+
18+
func savePointer(v interface{}) unsafe.Pointer {
19+
if v == nil {
20+
return nil
21+
}
22+
23+
ptr := C.malloc(C.size_t(1))
24+
if ptr == nil {
25+
panic(`can't allocate "cgo-pointer hack index pointer": ptr == nil`)
26+
}
27+
28+
store.Store(ptr, v)
29+
30+
return ptr
31+
}
32+
33+
func restorePointer(ptr unsafe.Pointer) (v interface{}) {
34+
if ptr == nil {
35+
return nil
36+
}
37+
38+
if v, ok := store.Load(ptr); ok {
39+
return v
40+
}
41+
return nil
42+
}
43+
44+
func unrefPointer(ptr unsafe.Pointer) {
45+
if ptr == nil {
46+
return
47+
}
48+
49+
store.Delete(ptr)
50+
51+
C.free(ptr)
52+
}
53+
54+
func unrefPointers(ptrs []unsafe.Pointer) {
55+
for _, ptr := range ptrs {
56+
unrefPointer(ptr)
57+
}
58+
}

rewriter.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ package lolhtml
77
import "C"
88
import (
99
"unsafe"
10-
11-
"github.com/mattn/go-pointer"
1210
)
1311

1412
// rewriter represents an actual HTML rewriter.
@@ -55,8 +53,6 @@ func (r *rewriter) End() error {
5553
func (r *rewriter) Free() {
5654
if r != nil {
5755
C.lol_html_rewriter_free(r.rw)
58-
for _, p := range r.pointers {
59-
pointer.Unref(p)
60-
}
56+
unrefPointers(r.pointers)
6157
}
6258
}

0 commit comments

Comments
 (0)