@
nekoyaki @
zhuang @
wwek @
oscarzhao 出现没有跑满 CPU 的原因是: rand.Intn()方法会调用一个全局对象的方法生成随机数,这个全局方法是有 lock 的,所以会出现 golang 的多个 goroutine 相互竞争, lock 的底层实现,我不是很清楚,但是“ lock 检测不让 CPU 满载”是高并发程序的基本原则。要想让其满载,应该使用 goroutine 独立的计算模块,最简单就是
for {} .
见:
https://golang.org/src/math/rand/rand.go#L238 238 func (r *lockedSource) Int63() (n int64) {
239 r.lk.Lock()
240 n = r.src.Int63()
241 r.lk.Unlock()
242 return
243 }
另外作者程序存在两个问题:
1. main 线程调用的计算和其他线程调用的计算不一样, main 线程会占用 100%,而其他线程并不会满负载。其 CPU 的负载结果很难说是平均每个线程的负载情况。
2. Golang 映射的 native 的线程数可以通过 runtime.GOMAXPROCS(cpunum)设置,实际运行中最大的线程数在此基础上+1 ,因为还有 gc 线程。作者为了测试 routineNum 满载,除了 main 线程外,应该再开启 routineNum-1 个 goroutine 。
我做了两个测试,硬件配置: 2CPU, 4 cores per CPU; Golang 配置: runtime.GOMAXPROCS(8):
1.
func main() {
routineNum, err := strconv.Atoi(os.Args(1))
cpunum := runtime.NumCPU()
runtime.GOMAXPROCS(cpunum)
for i := 0; i < routineNum -1 ; i += 1 {
go func() {
for {
rand.Intn(10000000)
}
}()
}
for {
rand.Intn(10000000)
}
}
以下是测试结果
routineNum 1 2 3 4 5 6 7 8
CPU 使用率 100% 120% 201% 210% 220% 240% 266% 290%
#native thread 4 5 6 6 7 8 9 9
2.
func main() {
routineNum, err := strconv.Atoi(os.Args(1))
cpunum := runtime.NumCPU()
runtime.GOMAXPROCS(cpunum)
for i := 0; i < routineNum -1 ; i += 1 {
go func() {
for {
// nothing
}
}()
}
for {
// nothing
}
}
routineNum 1 2 3 4 5 6 7 8
CPU 使用率 100% 199% 299% 399% 496% 597% 697% 790%
#native threads 4 4 5 6 7 8 9 9
所以楼上说 Golang 没有跑到满负载是做了什么优化是错误的,另外不同版本之间的差别只是因为 lock 的性能差别导致;内核调度器的实现不会这么傻,在 core 处于 idle 的时候难道不将计算密集型的线程分配在上面?
所以楼主想实现制定的运行百分比,就应该摒弃采用 rand 计算的方法。而采取独立的计算方法。例如 for 循环满足一定条件就睡觉,当然还得事先计算好一定时间内会计算多少条语句,编程之美第一道题就是这个,作者去脑补吧。^_^