两个list求和_使用go协程优化求和算法

dcbc6be6a58381b3149a58e27d928c4b.png

前言

我的主力开发语言是JavaScript,但是 js 的并行计算的支持并不是特别好。在入门golang后,了解到了go协程channel,想试一试并行计算。go原生对并发很友好,可以在多核CPU的设备上进行并行计算,我这里是希望尝试使用go协程优化大数组求和。

对比

进行对比的两个函数

  1. func sum1(bigArray []int) int 使用普通的数组求和
  2. func sum2(bigArray []int) int将数组分割成若干个数组,在不同的go协程进行求和,所有的go协程都得到结果后,再进行求和

源码如下

package main

import (
	"math/rand"
	"fmt"
	"sync"
	"time"
)

func main(){
	for i:=1000;i<10000000000;i*=2 {
		duration1,duration2,_,_ := runBenchmark(i);
		fmt.Println(i,duration1,duration2)
	}
}


func runBenchmark(amout int) (int64,int64,int64,int64) {
	bigArray :=createBigArray(amout);
	stop1 := createTimer();
	result1 := sum1(bigArray);
	duration1 := stop1();
	stop2 :=createTimer();
	result2 := sum2(bigArray);
	duration2 := stop2();
	return duration1,duration2,result1,result2;
}

func createBigArray(size int)[]int{
	rand.Seed(time.Now().Unix());
	list := make([]int,size);
	for i:=0;i<size;i++{
		list[i] = rand.Int()
	}
	return list;
}

func createTimer() func () int64 {
	startTime := time.Now().UnixNano()
	return func () int64 {
		return time.Now().UnixNano() - startTime
	}
}

func sum1(bigArray []int) int64 {
	sum := int64(0)
	for _,value := range(bigArray){
		sum += int64(value);
	}
	return sum;
}

func sum2(bigArray []int)int64 {
	SmallArraySize := len(bigArray)/100;
	sum:=int64(0);
	chanGroupSize := len(bigArray)/SmallArraySize;
	sumChan := make(chan int64,chanGroupSize+1);
	waitGroup := new(sync.WaitGroup);
	for i:=0;i<chanGroupSize;i++{
		waitGroup.Add(1);
		go sumRoutine(bigArray[i*SmallArraySize:(i+1)*SmallArraySize], sumChan, waitGroup)
	}
	waitGroup.Wait()
	for i:=0;i<chanGroupSize;i++ {
		sum+= <- sumChan;
	}
	return sum;
}

func sumRoutine(smallArray []int,ch chan int64,wg *sync.WaitGroup){
	defer wg.Done()
	sum :=int64(0);
	for _,value := range(smallArray){
		sum +=int64(value);
	}
	ch <- sum;
}

进行测试

首先我在自己的笔记本上跑了一下,效果似乎不是特别明显,当数据规模比较小时,使用go协程产生的开销相比多线程带来的性能提升会比较大,使用go协程反而造成效率降低,当数据规模达到 2.5e5 左右时两种求和方法的耗时差不多。

6ed0ed40734d04f532ba0126abc0f87a.png

运行的结果:

数据规模 sum1函数耗时(ns) sum2函数耗时(ns)
1000	1000	257000
2000	2000	196000
4000	3000	123000
8000	19000	566000
16000	24000	574000
32000	21000	106000
64000	46000	101000
128000	92000	117000
256000	161000	164000
512000	710000	354000
1024000	633000	380000
2048000	1855000	746000
4096000	3779000	1430000
8192000	9285000	7126000
16384000	17107000	7522000
32768000	37100000	16824000
65536000	71186000	28741000
131072000	153552000	55689000
262144000	349672000	125364000
524288000	3370025000	221678000

b797822cdf5fac380a2a57cbc4091351.png

后面我使用了一台 8 核 16GB 内存的云服务器进行测试,效果就比较明显了。sum2 函数的耗时明显降低。

processor : 6
vendor_id : GenuineIntel
cpu family : 6
model : 85
model name : Intel(R) Xeon(R) Platinum 8255C CPU @ 2.50GHz
stepping : 5
microcode : 0x1
cpu MHz : 2494.140
cache size : 36608 KB
physical id : 0
siblings : 8
core id : 6
cpu cores : 8
apicid : 6
initial apicid : 6
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes

运行的结果:

数据规模 sum1函数耗时(ns) sum2函数耗时(ns)
1000	975	184791
2000	1744	136564
4000	2452	77209
8000	4462	148065
16000	13041	151411
32000	17729	99903
64000	34824	88240
128000	72507	88009
256000	153425	118217
512000	307149	128954
1024000	716739	218762
2048000	1498043	340398
4096000	3121459	741179
8192000	6512778	1440240
16384000	12999638	2841228
32768000	25421817	5573615
65536000	52787702	11216755
131072000	102977136	22564239
262144000	212568740	48269912
524288000	408104714	90059294

a6700dbd6ff03bc95fc73239bcacd884.png

分析

在求和数组规模数据规模比较大,运行的机器核心数比较多的时候,使用go协程是可以提升大数组求和的效率的。go协程和系统的进程并不是一一对应的,创建一个go协程本身的开销也比创建一个线程小。对大数组求和只是一种场景,也有很多其他类似的cpu密集型场景可以利用go协程进行优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值