测试Channel和Lock之间的性能差别

1,970 views
Skip to first unread message

陆文杰

unread,
Jan 17, 2013, 8:00:09 AM1/17/13
to golang...@googlegroups.com
最近在看<<The Way To Go>>一书,上面写到应该用Channel来传递数据,会比用mutex锁更有效率。于是我做了以下Benchmark测试。(PS 已经通过runtime设置了GOMAXPROCS 为CPU个数)
简单的生产者-消费者模式。
单消费者不断从Channel里拿数据,并append到自己的切片里。
代码如下
type NodeList struct {
   nodes [ ]*Node
   buffer chan *Node
   mutex sync.Mutex 
}

//启动100个生产者
for i := 0; i < 100; i++ {
   go producer( ) {
       for i := 0; i < 1000; i++ {
          node := NewNode( )
          NodeList.buffer <- node
       }
    }
}

go consumer( ) {
    for {
        NodeList.nodes = append(NodeList.nodes,  <-NodeList.chan)
    }
}

而用mutex的伪码如下:
func AddNode(node *Node) {
     NodeList.mutex.Lock( )
     defer NodeList.mutex.UnLock( )
     NodeList.nodes = append(NodeList.nodes, node)
}

Buffer用的channel大小我初始化为1024。但是性能上还不如用Lock。

我想问:channel性能不如Lock的原因。还是因为我的代码写不正确?
谢谢!

Ruiqi Hong

unread,
Jan 17, 2013, 8:13:53 AM1/17/13
to golang...@googlegroups.com
借问一下,按照目前gc的情况,Mutex和channel各适用于什么样的场景


--
--
官网: http://golang-china.org/
IRC: irc.freenode.net #golang-china
@golangchina
 
 
 

tegoor

unread,
Jan 17, 2013, 8:31:37 AM1/17/13
to golang...@googlegroups.com

As a general guide, though:

ChannelMutex
passing ownership of data,

distributing units of work,

communicating async results
caches,

state


via: http://code.google.com/p/go-wiki/wiki/MutexOrChannel

lihui

unread,
Jan 17, 2013, 8:32:40 AM1/17/13
to golang...@googlegroups.com
书上肯定写错了,channel不但牵扯到锁,还需要执行调度,因此怎么都不可能比锁更高效。

个人觉得用mutex或者用channel与GC似乎无关,只与逻辑有关,怎么简单就该怎么来。


2013/1/17 陆文杰 <fio...@gmail.com>
mutex


steve wang

unread,
Jan 17, 2013, 9:05:12 AM1/17/13
to golang...@googlegroups.com
不讨论效率的话,我现在认为锁和channel在语义上是不同的东西,虽然有时两者可以用来做同一件事情。
锁的本质是界定临界区,而channel的本质是通讯;虽然也可以用锁来实现通讯,用channel来界定临界区。

steve wang

unread,
Jan 17, 2013, 12:15:36 PM1/17/13
to golang...@googlegroups.com
Rob Pike:
Goroutines and channels are big ideas. They're tools for program construction.
But sometimes all you need is a reference counter.
Go has "sync" and "sync/atomic" packages that provide mutexes, condition variables, etc. They provide tools for smaller problems.
Often, these things will work together to solve a bigger problem.
Always use the right tool for the job.

On Thursday, January 17, 2013 9:00:09 PM UTC+8, 陆文杰 wrote:

Risingv

unread,
Jan 17, 2013, 9:41:07 PM1/17/13
to golang...@googlegroups.com
Mutex和Message Passing(即channel)解决的都是race condition的问题
只是解决的思路不一样,而后者的实现成本一般都比较高,golang便是这样的
mutex或者semaphore更高效, 但channel让并发编程更容易
两者之间的取舍就是(效率+低可维护性)和(相对低效率+高可维护性)之间的取舍

这里有一个很好的比较例子,它们解决了相同的问题




2013/1/18 steve wang <steve....@gmail.com>

--
--
官网: http://golang-china.org/
IRC: irc.freenode.net #golang-china
@golangchina
 
 
 



--
Life is in chaos, keep struggling.
Personal blog: risingv.in

steve wang

