-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c2705a8
commit 7dab08a
Showing
5 changed files
with
249 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# 简介 | ||
对于线程安全的hashmap的选取是性能提升的一个重点,摆在面前的有三种选择,BaseMap,sync.map以及concurrentMap。 | ||
|
||
1. **BaseMap**:map中只有一把锁,所有操作都经过这把锁。 | ||
2. **sync.map**:Golang提供的一个线程安全的哈希map,采用无锁实现。 | ||
3. **concurrentMap**:每一个哈希桶加一把锁,最大程度并发。 | ||
|
||
一般情况直接使用标准库提供的就不会有太大问题,但在简单的查阅了sync.map的实现后我认为此种方法在多写的情况下并不高效,因为涉及到很多次的拷贝,为了更优的性能,我测试了这三种方案在不同情况下的性能,最终发现BaseMap效率在任何情况下表现都不突出,而其他两种方法各有优略,最终我选择的版本是客户可以根据不同的应用条件在配置文件中动态的配置选择sync.map还是concurrentMap,为了更优雅的代码与更少的空间消耗,引入反射动态区分这两种类型,最终带来了效率上一定的损耗,但增加了灵活性,我称之为ChubbyGoMap。 | ||
|
||
|
||
以下是BaseMap, sync.map, concurrentMap, 以及ChubbyGoMap的两种类型五种类型分别执行Put,Get,并发插入读取混合,删除四种操作的性能对比,以此为客户提供不同场景下最佳的选择: | ||
|
||
测试代码位于/ChubbyGo/MapPerformanceTest/test_test.go: | ||
|
||
执行以下命令可以执行测试: | ||
|
||
```go | ||
go test -v -run=^$ -bench . -benchmem | ||
``` | ||
|
||
说明: | ||
|
||
1. ns/op : 每次执行平均花费时间; | ||
2. b/op: 每次执行堆上分配内存总数; | ||
3. allocs/op: 每次执行堆上分配内存次数; | ||
|
||
## Put | ||
| | ns/op | b/op| allocs/op| | ||
|--|--|--|--|--| | ||
|BaseMap | 2505 | 205| 10 | ||
|ConcurrentMap|1257| 239|10 | ||
|SyncMap| 4546|500|32 | ||
|GoConcurrentMap|1838|423|20 | ||
|GoSyncMap|5096|662|42 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package main | ||
|
||
import ( | ||
"ChubbyGo/BaseServer" | ||
"strconv" | ||
"sync" | ||
"testing" | ||
) | ||
|
||
/* | ||
* @brief: 这个文件是为了对BaseMap, sync.map, concurrentMap, 以及ChubbyGoMap的两种类型 | ||
* 的Put,Get,并发插入读取混合,删除四种操作分别做性能比较, 为客户提供不同场景下最佳的选择. | ||
*/ | ||
|
||
/* | ||
* 循环次数;每次执行平均花费时间;每次执行堆上分配内存总数;每次执行堆上分配内存次数; | ||
*/ | ||
|
||
const ThreadNumber = 10 | ||
|
||
/*1.--------------测试Put操作--------------*/ | ||
|
||
/*[1]--------------插入不存在的key--------------*/ | ||
|
||
// 在此测试中冲突键较多,对并发的map并不有利 | ||
|
||
//(BaseMap) | ||
func BenchmarkSinglePutKeyNoExist_BaseMap(b *testing.B){ | ||
Map := BaseServer.NewBaseMap() | ||
|
||
groups := sync.WaitGroup{} | ||
groups.Add(ThreadNumber) | ||
|
||
b.ResetTimer() | ||
|
||
for j:=0; j < ThreadNumber; j++{ | ||
go func(){ | ||
for i := 0; i < b.N; i++ { | ||
Map.BaseStore(strconv.Itoa(i), "lizhaolong") | ||
} | ||
groups.Done() | ||
}() | ||
} | ||
groups.Wait() | ||
} | ||
|
||
//(ConcurrentMap) | ||
func BenchmarkSinglePutKeyNoExist_ConcurrentMap(b *testing.B){ | ||
Map := BaseServer.NewConcurrentMap() | ||
|
||
groups := sync.WaitGroup{} | ||
groups.Add(ThreadNumber) | ||
|
||
b.ResetTimer() | ||
|
||
for j:=0; j < ThreadNumber; j++{ | ||
go func(){ | ||
for i := 0; i < b.N; i++ { | ||
Map.Set(strconv.Itoa(i), "lizhaolong") | ||
} | ||
groups.Done() | ||
}() | ||
} | ||
groups.Wait() | ||
} | ||
|
||
// (Sync.map) | ||
func BenchmarkSinglePutKeyNoExist_SyncMap(b *testing.B){ | ||
syncMap := &sync.Map{} | ||
|
||
groups := sync.WaitGroup{} | ||
groups.Add(ThreadNumber) | ||
|
||
b.ResetTimer() | ||
|
||
for j:=0; j < ThreadNumber; j++{ | ||
go func(){ | ||
for i := 0; i < b.N; i++ { | ||
syncMap.Store(strconv.Itoa(i), "lizhaolong") | ||
} | ||
groups.Done() | ||
}() | ||
} | ||
groups.Wait() | ||
} | ||
|
||
// (chubbyGoMap.ConcurrentMap) | ||
func BenchmarkSinglePutKeyNoExist_ChubbyGo_ConcurrentMap(b *testing.B){ | ||
chubbyGoMap := BaseServer.NewChubbyGoMap(BaseServer.ConcurrentMap) | ||
|
||
groups := sync.WaitGroup{} | ||
groups.Add(ThreadNumber) | ||
|
||
b.ResetTimer() | ||
|
||
for j:=0; j < ThreadNumber; j++{ | ||
go func(){ | ||
for i := 0; i < b.N; i++ { | ||
chubbyGoMap.ChubbyGoMapSet(strconv.Itoa(i), "lizhaolong") | ||
} | ||
groups.Done() | ||
}() | ||
} | ||
groups.Wait() | ||
} | ||
|
||
// (chubbyGoMap.Sync.map) | ||
func BenchmarkSinglePutKeyNoExist_ChubbyGo_SyncMap(b *testing.B){ | ||
chubbyGoMap := BaseServer.NewChubbyGoMap(BaseServer.SyncMap) | ||
|
||
groups := sync.WaitGroup{} | ||
groups.Add(ThreadNumber) | ||
|
||
b.ResetTimer() | ||
|
||
for j:=0; j < ThreadNumber; j++{ | ||
go func(){ | ||
for i := 0; i < b.N; i++ { | ||
chubbyGoMap.ChubbyGoMapSet(strconv.Itoa(i), "lizhaolong") | ||
} | ||
groups.Done() | ||
}() | ||
} | ||
groups.Wait() | ||
} | ||
|
||
|
||
/*[1]--------------插入存在的key--------------*/ |