There is a PR in golang source code to improve performance of channel recently

Genchi Lu
2 min readMay 25, 2020

--

Recently, I notice that there is a Golang’s PR with title: runtime: speed up receive on empty closed channel. PR comments is:

Currently, nonblocking receive on an open channel is about
700 times faster than nonblocking receive on a closed channel.
This change makes closed channels equally fast.

I was curious about how the PR solve this problem, so I spent a few time tracing this PR’s changing code. Before explaining it, let’s introduce how channel work.

How channel work

The data structure of channel is like that:

Every time sending data to channel, golang would write data to the element which sendx points to, increase the sendx to next element, and plus 1 to qcount.

Every time receiving data from channel, golang would read data from the element which recvx points to, increase the sendx to next element, and qcount minus 1.

As a reference, Below is go’s source code of channel struct:

Since we always use channel for communication between goroutines, it’s important to use lock to protect chan from race condition when modifying the three variables: qcount, sendx and recvx. And lock is exactly the key point of this PR.

How to make receiving on an open channel faster

Below is the key code snippet of receiving in channel from golang v1.14:

We can notice that:

  1. Go would check if a channel is empty or not (line 7~11).
  2. Go would check if a channel is closed or not (line 20~29).
  3. Go would do receiving command (after line 31).

Before go checking channel is close or not, it would execute lock (line 18) and entry critical session.

Lock cost quite a few. It’s not necessary to fetch a lock when checking close status of channel, since it does not change any variables. So that is what this PR’s solution: detecting clode channel before fetching lock.

Here is the code snippet from the PR’s change to function chanrecv in channel, it integrate all checking logic together before locking, see line 14~28 :

Execute receiving on an empty closed channel would never fetch lock, thus take off the performance impact of lock.

Measure

I build go from the latest source code, try to run the benchmark and compare with go 1.4.

note: when i was writing this blog, the latest go source code’s commit sha is cb14bd8306b7d5fb7b23afadb347dfb697b252af

Here is the code to run the benchmark:

And the result of running the benchmark in my Mac:

It do speed up, cool!

--

--