unread,
Jan 17, 2013, 9:54:40 PM1/17/13
to golang...@googlegroups.com
我认为它们对应的现实模型是不一样的。
channel:本质是通讯,类比邮件、电话、交谈
lock:本质是公共数据,类比rob pike演示中多个gopher去搬运的那堆书。

Chen Yufei

unread,
Jan 17, 2013, 9:56:48 PM1/17/13
to golang...@googlegroups.com
On 2013年1月18日Friday at 上午10:41, Risingv wrote:
> Mutex和Message Passing(即channel)解决的都是race condition的问题

message passing 更多的是一种 programming paradigm。
mutex 才是专门用来解决 race condition 的一种方式。

比如科学计算里的 MPI 跟 mutex 一点关系都没有。

> 只是解决的思路不一样,而后者的实现成本一般都比较高,golang便是这样的
> mutex或者semaphore更高效, 但channel让并发编程更容易

> 两者之间的取舍就是(效率+低可维护性)和(相对低效率+高可维护性)之间的取舍

那个方便还是看具体场景吧。Rob Pike 的 reference counter 的例子里用 mutex 就比用 channel 更方便。

至于 mutex 是否一定让程序更高效还要看程序组织。
goroutine & channel 提供了一种新的程序组织方式,更容易从设计上避免共享,在多核上更容易得到伸缩性 。


