diff --git a/src/pkg/math/big/nat.go b/src/pkg/math/big/nat.go index 2d5a5c9587..b2d6cd96c6 100644 --- a/src/pkg/math/big/nat.go +++ b/src/pkg/math/big/nat.go @@ -920,8 +920,10 @@ type divisor struct { ndigits int // digit length of divisor in terms of output base digits } -var cacheBase10 [64]divisor // cached divisors for base 10 -var cacheLock sync.Mutex // protects cacheBase10 +var cacheBase10 struct { + sync.Mutex + table [64]divisor // cached divisors for base 10 +} // expWW computes x**y func (z nat) expWW(x, y Word) nat { @@ -937,34 +939,28 @@ func divisors(m int, b Word, ndigits int, bb Word) []divisor { // determine k where (bb**leafSize)**(2**k) >= sqrt(x) k := 1 - for words := leafSize; words < m>>1 && k < len(cacheBase10); words <<= 1 { + for words := leafSize; words < m>>1 && k < len(cacheBase10.table); words <<= 1 { k++ } - // create new table of divisors or extend and reuse existing table as appropriate - var table []divisor - var cached bool - switch b { - case 10: - table = cacheBase10[0:k] // reuse old table for this conversion - cached = true - default: - table = make([]divisor, k) // new table for this conversion + // reuse and extend existing table of divisors or create new table as appropriate + var table []divisor // for b == 10, table overlaps with cacheBase10.table + if b == 10 { + cacheBase10.Lock() + table = cacheBase10.table[0:k] // reuse old table for this conversion + } else { + table = make([]divisor, k) // create new table for this conversion } // extend table if table[k-1].ndigits == 0 { - if cached { - cacheLock.Lock() // begin critical section - } - // add new entries as needed var larger nat for i := 0; i < k; i++ { if table[i].ndigits == 0 { if i == 0 { - table[i].bbb = nat(nil).expWW(bb, Word(leafSize)) - table[i].ndigits = ndigits * leafSize + table[0].bbb = nat(nil).expWW(bb, Word(leafSize)) + table[0].ndigits = ndigits * leafSize } else { table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb) table[i].ndigits = 2 * table[i-1].ndigits @@ -980,10 +976,10 @@ func divisors(m int, b Word, ndigits int, bb Word) []divisor { table[i].nbits = table[i].bbb.bitLen() } } + } - if cached { - cacheLock.Unlock() // end critical section - } + if b == 10 { + cacheBase10.Unlock() } return table diff --git a/src/pkg/math/big/nat_test.go b/src/pkg/math/big/nat_test.go index 68dd1a96d3..8dfbf092b4 100644 --- a/src/pkg/math/big/nat_test.go +++ b/src/pkg/math/big/nat_test.go @@ -409,6 +409,20 @@ func TestScanPi(t *testing.T) { } } +func TestScanPiParallel(t *testing.T) { + const n = 2 + c := make(chan int) + for i := 0; i < n; i++ { + go func() { + TestScanPi(t) + c <- 0 + }() + } + for i := 0; i < n; i++ { + <-c + } +} + func BenchmarkScanPi(b *testing.B) { for i := 0; i < b.N; i++ { var x nat @@ -416,6 +430,28 @@ func BenchmarkScanPi(b *testing.B) { } } +func BenchmarkStringPiParallel(b *testing.B) { + var x nat + x, _, _ = x.scan(strings.NewReader(pi), 0) + if x.decimalString() != pi { + panic("benchmark incorrect: conversion failed") + } + n := runtime.GOMAXPROCS(0) + m := b.N / n // n*m <= b.N due to flooring, but the error is neglibible (n is not very large) + c := make(chan int, n) + for i := 0; i < n; i++ { + go func() { + for j := 0; j < m; j++ { + x.decimalString() + } + c <- 0 + }() + } + for i := 0; i < n; i++ { + <-c + } +} + func BenchmarkScan10Base2(b *testing.B) { ScanHelper(b, 2, 10, 10) } func BenchmarkScan100Base2(b *testing.B) { ScanHelper(b, 2, 10, 100) } func BenchmarkScan1000Base2(b *testing.B) { ScanHelper(b, 2, 10, 1000) } @@ -516,7 +552,7 @@ func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) } func LeafSizeHelper(b *testing.B, base Word, size int) { b.StopTimer() originalLeafSize := leafSize - resetTable(cacheBase10[:]) + resetTable(cacheBase10.table[:]) leafSize = size b.StartTimer() @@ -533,7 +569,7 @@ func LeafSizeHelper(b *testing.B, base Word, size int) { } b.StopTimer() - resetTable(cacheBase10[:]) + resetTable(cacheBase10.table[:]) leafSize = originalLeafSize b.StartTimer() }