最近希望能把一些过程,由传统的顺序执行改变成并发执行,看这样的优化是否能带来性能的提高。于是写了几个test来测试带来的影响。 测试的环境为mac pro,2.3 GHz Intel Core i5(双核),16GB内存。
(1)先测试并发写入内存是否能够得到性能的提高
测试代码如下:
func TestMemoryDB_SequenceExecute(t *testing.T) {
db := New()
t1 := time.Now()
//如果是同一个key只会覆盖,无法测试出性能,因此需要是不同的key来测,写入同一个key需要的时间约等于不同key的一半时间
//1千万次
for i := 0; i < 10000000; i++ {
err := db.Put([]byte("0x6766c3279a7b32e52e89b24d203dd311aaf3019f9dd182f0128d8f12ab4490c2"+strconv.Itoa(i)), []byte("0x6766c3279a7b32e52e89b24d203dd311aaf3019f9dd182f0128d8f12ab4490c2"))
if err != nil {
t.Fatalf("put failed: %v", err)
}
}
t2 := time.Now()
fmt.Println("spend time:", t2.Sub(t1)) //2.337S
}
func TestMemoryDB_ConcurrentExecute(t *testing.T) {
db := New()
wg := sync.WaitGroup{}
wg.Add(2)
t1 := time.Now()
exec1 := func() {
for i := 0; i < 5000000; i++ {
err := db.Put([]byte("0x6766c3279a7b32e52e89b24d203dd311aaf3019f9dd182f0128d8f12ab4490c2"+strconv.Itoa(i)), []byte("0x6766c3279a7b32e52e89b24d203dd311aaf3019f9dd182f0128d8f12ab4490c2"))
if err != nil {
t.Fatalf("put failed: %v", err)
}
}
wg.Done()
}
exec2 := func() {
for i := 2500000; i < 500000; i++ {
err := db.Put([]byte("0x6766c3279a7b32e52e89b24d203dd311aaf3019f9dd182f0128d8f12ab4490c2"+string(i)), []byte("0x6766c3279a7b32e52e89b24d203dd311aaf3019f9dd182f0128d8f12ab4490c2"))
if err != nil {
t.Fatalf("put failed: %v", err)
}
}
wg.Done()
}
go exec1()
go exec2()
wg.Wait()
t2 := time.Now()
fmt.Println("spend time:", t2.Sub(t1))
}
经测试顺序写入上述字符串1千万次,耗时13秒左右,开一个协程和开两个协程的耗时也是在14秒左右,如果开两个协程以上执行,耗时反而增多。因为越多协程,需要的调度和切换的时间会增多。
(2)再测试并发写入磁盘文件是否能够得到性能的提高
因为磁盘的性能比内存的性能要低一个数量级别以上,因为只写入100万次刚才的字符串。
测试代码如下:
func TestFile_SequenceWrite(t *testing.T) {
f, _ := os.Create("./output.txt")
defer f.Close()
t1 := time.Now()
for i := 0; i < 2000000; i++ {
_, err := f.Write([]byte("0x6766c3279a7b32e52e89b24d203dd311aaf3019f9dd182f0128d8f12ab4490c2"))
if err != nil {
t.Fatalf("put failed: %v", err)
}
}
t2 := time.Now()
fmt.Println("spend time:", t2.Sub(t1))
}
func TestFile_ConcurrentWrite(t *testing.T) {
f1, _ := os.Create("./output1.txt")
f2, _ := os.Create("./output2.txt")
wg := sync.WaitGroup{}
wg.Add(2)
t1 := time.Now()
go func() {
for i := 0; i < 1000000; i++ {
_, err := f1.Write([]byte("0x6766c3279a7b32e52e89b24d203dd311aaf3019f9dd182f0128d8f12ab4490c2"))
if err != nil {
t.Fatalf("put failed: %v", err)
}
}
wg.Done()
}()
go func() {
for i := 1000000; i < 2000000; i++ {
_, err := f2.Write([]byte("0x6766c3279a7b32e52e89b24d203dd311aaf3019f9dd182f0128d8f12ab4490c2"))
if err != nil {
t.Fatalf("put failed: %v", err)
}
}
wg.Done()
}()
wg.Wait()
t2 := time.Now()
fmt.Println("spend time:", t2.Sub(t1))
}
经测试,写入一个66字节的字符串200万次 到一个文件里(约134.1MB),如果顺序执行,需要耗时12秒左右。但如果开两个协程并发写入两个不同的文件,耗时在8秒左右。如果开两个协程以上并发写入多个文件,不会再有性能的提高。当然,这个速度和固态硬盘的读写速度是差很远的,如果想提高写入速度,可以在内存里先构造出需要写入文件的内容,再写入,这样能极大提高写入速度。如果使用这种方式写入文件数据,以上述的环境只需要30-40µs即可写入完成。