另外觉得 channel 是否一定比 mutex 低效还要看具体实现吧,minux 应该可以提供更多解释。
>
> 这里有一个很好的比较例子,它们解决了相同的问题
> http://nf.id.au/concurrency-patterns-a-source-of-unique-numbe (注意看评论)
>
>
>
>
> 2013/1/18 steve wang <steve....@gmail.com (mailto:steve....@gmail.com)>
> > IRC: irc.freenode.net (http://irc.freenode.net) #golang-china
> > @golangchina
> >
> >
> >
>
>
>
>
> --
> Life is in chaos, keep struggling.
> Personal blog: risingv.in (http://risingv.in)
>
>
> --
> --
> 官网: http://golang-china.org/
> IRC: irc.freenode.net (http://irc.freenode.net) #golang-china
> @golangchina
>
>
>



lihui

unread,
Jan 17, 2013, 9:57:50 PM1/17/13
to golang...@googlegroups.com
在计数器的场合中,使用原子操作的方案是不是更好些?

2013/1/18 Risingv <franc...@gmail.com>

Chen Yufei

unread,
Jan 17, 2013, 10:10:17 PM1/17/13
to golang...@googlegroups.com
On 2013年1月18日Friday at 上午10:57, lihui wrote:
> 在计数器的场合中,使用原子操作的方案是不是更好些?

sync/atomic 应该比用 mutex 更快一些,看 go 源码这个 package 大部分是直接用汇编实现的。

sync.Mutex 本身就是用 sync/atomic 里提供的操作实现的。
>
> 2013/1/18 Risingv <franc...@gmail.com (mailto:franc...@gmail.com)>

Risingv

unread,
Jan 17, 2013, 10:49:04 PM1/17/13
to golang...@googlegroups.com
在 2013年1月18日上午10:56,Chen Yufei <cyfd...@gmail.com>写道:
On 2013年1月18日Friday at 上午10:41, Risingv wrote:
> Mutex和Message Passing(即channel)解决的都是race condition的问题

message passing 更多的是一种 programming paradigm。
mutex 才是专门用来解决 race condition 的一种方式。

比如科学计算里的 MPI 跟 mutex 一点关系都没有。
 
      message passing更多是解决ipc问题,但我认为其本质就是一个lock-based FIFO Queue
      它天然地保证了上锁和解锁的顺序,所以使用起来很方便,也使它天然地易于解决race condition
 

> 只是解决的思路不一样,而后者的实现成本一般都比较高,golang便是这样的
> mutex或者semaphore更高效, 但channel让并发编程更容易

> 两者之间的取舍就是(效率+低可维护性)和(相对低效率+高可维护性)之间的取舍

那个方便还是看具体场景吧。Rob Pike 的 reference counter 的例子里用 mutex 就比用 channel 更方便。

至于 mutex 是否一定让程序更高效还要看程序组织。
goroutine & channel 提供了一种新的程序组织方式,更容易从设计上避免共享,在多核上更容易得到伸缩性 。


另外觉得 channel 是否一定比 mutex 低效还要看具体实现吧,minux 应该可以提供更多解释。

   从程序角度考虑,这个问题自然是没有固定结论的。
   我的意思是在一个很小的上下文中,chanel & goroutine的开销往往比Mutex要大,
   chanel要和goroutine一起使用才有威力,而一个goroutine至少需要4k+的内存,而mutex的开销要小得多

   当并行的逻辑和上下文变得复杂,那么这种开销就变得值得了,没必要去节省那一点点的内存的cpu时间
   我想Rob Pike想传达的是这个意思吧
   
IRC:  irc.freenode.net     #golang-china
@golangchina



Chen Yufei

unread,
Jan 18, 2013, 12:24:43 AM1/18/13
to golang...@googlegroups.com
On 2013年1月18日Friday at 上午11:49, Risingv wrote:
>
> 在 2013年1月18日上午10:56,Chen Yufei <cyfd...@gmail.com (mailto:cyfd...@gmail.com)>写道:
> > On 2013年1月18日Friday at 上午10:41, Risingv wrote:
> > > Mutex和Message Passing(即channel)解决的都是race condition的问题
> >
> >
> > message passing 更多的是一种 programming paradigm。
> > mutex 才是专门用来解决 race condition 的一种方式。
> >
> > 比如科学计算里的 MPI 跟 mutex 一点关系都没有。
>
> message passing更多是解决ipc问题,但我认为其本质就是一个lock-based FIFO Queue

message passing 是提供了一种通信机制,不过它在概念上跟 lock 没什么本质联系。

多核 CPU 的 cache coherence protocol 其实就是 message passing 的方式实现的,也就是说虽然我们是用 coherent shared memory 来实现 lock,但在底层是用 message passing 实现的。

如果 CPU 暴露 message passing 的功能给程序员,那 message passing 在某些场合下效率就会比 lock based programming 更高。

> 它天然地保证了上锁和解锁的顺序,所以使用起来很方便,也使它天然地易于解决race condition
即使用 lock 来实现 message passing,拿锁解锁顺序也没保证。多个 goroutine 同时向一个 channel 发数据,在接收端先收到哪个是不能保证的。

没有任何的共享就不会有 race condition。但实际程序中总是会有一些东西需要共享,所以需要某种同步机制控制对共享资源的访问。

mutex 是同步机制的一种(互斥访问),channel 提供了另一种同步机制,保证通信和同步同时发生(buffer size 为 0)。但是注意 message passing 本身并没有提供同步。

基于 lock 的同步机制难以使用,channel 好用并只是因为 message passing。

Risingv

unread,
Jan 18, 2013, 1:50:28 AM1/18/13
to golang...@googlegroups.com
在 2013年1月18日下午1:24,Chen Yufei <cyfd...@gmail.com>写道:
On 2013年1月18日Friday at 上午11:49, Risingv wrote:
>
> 在 2013年1月18日上午10:56,Chen Yufei <cyfd...@gmail.com (mailto:cyfd...@gmail.com)>写道:
> > On 2013年1月18日Friday at 上午10:41, Risingv wrote:
> > > Mutex和Message Passing(即channel)解决的都是race condition的问题
> >
> >
> > message passing 更多的是一种 programming paradigm。
> > mutex 才是专门用来解决 race condition 的一种方式。
> >
> > 比如科学计算里的 MPI 跟 mutex 一点关系都没有。
>
> message passing更多是解决ipc问题,但我认为其本质就是一个lock-based FIFO Queue

message passing 是提供了一种通信机制,不过它在概念上跟 lock 没什么本质联系。

多核 CPU 的 cache coherence protocol 其实就是 message passing 的方式实现的,也就是说虽然我们是用 coherent shared memory 来实现 lock,但在底层是用 message passing 实现的。

如果 CPU 暴露 message passing 的功能给程序员,那 message passing 在某些场合下效率就会比 lock based programming 更高。
 
底层的硬件,当然能保证message  passing中send和receive的原子性,又何需锁,效率也高。高层中,要实现message passing
保证send和receive的原子性,本身肯定是包含隐式的锁的。“channel 是否一定比 mutex 低效还要看具体实现吧” 这话是没错,
但是在软件层,多数情况,还是前者的实现比后者慢


> 它天然地保证了上锁和解锁的顺序,所以使用起来很方便,也使它天然地易于解决race condition
即使用 lock 来实现 message passing,拿锁解锁顺序也没保证。多个 goroutine 同时向一个 channel 发数据,在接收端先收到哪个是不能保证的。
 
我的意思不是指多个goroutine向一个channel操作,能保证顺序,而是每次send和receive的上锁和解锁的问题不会出错。
举个例子就是,假设32位赋值是原子的,而64为赋值不是原子的,那么多个routine通过channel向一个64位int赋值时,不会造成其前32位和后32位的不一致。而如果用一个锁去保护这个64位int,那么锁是需要我们显式地去操作的,而自己控制锁往往容易犯错

没有任何的共享就不会有 race condition。但实际程序中总是会有一些东西需要共享,所以需要某种同步机制控制对共享资源的访问。

mutex 是同步机制的一种(互斥访问),channel 提供了另一种同步机制,保证通信和同步同时发生(buffer size 为 0)。但是注意 message passing 本身并没有提供同步。

基于 lock 的同步机制难以使用,channel 好用并只是因为 message passing。
 
可能你想阐述清除message passing和Mutex本质上的不同,而我则想说实现上,两者是相关的,实现上好比channel的每次send和recieve都受到一个互斥锁的保护。

>
> >
> > > 只是解决的思路不一样,而后者的实现成本一般都比较高,golang便是这样的
> > > mutex或者semaphore更高效, 但channel让并发编程更容易
> >
> > > 两者之间的取舍就是(效率+低可维护性)和(相对低效率+高可维护性)之间的取舍
> >
> >
> > 那个方便还是看具体场景吧。Rob Pike 的 reference counter 的例子里用 mutex 就比用 channel 更方便。
> >
> > 至于 mutex 是否一定让程序更高效还要看程序组织。
> > goroutine & channel 提供了一种新的程序组织方式,更容易从设计上避免共享,在多核上更容易得到伸缩性 。
> >
> >
> > 另外觉得 channel 是否一定比 mutex 低效还要看具体实现吧,minux 应该可以提供更多解释。
>
> 从程序角度考虑,这个问题自然是没有固定结论的。
> 我的意思是在一个很小的上下文中,chanel & goroutine的开销往往比Mutex要大,
> chanel要和goroutine一起使用才有威力,而一个goroutine至少需要4k+的内存,而mutex的开销要小得多
>
> 当并行的逻辑和上下文变得复杂,那么这种开销就变得值得了,没必要去节省那一点点的内存的cpu时间
> 我想Rob Pike想传达的是这个意思吧
>



--
--
官网: http://golang-china.org/
IRC:  irc.freenode.net     #golang-china
@golangchina



Chen Yufei

unread,
Jan 18, 2013, 2:50:54 AM1/18/13
to golang...@googlegroups.com
On 2013年1月18日Friday at 下午2:50, Risingv wrote:
>
>
>
> 在 2013年1月18日下午1:24,Chen Yufei <cyfd...@gmail.com (mailto:cyfd...@gmail.com)>写道:
> > On 2013年1月18日Friday at 上午11:49, Risingv wrote:
> > >
> > > 在 2013年1月18日上午10:56,Chen Yufei <cyfd...@gmail.com (mailto:cyfd...@gmail.com) (mailto:cyfd...@gmail.com)>写道:
> > > > On 2013年1月18日Friday at 上午10:41, Risingv wrote:
> > > > > Mutex和Message Passing(即channel)解决的都是race condition的问题
> > > >
> > > >
> > > >
> > > > message passing 更多的是一种 programming paradigm。
> > > > mutex 才是专门用来解决 race condition 的一种方式。
> > > >
> > > > 比如科学计算里的 MPI 跟 mutex 一点关系都没有。
> > >
> > > message passing更多是解决ipc问题,但我认为其本质就是一个lock-based FIFO Queue
> >
> > message passing 是提供了一种通信机制,不过它在概念上跟 lock 没什么本质联系。
> >
> > 多核 CPU 的 cache coherence protocol 其实就是 message passing 的方式实现的,也就是说虽然我们是用 coherent shared memory 来实现 lock,但在底层是用 message passing 实现的。
> >
> > 如果 CPU 暴露 message passing 的功能给程序员,那 message passing 在某些场合下效率就会比 lock based programming 更高。
>
> 底层的硬件,当然能保证message passing中send和receive的原子性,又何需锁,效率也高。高层中,要实现message passing
> 保证send和receive的原子性,本身肯定是包含隐式的锁的。“channel 是否一定比 mutex 低效还要看具体实现吧” 这话是没错,
> 但是在软件层,多数情况,还是前者的实现比后者慢
> >
> > > 它天然地保证了上锁和解锁的顺序,所以使用起来很方便,也使它天然地易于解决race condition
> > 即使用 lock 来实现 message passing,拿锁解锁顺序也没保证。多个 goroutine 同时向一个 channel 发数据,在接收端先收到哪个是不能保证的。
>
>
> 我的意思不是指多个goroutine向一个channel操作,能保证顺序,而是每次send和receive的上锁和解锁的问题不会出错。
> 举个例子就是,假设32位赋值是原子的,而64为赋值不是原子的,那么多个routine通过channel向一个64位int赋值时,不会造成其前32位和后32位的不一致。而如果用一个锁去保护这个64位int,那么锁是需要我们显式地去操作的,而自己控制锁往往容易犯错
>
> > 没有任何的共享就不会有 race condition。但实际程序中总是会有一些东西需要共享,所以需要某种同步机制控制对共享资源的访问。
> >
> > mutex 是同步机制的一种(互斥访问),channel 提供了另一种同步机制,保证通信和同步同时发生(buffer size 为 0)。但是注意 message passing 本身并没有提供同步。
> >
> > 基于 lock 的同步机制难以使用,channel 好用并只是因为 message passing。
>
> 可能你想阐述清除message passing和Mutex本质上的不同,而我则想说实现上,两者是相关的,实现上好比channel的每次send和recieve都受到一个互斥锁的保护。

我的确是想说这两者是不同的概念。

关于 coherence protocol 的描述想说的是,现在实现 message passing 需要锁是由于我们只有 coherent shared memory,绕了一圈回到了 message passing。

如果把底层的 message passing 暴露给程序员的话有助于在软件里更高效地实现 message passing,也可以设计出伸缩性更好的锁。(参考 x86 上 monitor/mwait 指令,现在这两条指令只有 kernel 可以使用。)
> > > >
> > > > > 只是解决的思路不一样,而后者的实现成本一般都比较高,golang便是这样的
> > > > > mutex或者semaphore更高效, 但channel让并发编程更容易
> > > >
> > > >
> > > > > 两者之间的取舍就是(效率+低可维护性)和(相对低效率+高可维护性)之间的取舍
> > > >
> > > >
> > > > 那个方便还是看具体场景吧。Rob Pike 的 reference counter 的例子里用 mutex 就比用 channel 更方便。
> > > >
> > > > 至于 mutex 是否一定让程序更高效还要看程序组织。
> > > > goroutine & channel 提供了一种新的程序组织方式,更容易从设计上避免共享,在多核上更容易得到伸缩性 。
> > > >
> > > >
> > > > 另外觉得 channel 是否一定比 mutex 低效还要看具体实现吧,minux 应该可以提供更多解释。
> > >
> > > 从程序角度考虑,这个问题自然是没有固定结论的。
> > > 我的意思是在一个很小的上下文中,chanel & goroutine的开销往往比Mutex要大,
> > > chanel要和goroutine一起使用才有威力,而一个goroutine至少需要4k+的内存,而mutex的开销要小得多
> > >
> > > 当并行的逻辑和上下文变得复杂,那么这种开销就变得值得了,没必要去节省那一点点的内存的cpu时间
> > > 我想Rob Pike想传达的是这个意思吧
> >
> >
> >
> >
> > --
> > --
> > 官网: http://golang-china.org/
> > IRC: irc.freenode.net (http://irc.freenode.net) #golang-china
> > @golangchina
>
>
>
>
> --
> Life is in chaos, keep struggling.
> Personal blog: risingv.in (http://risingv.in)
>
>
>
> --
> --
> 官网: http://golang-china.org/
> IRC: irc.freenode.net (http://irc.freenode.net) #golang-china
> @golangchina
>
>
>



Chen Yufei

unread,
Jan 18, 2013, 3:32:01 AM1/18/13
to golang...@googlegroups.com
On 2013年1月17日Thursday at 下午9:00, 陆文杰 wrote:
> 最近在看<<The Way To Go>>一书,上面写到应该用Channel来传递数据,会比用mutex锁更有效率。于是我做了以下Benchmark测试。(PS 已经通过runtime设置了GOMAXPROCS 为CPU个数)
> 简单的生产者-消费者模式。
> 单消费者不断从Channel里拿数据,并append到自己的切片里。
> 代码如下

你给的代码不能直接拿来跑。
我写了一个放在 gist 上了 https://gist.github.com/4562635 ,去掉了 new 之类的操作,避免带来影响。

用锁的版本用 go test -test.bench Bench 跑的时候有时会 index out of range,没找到原因。不过测性能应该没影响。
> type NodeList struct {
> nodes [ ]*Node
> buffer chan *Node
> mutex sync.Mutex
> }
>
> Buffer用的channel大小我初始化为1024。但是性能上还不如用Lock。
我测试时用跟 cpu 相同数量的 producer,如果 channel buffer 大小跟总的产生的 item 数量相同的话 channel 没有慢太多。

如果 channel buffer 大小跟 producer 相同则还是差不少的。
>
> 我想问:channel性能不如Lock的原因。还是因为我的代码写不正确?
channel send 的实现代码在 $GOROOT/src/pkg/runtime/chan.c 里,应该是 runtime.chansend 函数。
sync.Mutex 的实现在 $GOROOT/src/pkg/sync/mutex.go,Lock/Unlock 利用了 sync/atomic 里用汇编实现的原子操作。

从代码看 channel send 就要比 mutex lock 复杂很多,channel send 涉及到 scheduling 等操作,比 mutex 慢还是很正常的。
>
> 谢谢!

Chen Yufei

unread,
Jan 18, 2013, 4:20:08 AM1/18/13
to golang...@googlegroups.com
On 2013年1月18日Friday at 下午4:32, Chen Yufei wrote:
> On 2013年1月17日Thursday at 下午9:00, 陆文杰 wrote:
> > 最近在看<<The Way To Go>>一书,上面写到应该用Channel来传递数据,会比用mutex锁更有效率。于是我做了以下Benchmark测试。(PS 已经通过runtime设置了GOMAXPROCS 为CPU个数)
> > 简单的生产者-消费者模式。
> > 单消费者不断从Channel里拿数据,并append到自己的切片里。
> > 代码如下
>
> 你给的代码不能直接拿来跑。
> 我写了一个放在 gist 上了 https://gist.github.com/4562635 ,去掉了 new 之类的操作,避免带来影响。
>
> 用锁的版本用 go test -test.bench Bench 跑的时候有时会 index out of range,没找到原因。不过测性能应该没影响。
问题 fix 了,有兴趣的同学可以自己测试下。


Liigo Zhuang

unread,
Jan 20, 2013, 9:17:40 PM1/20/13
to golang...@googlegroups.com

+1
channel是更高层次上的抽象概念,内部估计也是借用mutex之类同步手段。

Liigo Zhuang

unread,
Jan 20, 2013, 9:26:55 PM1/20/13
to golang...@googlegroups.com


在 2013-1-18 上午11:49,"Risingv" <franc...@gmail.com>写道:
>
>
>
>
> 在 2013年1月18日上午10:56,Chen Yufei <cyfd...@gmail.com>写道:
>
>> On 2013年1月18日Friday at 上午10:41, Risingv wrote:
>> > Mutex和Message Passing(即channel)解决的都是race condition的问题
>>
>> message passing 更多的是一种 programming paradigm。
>> mutex 才是专门用来解决 race condition 的一种方式。
>>
>> 比如科学计算里的 MPI 跟 mutex 一点关系都没有。
>
>  
>       message passing更多是解决ipc问题,但我认为其本质就是一个lock-based FIFO Queue
>       它天然地保证了上锁和解锁的顺序,所以使用起来很方便,也使它天然地易于解决race condition
>  

+1 这channel的本质就是一个基于全局锁的先进先出的缓存队列。

Reply all
Reply to author
Forward
0 new messages