跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • 有关 Golang 多线程对数组操作时的性能问题
未分類
29 7 月 2019

有关 Golang 多线程对数组操作时的性能问题

有关 Golang 多线程对数组操作时的性能问题

資深大佬 : vcfghtyjc 43

我尝试着用多线程分别运行以下两个函数,并使用 time 来查看它们的 cpu 使用率,一个的使用率就符合启动的线程数(比如 8 线程就得到 800% 的使用率),另一个就无法得到相符合的 cpu 使用率 (比如 8 线程得到 600% 的使用率,2 线程得到 300% 的使用率)。虽然两个函数都没有线程间的互斥锁,这两个函数的区别在于:第一个函数没有对数组的操作,第二个函数有对数组的操作。具体函数如下:

函数 1: addOne

该函数持续做加法运算

func addOne(n int,wg *sync.WaitGroup){     var i int     for i < n{         i++     }     wg.Done() } 

函数 2: appendArray

该函数不断向一个数组中添加元素,为了防止内存溢出,每添加 10,000 个元素,就清空数组。

func appendArray(n int,wg *sync.WaitGroup){     var i int     list := make([]int,10000)     for i < n{         for j := 0 ; j < 10000; j++{             list = append(list, i)         }         list = make([]int,10000)         i++     }     wg.Done() } 

启动多线程的函数: test

该函数将工作总数 totalJobs 平分给每个线程

func test(totalThreads, totalJobs int){     n := totalJobs/totalThreads     var wg sync.WaitGroup     for i := 0; i < totalThreads; i++{         wg.Add(1)         go appendArray(n,&wg)         // go addOne(n,&wg)     }     wg.Wait() } 

通过使用 pprof, 两个代码的 cpu 分析如下:

函数 1: 有关 Golang 多线程对数组操作时的性能问题

函数 2: 有关 Golang 多线程对数组操作时的性能问题

我发现对于第二个函数,虽然代码中没有调用锁,在执行的过程中会出现 runtime lock, runtime futex 之类的锁操作。

这里我不明白的是:

  1. 为什么在有数组操作后,编译后的程序出现了锁操作,从而影响力 cpu 的使用?
  2. 如何避免 /减缓这些锁操作带来的性能影响?
大佬有話說 (17)

  • 資深大佬 : nifury

    因为 append 做不到原子操作吧?

  • 資深大佬 : ncwhale

    mem alloc 和 move 都是开销大头啊喵……没事别这么玩内存啊喵……碎片化和反复拷贝都是超级消耗资源的,而且,还因为内存 /缓存 /CPU 多核同步等问题,导致必须上锁否则会出现未知内存分布状态喵……

    正常一点做法就是别在循环里艹数组尺寸喵!提前预判一下分配空间就好!

    否则请使用其它数据结构而不是数组!

  • 資深大佬 : mcrwayfun

    我想主想表达的是切片而不是数组

  • 資深大佬 : gramyang

    我测试过 append 是可以直接操作一个切片声明而不报空指针的,所以 append 里面会调用 make 返回一个切片实例。这种操作必然是耗时操作。
    不过题主用的这个 pprof 看起来挺好用的,可以试试

  • 資深大佬 : Herobs

    make 的第二个参数是长度,第三个参数才是容量。

  • 資深大佬 : useben

    make 的第二个参数是长度,第三个参数才是容量。+1
    你这里还是会指数分配内存

  • 資深大佬 : nifury

    @mcrwayfun #3 啊瞎了。就扫了一眼没过大脑

  • 資深大佬 : zhs227

    append 很多情况下是对对内存进行 malloc, memcpy, free。这些操作未完成之前是不会返回的。所以你会看到各种锁。在有锁的状态下,其它的 goroutine 实际上是在等待。

  • 資深大佬 : dazhangpan

    用 time 查看 CPU 的使用率???

  • 資深大佬 : Smash

    歪个,主函数调用图是用什么生成的?

  • 資深大佬 : qieqie

    @Smash pprof 然后用 svg 或者别的图片格式导出

  • 資深大佬 : watsy0007

    “`golang
    func appendArray(n int,wg *sync.WaitGroup){
    var i int
    list := make([]int,10000)
    for i < n{
    for j := 0 ; j < 10000; j++{
    list[j] = i
    }
    list = make([]int,10000)
    i++
    }
    wg.Done()
    }
    “`

  • 資深大佬 : whoami9894

    原因就是#5 说的,应该`make([]int, 0, 10000)`

  • 資深大佬 : CEBBCAT

    鼓捣了大半天,被人家几小时之前就说过了,不爽啊……

    https://play.golang.org/p/3iTdWuMIgoe

  • 主 資深大佬 : vcfghtyjc

    @ncwhale 是否对于任何多线程程序都会出现这个问题(线程申请新的内存时,所有线程需要用锁来同步当前进程控制的内存)?如果用其他语言,如 C++,它的 overhead 还有这么高吗?

  • 主 資深大佬 : vcfghtyjc

    @dazhangpan 有什么推荐的工具吗?

  • 資深大佬 : fcten

    内存分配是必然要加锁的,因为堆内存是整个程序共享的
    比较现代的内存分配器会对多线程场景进行优化,把一部分内存划分为线程独占,从而减少锁的使用。

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具