Skip to content

Commit

Permalink
feat:structure
Browse files Browse the repository at this point in the history
  • Loading branch information
songzhibin97 committed Jun 17, 2022
1 parent 7ad2489 commit 45b1d5d
Show file tree
Hide file tree
Showing 50 changed files with 22,784 additions and 1 deletion.
54 changes: 54 additions & 0 deletions internal/benchmark/linkedq/linkedq.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2021 ByteDance Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package linkedq

import "sync"

type LinkedQueue struct {
head *linkedqueueNode
tail *linkedqueueNode
mu sync.Mutex
}

type linkedqueueNode struct {
value uint64
next *linkedqueueNode
}

func New() *LinkedQueue {
node := new(linkedqueueNode)
return &LinkedQueue{head: node, tail: node}
}

func (q *LinkedQueue) Enqueue(value uint64) bool {
q.mu.Lock()
q.tail.next = &linkedqueueNode{value: value}
q.tail = q.tail.next
q.mu.Unlock()
return true
}

func (q *LinkedQueue) Dequeue() (uint64, bool) {
q.mu.Lock()
if q.head.next == nil {
q.mu.Unlock()
return 0, false
} else {
value := q.head.next.value
q.head = q.head.next
q.mu.Unlock()
return value, true
}
}
85 changes: 85 additions & 0 deletions internal/benchmark/msq/msq.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2021 ByteDance Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package msq

import (
"sync"
"sync/atomic"
"unsafe"
)

var msqv1pool *sync.Pool = &sync.Pool{New: func() interface{} { return new(msqv1node) }}

type MSQueue struct {
head unsafe.Pointer // *msqv1node
tail unsafe.Pointer // *msqv1node
}

type msqv1node struct {
value uint64
next unsafe.Pointer // *msqv1node
}

func New() *MSQueue {
node := unsafe.Pointer(new(msqv1node))
return &MSQueue{head: node, tail: node}
}

func loadMSQPointer(p *unsafe.Pointer) *msqv1node {
return (*msqv1node)(atomic.LoadPointer(p))
}

func (q *MSQueue) Enqueue(value uint64) bool {
node := &msqv1node{value: value}
for {
tail := atomic.LoadPointer(&q.tail)
tailstruct := (*msqv1node)(tail)
next := atomic.LoadPointer(&tailstruct.next)
if tail == atomic.LoadPointer(&q.tail) {
if next == nil {
// tail.next is empty, inset new node
if atomic.CompareAndSwapPointer(&tailstruct.next, next, unsafe.Pointer(node)) {
atomic.CompareAndSwapPointer(&q.tail, tail, unsafe.Pointer(node))
break
}
} else {
atomic.CompareAndSwapPointer(&q.tail, tail, next)
}
}
}
return true
}

func (q *MSQueue) Dequeue() (value uint64, ok bool) {
for {
head := atomic.LoadPointer(&q.head)
tail := atomic.LoadPointer(&q.tail)
headstruct := (*msqv1node)(head)
next := atomic.LoadPointer(&headstruct.next)
if head == atomic.LoadPointer(&q.head) {
if head == tail {
if next == nil {
return 0, false
}
atomic.CompareAndSwapPointer(&q.tail, tail, next)
} else {
value = ((*msqv1node)(next)).value
if atomic.CompareAndSwapPointer(&q.head, head, next) {
return value, true
}
}
}
}
}
66 changes: 66 additions & 0 deletions structure/hashset/hashset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2021 ByteDance Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hashset

type Int64Set map[int64]struct{}

// NewInt64 returns an empty int64 set
func NewInt64() Int64Set {
return make(map[int64]struct{})
}

// NewInt64WithSize returns an empty int64 set initialized with specific size
func NewInt64WithSize(size int) Int64Set {
return make(map[int64]struct{}, size)
}

// Add adds the specified element to this set
// Always returns true due to the build-in map doesn't indicate caller whether the given element already exists
// Reserves the return type for future extension
func (s Int64Set) Add(value int64) bool {
s[value] = struct{}{}
return true
}

// Contains returns true if this set contains the specified element
func (s Int64Set) Contains(value int64) bool {
if _, ok := s[value]; ok {
return true
}
return false
}

// Remove removes the specified element from this set
// Always returns true due to the build-in map doesn't indicate caller whether the given element already exists
// Reserves the return type for future extension
func (s Int64Set) Remove(value int64) bool {
delete(s, value)
return true
}

// Range calls f sequentially for each value present in the hashset.
// If f returns false, range stops the iteration.
func (s Int64Set) Range(f func(value int64) bool) {
for k := range s {
if !f(k) {
break
}
}
}

// Len returns the number of elements of this set
func (s Int64Set) Len() int {
return len(s)
}
187 changes: 187 additions & 0 deletions structure/hashset/hashset_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// Copyright 2021 ByteDance Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hashset

import (
"math/rand"
"testing"
)

type int64SetBool map[int64]bool

func newInt64Bool() *int64SetBool {
return &int64SetBool{}
}

func (s *int64SetBool) Add(value int64) bool {
(*s)[value] = true
return true
}

func (s *int64SetBool) Contains(value int64) bool {
if _, ok := (*s)[value]; ok {
return true
}
return false
}

func (s *int64SetBool) Remove(value int64) bool {
delete(*s, value)
return true
}

func (s *int64SetBool) Range(f func(value int64) bool) {
for k := range *s {
if !f(k) {
break
}
}
}

func (s *int64SetBool) Len() int {
return len(*s)
}

type int64SetAdd map[int64]struct{}

func newInt64Add() *int64SetAdd {
return &int64SetAdd{}
}

func (s *int64SetAdd) Add(value int64) bool {
if s.Contains(value) {
return true
}
(*s)[value] = struct{}{}
return true
}

func (s *int64SetAdd) Contains(value int64) bool {
if _, ok := (*s)[value]; ok {
return true
}
return false
}

func (s *int64SetAdd) Remove(value int64) bool {
if s.Contains(value) {
delete(*s, value)
return true
}
return false
}

func (s *int64SetAdd) Range(f func(value int64) bool) {
for k := range *s {
if !f(k) {
break
}
}
}

func (s *int64SetAdd) Len() int {
return len(*s)
}

const capacity = 10000000

var randomList [capacity]int64

func init() {
for i := 0; i < capacity; i++ {
randomList[i] = int64(rand.Int63())
}
}

func BenchmarkValueAsBool(b *testing.B) {
b.ResetTimer()
l := newInt64Bool()
for n := 0; n < b.N; n++ {
l.Add(randomList[n%capacity])
}
}

func BenchmarkValueAsEmptyStruct(b *testing.B) {
b.ResetTimer()
l := NewInt64()
for n := 0; n < b.N; n++ {
l.Add(randomList[n%capacity])
}
}

func BenchmarkAddAfterContains(b *testing.B) {
b.ResetTimer()
l := newInt64Add()
for n := 0; n < b.N; n++ {
l.Add(randomList[n%capacity])
}
}

func BenchmarkAddWithoutContains(b *testing.B) {
b.ResetTimer()
l := NewInt64()
for n := 0; n < b.N; n++ {
l.Add(randomList[n%capacity])
}
}

func BenchmarkRemoveAfterContains_Missing(b *testing.B) {
l := newInt64Add()
for n := 0; n < b.N; n++ {
l.Add(randomList[n%capacity])
}
b.ResetTimer()

for n := 0; n < b.N; n++ {
l.Remove(int64(rand.Int63()))
}
}

func BenchmarkRemoveWithoutContains_Missing(b *testing.B) {

l := NewInt64()
for n := 0; n < b.N; n++ {
l.Add(randomList[n%capacity])
}
b.ResetTimer()

for n := 0; n < b.N; n++ {
l.Remove(int64(rand.Int63()))
}
}

func BenchmarkRemoveAfterContains_Hitting(b *testing.B) {
l := newInt64Add()
for n := 0; n < b.N; n++ {
l.Add(randomList[n%capacity])
}

b.ResetTimer()
for n := 0; n < b.N; n++ {
l.Remove(randomList[n%capacity])
}
}

func BenchmarkRemoveWithoutContains_Hitting(b *testing.B) {
l := NewInt64()
for n := 0; n < b.N; n++ {
l.Add(randomList[n%capacity])
}

b.ResetTimer()
for n := 0; n < b.N; n++ {
l.Remove(randomList[n%capacity])
}
}
Loading

0 comments on commit 45b1d5d

Please sign in to comment.