Skip to content

Instantly share code, notes, and snippets.

@a-robinson
Last active March 14, 2017 20:49
Show Gist options
  • Save a-robinson/27a75e4a2cc6f32e955dad5d7d513958 to your computer and use it in GitHub Desktop.
Save a-robinson/27a75e4a2cc6f32e955dad5d7d513958 to your computer and use it in GitHub Desktop.
package cgosleep
/*
#include "unistd.h"
void sleepMicros(unsigned int micros) {
usleep(micros);
}
*/
import "C"
import (
"sync"
"time"
)
type commit struct {
sync.Mutex
cond *sync.Cond
committing bool
commitSeq uint64
pendingSeq uint64
pending []time.Duration
}
var c commit
func init() {
c.cond = sync.NewCond(&c.Mutex)
}
func batchedSleep(d time.Duration) {
c.Lock()
leader := len(c.pending) == 0
c.pending = append(c.pending, d)
seq := c.pendingSeq
if leader {
// We're the leader. Wait for any running commit to finish.
for c.committing {
c.cond.Wait()
}
pending := c.pending
c.pending = nil
c.pendingSeq++
c.committing = true
c.Unlock()
var max time.Duration
for _, dur := range pending {
if dur > max {
max = dur
}
}
C.sleepMicros(C.uint(max / time.Microsecond))
c.Lock()
c.committing = false
c.commitSeq = seq
c.cond.Broadcast()
} else {
// We're a follower. Wait for the commit to finish.
for c.commitSeq < seq {
c.cond.Wait()
}
}
c.Unlock()
}
func benchmarkCgoSleep(n int) {
var wg sync.WaitGroup
for i := 0; i < n; i++ {
//fmt.Println(i)
wg.Add(1)
time.Sleep(100 * time.Microsecond)
go func() {
C.sleepMicros(C.uint(5 * time.Millisecond / time.Microsecond))
wg.Done()
}()
}
wg.Wait()
}
func benchmarkBatchedSleep(n int) {
var wg sync.WaitGroup
for i := 0; i < n; i++ {
wg.Add(1)
time.Sleep(100 * time.Microsecond)
go func() {
batchedSleep(5 * time.Millisecond)
wg.Done()
}()
}
wg.Wait()
}
package cgosleep
import (
"testing"
"time"
)
/*
func BenchmarkCgoSleep(b *testing.B) {
benchmarkCgoSleep(b.N)
}
func BenchmarkBatchedSleep(b *testing.B) {
benchmarkBatchedSleep(b.N)
}
*/
func TestCgoSleep(t *testing.T) {
start := time.Now()
benchmarkCgoSleep(2000)
t.Error(time.Since(start))
}
func TestBatchedSleep(t *testing.T) {
start := time.Now()
benchmarkBatchedSleep(2000)
t.Error(time.Since(start))
}
$ go test -bench=.
BenchmarkCgoSleep-8 10000 136946 ns/op
BenchmarkBatchedSleep-8 10000 136282 ns/op
PASS
ok _/Users/alex/play/run 2.798s
$ go test .
--- FAIL: TestCgoSleep (1.00s)
cgo_sleep_test.go:23: 1.001945863s
--- FAIL: TestBatchedSleep (0.28s)
cgo_sleep_test.go:29: 284.268565ms
FAIL
FAIL _/Users/alex/play/run 1.295s
@petermattis
Copy link

petermattis commented Mar 14, 2017

Hmm, I'm seeing different benchmark numbers (this is on Mac OS X):

BenchmarkCgoSleep-8       	   10000	    701665 ns/op
BenchmarkBatchedSleep-8   	   10000	    134935 ns/op

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment