Skip to content

Commit 249fa0b

Browse files
committed
linked list properties
benchmark old ns/op new ns/op delta BenchmarkAllocProperty/props0 67.8 68.2 +0.59% BenchmarkAllocProperty/props1 149 135 -9.40% BenchmarkAllocProperty/props2 262 202 -22.90% BenchmarkAllocProperty/props3 375 275 -26.67% BenchmarkAllocProperty/props4 522 341 -34.67% BenchmarkAllocProperty/props5 712 408 -42.70% BenchmarkAllocProperty/props6 970 474 -51.13% BenchmarkAllocProperty/props7 1100 540 -50.91% BenchmarkAllocProperty/props8 1296 607 -53.16% benchmark old allocs new allocs delta BenchmarkAllocProperty/props0 1 1 +0.00% BenchmarkAllocProperty/props1 3 3 +0.00% BenchmarkAllocProperty/props2 6 5 -16.67% BenchmarkAllocProperty/props3 9 7 -22.22% BenchmarkAllocProperty/props4 13 9 -30.77% BenchmarkAllocProperty/props5 18 11 -38.89% BenchmarkAllocProperty/props6 24 13 -45.83% BenchmarkAllocProperty/props7 28 15 -46.43% BenchmarkAllocProperty/props8 33 17 -48.48% benchmark old bytes new bytes delta BenchmarkAllocProperty/props0 64 64 +0.00% BenchmarkAllocProperty/props1 176 160 -9.09% BenchmarkAllocProperty/props2 336 256 -23.81% BenchmarkAllocProperty/props3 496 352 -29.03% BenchmarkAllocProperty/props4 704 448 -36.36% BenchmarkAllocProperty/props5 960 544 -43.33% BenchmarkAllocProperty/props6 1264 640 -49.37% BenchmarkAllocProperty/props7 1472 736 -50.00% BenchmarkAllocProperty/props8 1728 832 -51.85% benchmark old ns/op new ns/op delta BenchmarkGetProperty/props0 14.4 15.0 +4.17% BenchmarkGetProperty/props1 22.1 15.9 -28.05% BenchmarkGetProperty/props2 20.4 17.7 -13.24% BenchmarkGetProperty/props3 26.2 20.2 -22.90% BenchmarkGetProperty/props4 29.3 21.9 -25.26% BenchmarkGetProperty/props5 32.7 22.3 -31.80% BenchmarkGetProperty/props6 26.2 23.5 -10.31% BenchmarkGetProperty/props7 29.3 24.0 -18.09% BenchmarkGetProperty/props8 26.2 25.9 -1.15%
1 parent 0069c94 commit 249fa0b

File tree

3 files changed

+14
-47
lines changed

3 files changed

+14
-47
lines changed

error.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ var _ fmt.Formatter = (*Error)(nil)
2727
// If an error already contained another value for the same property, it is overwritten.
2828
// It is a caller's responsibility to accumulate and update a property, if needed.
2929
// Dynamic properties is a brittle mechanism and should therefore be used with care and in a simple and robust manner.
30+
// Currently, properties are implemented as a linked list, therefore it is not safe to have many dozens of them. But couple of dozen is just ok.
3031
func (e *Error) WithProperty(key Property, value interface{}) *Error {
3132
errorCopy := *e
3233
errorCopy.properties = errorCopy.properties.with(key, value)

id.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
11
package errorx
22

33
import (
4-
"sync"
4+
"sync/atomic"
55
)
66

7-
var internalID uint64 = 0x123456789abcdef0
8-
var idMtx sync.Mutex
7+
var internalID uint64
98

109
// nextInternalID creates next unique id for errorx entities.
1110
// All equality comparison should take id into account, lest there be some false positive matches.
1211
func nextInternalID() uint64 {
13-
idMtx.Lock()
14-
// xorshift
15-
x := internalID
16-
x ^= x << 13
17-
x ^= x >> 7
18-
x ^= x << 17
19-
id := x + internalID
20-
internalID = x
21-
idMtx.Unlock()
22-
return id
12+
return atomic.AddUint64(&internalID, 1)
2313
}

property.go

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ import (
88
// Property value belongs to an error instance only, never inherited from a type.
99
// Property visibility is hindered by Wrap, preserved by Decorate.
1010
type Property struct {
11-
*property
11+
*property // Property is compared by this pointer.
1212
}
1313

1414
type property struct {
15-
id uint64
1615
label string
1716
}
1817

@@ -77,7 +76,6 @@ var (
7776
func newProperty(label string) Property {
7877
p := Property{
7978
&property{
80-
id: nextInternalID(),
8179
label: label,
8280
},
8381
}
@@ -86,45 +84,23 @@ func newProperty(label string) Property {
8684

8785
// propertyMap represents map of properties.
8886
// Compared to builtin type, it uses less allocations and reallocations on copy.
89-
// It is simple binary search tree that relies on property id randomness for balancing.
87+
// It is implemented as a simple linked list.
9088
type propertyMap struct {
9189
p Property
9290
value interface{}
93-
left *propertyMap
94-
right *propertyMap
91+
next *propertyMap
9592
}
9693

97-
// withProperty creates new map with property added or overwritten
9894
func (pm *propertyMap) with(p Property, value interface{}) *propertyMap {
99-
if pm == nil {
100-
return &propertyMap{p: p, value: value}
101-
} else if pm.p == p {
102-
return &propertyMap{
103-
p: p,
104-
value: value,
105-
left: pm.left,
106-
right: pm.right,
107-
}
108-
} else {
109-
copy := *pm
110-
if copy.p.id < p.id {
111-
copy.right = copy.right.with(p, value)
112-
} else {
113-
copy.left = copy.left.with(p, value)
114-
}
115-
return &copy
116-
}
95+
return &propertyMap{p: p, value: value, next: pm}
11796
}
11897

11998
func (pm *propertyMap) get(p Property) (value interface{}, ok bool) {
120-
switch {
121-
case pm == nil:
122-
return nil, false
123-
case pm.p == p:
124-
return pm.value, true
125-
case pm.p.id < p.id:
126-
return pm.right.get(p)
127-
default:
128-
return pm.left.get(p)
99+
for pm != nil {
100+
if pm.p == p {
101+
return pm.value, true
102+
}
103+
pm = pm.next
129104
}
105+
return nil, false
130106
}

0 commit comments

Comments
 (0)