Skip to content

Commit

Permalink
Add tests for proportionality
Browse files Browse the repository at this point in the history
Evaluate measured cputime using the tests from
github.com/golang/go/issues/36821, which demonstrate the inaccuracy and
imprecision of go's 100Hz cpu profiler. The results here suggest that
measured cputime is both accurate and precise with regards to computing
on-CPU time.

    === RUN   TestEquivalentGoroutines
        0's got  9.98% of total time
        1's got  9.53% of total time
        2's got  9.22% of total time
        3's got 10.42% of total time
        4's got  9.84% of total time
        5's got 10.43% of total time
        6's got 10.50% of total time
        7's got 10.21% of total time
        8's got 10.03% of total time
        9's got  9.86% of total time

    === RUN   TestProportionalGoroutines
        0's got  1.87% of total time (1.000000x)
        1's got  3.60% of total time (1.931999x)
        2's got  5.41% of total time (2.899312x)
        3's got  7.21% of total time (3.864451x)
        4's got  9.11% of total time (4.880925x)
        5's got 10.94% of total time (5.864723x)
        6's got 12.77% of total time (6.842004x)
        7's got 14.34% of total time (7.685840x)
        8's got 16.58% of total time (8.885060x)
        9's got 18.18% of total time (9.741030x)
  • Loading branch information
irfansharif committed May 12, 2022
1 parent f47cb8d commit 97d573c
Showing 1 changed file with 100 additions and 0 deletions.
100 changes: 100 additions & 0 deletions runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,106 @@ func TestInternalDuration(t *testing.T) {
}
}

// TestEquivalentGoroutines is a variant of the parallel test in
// https://github.com/golang/go/issues/36821.
func TestEquivalentGoroutines(t *testing.T) {
mu := struct {
sync.Mutex
nanos map[int]int64
}{}
mu.nanos = make(map[int]int64)

f := func(wg *sync.WaitGroup, id int) {
defer wg.Done()

var sum int
for i := 0; i < 500000000; i++ {
sum -= i / 2
sum *= i
sum /= i/3 + 1
sum -= i / 4
}

nanos := grunningnanos()
mu.Lock()
mu.nanos[id] = nanos
mu.Unlock()
}

var wg sync.WaitGroup
for i := 0; i < 10; i++ {
i := i // copy loop variable
wg.Add(1)
go f(&wg, i)
}
wg.Wait()

mu.Lock()
defer mu.Unlock()

total := int64(0)
for _, nanos := range mu.nanos {
total += nanos
}

minexp, maxexp := float64(0.085), float64(0.115)
for i, nanos := range mu.nanos {
got := float64(nanos) / float64(total)

assert.Greaterf(t, got, minexp,
"expected proportion > %f, got %f", minexp, got)
assert.Lessf(t, got, maxexp,
"expected proportion < %f, got %f", maxexp, got)

t.Logf("%d's got %0.2f%% of total time", i, got*100)
}
}

// TestProportionalGoroutines is a variant of the serial test in
// https://github.com/golang/go/issues/36821.
func TestProportionalGoroutines(t *testing.T) {
f := func(wg *sync.WaitGroup, v uint64, trip uint64, result *int64) {
defer wg.Done()

ret := v
for i := trip; i > 0; i-- {
ret += i
ret = ret ^ (i + 0xcafebabe)
}

nanos := grunningnanos()
atomic.AddInt64(result, nanos)
}

results := make([]int64, 10, 10)
var wg sync.WaitGroup

for iters := 0; iters < 10000; iters++ {
for i := uint64(0); i < 10; i++ {
i := i // copy loop variable
wg.Add(1)
go f(&wg, i+1, (i+1)*100000, &results[i])
}
}

wg.Wait()

total := int64(0)
for _, result := range results {
total += result
}

initial := float64(results[0]) / float64(total)
maxdelta := float64(0.5)
for i, result := range results {
got := float64(result) / float64(total)
mult := got / initial
assert.InDelta(t, float64(i+1), mult, maxdelta)

t.Logf("%d's got %0.2f%% of total time (%fx)", i, got*100, mult)
}
}

// BenchmarkMetricNanos measures how costly it is to read the current
// goroutine's running time when going through the exported runtime metric.
func BenchmarkMetricNanos(b *testing.B) {
Expand Down

0 comments on commit 97d573c

Please sign in to comment.