Skip to content

Commit

Permalink
optimize skiplist with inline compare for ordered types (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
chen3feng authored Aug 10, 2022
1 parent 75d8660 commit 1d444f1
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 36 deletions.
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,7 @@ type Signed interface {
}
```

## type [SkipList](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L27-L36>)
## type [SkipList](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L27-L37>)

SkipList is a probabilistic data structure that seem likely to supplant balanced trees as the implementation method of choice for many applications. Skip list algorithms have the same asymptotic expected time bounds as balanced trees and are simpler, faster and use less space.

Expand All @@ -1075,95 +1075,95 @@ type SkipList[K any, V any] struct {
}
```

### func [NewSkipList](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L48>)
### func [NewSkipList](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L49>)

```go
func NewSkipList[K Ordered, V any]() *SkipList[K, V]
```

NewSkipList creates a new Skiplist.

### func [NewSkipListFromMap](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L53>)
### func [NewSkipListFromMap](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L56>)

```go
func NewSkipListFromMap[K Ordered, V any](m map[K]V) *SkipList[K, V]
```

NewSkipListFromMap create a new Skiplist from a map.

### func [NewSkipListFunc](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L62>)
### func [NewSkipListFunc](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L65>)

```go
func NewSkipListFunc[K any, V any](keyCmp CompareFn[K]) *SkipList[K, V]
```

NewSkipListFunc creates a new Skiplist with specified compare function keyCmp.

### func \(\*SkipList\[K, V\]\) [Clear](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L82>)
### func \(\*SkipList\[K, V\]\) [Clear](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L85>)

```go
func (sl *SkipList[K, V]) Clear()
```

### func \(\*SkipList\[K, V\]\) [Find](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L121>)
### func \(\*SkipList\[K, V\]\) [Find](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L124>)

```go
func (sl *SkipList[K, V]) Find(key K) *V
```

Find returns the value associated with the passed key if the key is in the skiplist, otherwise returns nil.

### func \(\*SkipList\[K, V\]\) [ForEach](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L150>)
### func \(\*SkipList\[K, V\]\) [ForEach](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L153>)

```go
func (sl *SkipList[K, V]) ForEach(op func(K, V))
```

### func \(\*SkipList\[K, V\]\) [ForEachIf](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L162>)
### func \(\*SkipList\[K, V\]\) [ForEachIf](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L165>)

```go
func (sl *SkipList[K, V]) ForEachIf(op func(K, V) bool)
```

### func \(\*SkipList\[K, V\]\) [ForEachMutable](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L156>)
### func \(\*SkipList\[K, V\]\) [ForEachMutable](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L159>)

```go
func (sl *SkipList[K, V]) ForEachMutable(op func(K, *V))
```

### func \(\*SkipList\[K, V\]\) [ForEachMutableIf](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L170>)
### func \(\*SkipList\[K, V\]\) [ForEachMutableIf](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L173>)

```go
func (sl *SkipList[K, V]) ForEachMutableIf(op func(K, *V) bool)
```

### func \(\*SkipList\[K, V\]\) [Has](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L129>)
### func \(\*SkipList\[K, V\]\) [Has](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L132>)

```go
func (sl *SkipList[K, V]) Has(key K) bool
```

### func \(\*SkipList\[K, V\]\) [Insert](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L92>)
### func \(\*SkipList\[K, V\]\) [Insert](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L95>)

```go
func (sl *SkipList[K, V]) Insert(key K, value V)
```

Insert inserts a key\-value pair into the skiplist. If the key is already in the skip list, it's value will be updated.

### func \(\*SkipList\[K, V\]\) [IsEmpty](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L74>)
### func \(\*SkipList\[K, V\]\) [IsEmpty](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L77>)

```go
func (sl *SkipList[K, V]) IsEmpty() bool
```

### func \(\*SkipList\[K, V\]\) [Len](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L78>)
### func \(\*SkipList\[K, V\]\) [Len](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L81>)

```go
func (sl *SkipList[K, V]) Len() int
```

### func \(\*SkipList\[K, V\]\) [Remove](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L135>)
### func \(\*SkipList\[K, V\]\) [Remove](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L138>)

```go
func (sl *SkipList[K, V]) Remove(key K) bool
Expand Down
30 changes: 15 additions & 15 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ type Signed interface {
}
```

## type [SkipList](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L27-L36>)
## type [SkipList](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L27-L37>)

SkipList is a probabilistic data structure that seem likely to supplant balanced trees as the implementation method of choice for many applications. Skip list algorithms have the same asymptotic expected time bounds as balanced trees and are simpler, faster and use less space.

Expand All @@ -1073,95 +1073,95 @@ type SkipList[K any, V any] struct {
}
```

### func [NewSkipList](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L48>)
### func [NewSkipList](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L49>)

```go
func NewSkipList[K Ordered, V any]() *SkipList[K, V]
```

NewSkipList creates a new Skiplist.

### func [NewSkipListFromMap](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L53>)
### func [NewSkipListFromMap](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L56>)

```go
func NewSkipListFromMap[K Ordered, V any](m map[K]V) *SkipList[K, V]
```

NewSkipListFromMap create a new Skiplist from a map.

### func [NewSkipListFunc](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L62>)
### func [NewSkipListFunc](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L65>)

```go
func NewSkipListFunc[K any, V any](keyCmp CompareFn[K]) *SkipList[K, V]
```

NewSkipListFunc creates a new Skiplist with specified compare function keyCmp.

### func \(\*SkipList\[K, V\]\) [Clear](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L82>)
### func \(\*SkipList\[K, V\]\) [Clear](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L85>)

```go
func (sl *SkipList[K, V]) Clear()
```

### func \(\*SkipList\[K, V\]\) [Find](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L121>)
### func \(\*SkipList\[K, V\]\) [Find](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L124>)

```go
func (sl *SkipList[K, V]) Find(key K) *V
```

Find returns the value associated with the passed key if the key is in the skiplist, otherwise returns nil.

### func \(\*SkipList\[K, V\]\) [ForEach](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L150>)
### func \(\*SkipList\[K, V\]\) [ForEach](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L153>)

```go
func (sl *SkipList[K, V]) ForEach(op func(K, V))
```

### func \(\*SkipList\[K, V\]\) [ForEachIf](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L162>)
### func \(\*SkipList\[K, V\]\) [ForEachIf](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L165>)

```go
func (sl *SkipList[K, V]) ForEachIf(op func(K, V) bool)
```

### func \(\*SkipList\[K, V\]\) [ForEachMutable](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L156>)
### func \(\*SkipList\[K, V\]\) [ForEachMutable](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L159>)

```go
func (sl *SkipList[K, V]) ForEachMutable(op func(K, *V))
```

### func \(\*SkipList\[K, V\]\) [ForEachMutableIf](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L170>)
### func \(\*SkipList\[K, V\]\) [ForEachMutableIf](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L173>)

```go
func (sl *SkipList[K, V]) ForEachMutableIf(op func(K, *V) bool)
```

### func \(\*SkipList\[K, V\]\) [Has](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L129>)
### func \(\*SkipList\[K, V\]\) [Has](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L132>)

```go
func (sl *SkipList[K, V]) Has(key K) bool
```

### func \(\*SkipList\[K, V\]\) [Insert](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L92>)
### func \(\*SkipList\[K, V\]\) [Insert](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L95>)

```go
func (sl *SkipList[K, V]) Insert(key K, value V)
```

Insert inserts a key\-value pair into the skiplist. If the key is already in the skip list, it's value will be updated.

### func \(\*SkipList\[K, V\]\) [IsEmpty](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L74>)
### func \(\*SkipList\[K, V\]\) [IsEmpty](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L77>)

```go
func (sl *SkipList[K, V]) IsEmpty() bool
```

### func \(\*SkipList\[K, V\]\) [Len](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L78>)
### func \(\*SkipList\[K, V\]\) [Len](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L81>)

```go
func (sl *SkipList[K, V]) Len() int
```

### func \(\*SkipList\[K, V\]\) [Remove](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L135>)
### func \(\*SkipList\[K, V\]\) [Remove](<https://github.com/chen3feng/stl4go/blob/master/skiplist.go#L138>)

```go
func (sl *SkipList[K, V]) Remove(key K) bool
Expand Down
38 changes: 32 additions & 6 deletions skiplist.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ const (
//
// See https://en.wikipedia.org/wiki/Skip_list for more details.
type SkipList[K any, V any] struct {
keyCmp CompareFn[K]
level int // Current level, may increase dynamically during insertion
len int // Total elements numner in the skiplist.
head skipListNode[K, V]
keyCmp CompareFn[K]
builtinCompare bool // Whether the key type is Ordered, so we can use builtin compare to improve performance.
level int // Current level, may increase dynamically during insertion
len int // Total elements numner in the skiplist.
head skipListNode[K, V]
// This cache is used to save the previous nodes when modifying the skip list to avoid
// allocating memory each time it is called.
prevsCache []*skipListNode[K, V] // Cache to avoid memory allocation.
Expand All @@ -46,7 +47,9 @@ type skipListNode[K any, V any] struct {

// NewSkipList creates a new Skiplist.
func NewSkipList[K Ordered, V any]() *SkipList[K, V] {
return NewSkipListFunc[K, V](OrderedCompare[K])
sl := NewSkipListFunc[K, V](OrderedCompare[K])
sl.builtinCompare = true
return sl
}

// NewSkipListFromMap create a new Skiplist from a map.
Expand Down Expand Up @@ -192,7 +195,30 @@ func (sl *SkipList[K, V]) randomLevel() int {
return level
}

func (sl *SkipList[K, V]) findNode(key K) *skipListNode[K, V] {
//go:generate bash ./skiplist_findnode_generate.sh skiplist_findnode.go
// func (sl *SkipList[K, V]) findNode(key K) *skipListNode[K, V]

// findNodeFast find node with builtin compare function, which is faster because it can be inlined.
func findNodeFast[K Ordered, V any](sl *SkipList[K, V], key K) *skipListNode[K, V] {
var pre = &sl.head
for i := sl.level - 1; i >= 0; i-- {
cur := pre.next[i]
for ; cur != nil; cur = cur.next[i] {
cmpRet := OrderedCompare(cur.key, key)
if cmpRet == 0 {
return cur
}
if cmpRet > 0 {
break
}
pre = cur
}
}
return nil
}

// findNodeSlow use keyCmp to compare key, which is slower.
func (sl *SkipList[K, V]) findNodeSlow(key K) *skipListNode[K, V] {
var pre = &sl.head
for i := sl.level - 1; i >= 0; i-- {
cur := pre.next[i]
Expand Down
75 changes: 75 additions & 0 deletions skiplist_findnode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// AUTO GENERATED CODE, DON'T EDIT!!!
// EDIT skiplist_findnode_generate.sh accordingly.

package stl4go

import "unsafe"

func forceCast[T any, F any](f *F) *T {
//#nosec G103
return (*T)(unsafe.Pointer(f))
}

// findNode find the node which has specified key.
func (sl *SkipList[K, V]) findNode(key K) *skipListNode[K, V] {
if !sl.builtinCompare {
return sl.findNodeSlow(key)
}
// For knowned Ordered types, use findNodeFast to improve performance.
var iface interface{} = key
switch iface.(type) {
case int:
tsl := forceCast[SkipList[int, V]](sl)
v, _ := iface.(int)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case string:
tsl := forceCast[SkipList[string, V]](sl)
v, _ := iface.(string)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case int32:
tsl := forceCast[SkipList[int32, V]](sl)
v, _ := iface.(int32)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case uint32:
tsl := forceCast[SkipList[uint32, V]](sl)
v, _ := iface.(uint32)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case int64:
tsl := forceCast[SkipList[int64, V]](sl)
v, _ := iface.(int64)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case uint64:
tsl := forceCast[SkipList[uint64, V]](sl)
v, _ := iface.(uint64)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case int8:
tsl := forceCast[SkipList[int8, V]](sl)
v, _ := iface.(int8)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case uint8:
tsl := forceCast[SkipList[uint8, V]](sl)
v, _ := iface.(uint8)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case int16:
tsl := forceCast[SkipList[int16, V]](sl)
v, _ := iface.(int16)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case uint16:
tsl := forceCast[SkipList[uint16, V]](sl)
v, _ := iface.(uint16)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case uintptr:
tsl := forceCast[SkipList[uintptr, V]](sl)
v, _ := iface.(uintptr)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case float32:
tsl := forceCast[SkipList[float32, V]](sl)
v, _ := iface.(float32)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
case float64:
tsl := forceCast[SkipList[float64, V]](sl)
v, _ := iface.(float64)
return forceCast[skipListNode[K, V]](findNodeFast(tsl, v))
}
return sl.findNodeSlow(key)
}
Loading

0 comments on commit 1d444f1

Please sign in to comment.