根据数组大小,可能会出现意外的减速或突然减速。

huangapple go评论82阅读模式
英文:

OR sudden slowdown depending on array size

问题

我写了一个简单的程序,它对一个巨大的切片中包含的所有值进行逻辑或运算。当我使用一个大小为原来的10倍的切片时,我期望性能下降10倍。然而,在执行提供的测试时,存在巨大的性能差距。程序的输出如下所示:

oadam@oadam-Latitude-E6510:~/$ go test -bench .
testing: warning: no tests to run
PASS
BenchmarkLittle	2000000000	         0.11 ns/op
BenchmarkBig	       1	2417869962 ns/op
ok  	_/home/oadam/	5.048s

以下是代码:

package main

import (
    "math/rand"
    "testing"
)

const (
    little = 5000000
    big    = 50000000
)

var a = make([]uint32, big)

func benchOR(b *testing.B, l int) {
    for i := 0; i < l; i++ {
        a[i] = rand.Uint32()
    }

    var result uint32
    for i := 0; i < l; i++ {
        result |= a[i]
    }
}

func BenchmarkLittle(b *testing.B) {
    benchOR(b, little)
}

func BenchmarkBig(b *testing.B) {
    benchOR(b, big)
}

编辑:可能是go test -bench中的一个错误。使用手动计时我无法复现。

package main

import (
    "log"
    "math/rand"
    "time"
)

const (
    little = 5000000
    big    = 50000000
)

var a = make([]uint32, big)

func initA(l int) {
    for i := 0; i < l; i++ {
        a[i] = rand.Uint32()
    }
}

func test(l int) uint32 {
    var result uint32
    for i := 0; i < l; i++ {
        result |= a[i]
    }
    return result
}

func main() {
    initA(little)
    var before = time.Now()
    test(little)
    log.Println(time.Since(before))

    initA(big)
    var before2 = time.Now()
    test(big)
    log.Println(time.Since(before2))

}
英文:

I wrote a simple program that ORs all the values contained in a huge go slice. When I use a 10 times bigger slice, I would expect a 10x performance drop. However when executing the provided test, there is a huge performance gap. The program output is the following :

oadam@oadam-Latitude-E6510:~/$ go test -bench .
testing: warning: no tests to run
PASS
BenchmarkLittle	2000000000	         0.11 ns/op
BenchmarkBig	       1	2417869962 ns/op
ok  	_/home/oadam/	5.048s

And the code

package main

import (
	&quot;math/rand&quot;
	&quot;testing&quot;
)

const (
	little = 5000000
	big    = 50000000
)

var a = make([]uint32, big)

func benchOR(b *testing.B, l int) {
	for i := 0; i &lt; l; i++ {
		a[i] = rand.Uint32()
	}

	var result uint32
	for i := 0; i &lt; l; i++ {
		result |= a[i]
	}
}

func BenchmarkLittle(b *testing.B) {
	benchOR(b, little)
}

func BenchmarkBig(b *testing.B) {
	benchOR(b, big)
}

EDIT: must be a bug in go test -bench. Using a manual timing I don't reproduce

package main

import (
	&quot;log&quot;
	&quot;math/rand&quot;
	&quot;time&quot;
)

const (
	little = 5000000
	big    = 50000000
)

var a = make([]uint32, big)

func initA(l int) {
	for i := 0; i &lt; l; i++ {
		a[i] = rand.Uint32()
	}
}

func test(l int) uint32 {
	var result uint32
	for i := 0; i &lt; l; i++ {
		result |= a[i]
	}
	return result
}

func main() {
	initA(little)
	var before = time.Now()
	test(little)
	log.Println(time.Since(before))

	initA(big)
	var before2 = time.Now()
	test(big)
	log.Println(time.Since(before2))

}

答案1

得分: 5

问题在于你没有使用b.N,它告诉你要运行基准测试的次数。此外,如果你只想对OR操作进行基准测试,你可能应该只初始化数组一次,或者至少调用b.ResetTimer()以便初始化不被计入时间。

以下是我得到的代码,它给出了预期的结果:

package main

import (
	"math/rand"
	"testing"
)

const (
	little = 5000000
	big    = 50000000
)

var a = make([]uint32, big)

func init() {
	for i := 0; i < big; i++ {
		a[i] = rand.Uint32()
	}
}

func benchOR(b *testing.B, l int) {
	var result uint32
	for _, u := range a[:l] {
		result |= u
	}
}

func BenchmarkLittle(b *testing.B) {
	for i := 0; i < b.N; i++ {
		benchOR(b, little)
	}
}

func BenchmarkBig(b *testing.B) {
	for i := 0; i < b.N; i++ {
		benchOR(b, big)
	}
}

我的结果:

BenchmarkLittle     500     3222064 ns/op
BenchmarkBig        50      32268023 ns/op
英文:

The problem is that you are not using b.N, which tells you how many times to run the benchmark. Also, if you only want to benchmark the ORing, you should probably just initialize the array once, or at least call b.ResetTimer() so the initialization is not counted.

Here's what I ended up with, which gives the expected results:

package main

import (
	&quot;math/rand&quot;
	&quot;testing&quot;
)

const (
	little = 5000000
	big    = 50000000
)

var a = make([]uint32, big)

func init() {
	for i := 0; i &lt; big; i++ {
		a[i] = rand.Uint32()
	}
}

func benchOR(b *testing.B, l int) {
	var result uint32
	for _, u := range a[:l] {
		result |= u
	}
}

func BenchmarkLittle(b *testing.B) {
	for i := 0; i &lt; b.N; i++ {
		benchOR(b, little)
	}
}

func BenchmarkBig(b *testing.B) {
	for i := 0; i &lt; b.N; i++ {
		benchOR(b, big)
	}
}

My results:

BenchmarkLittle	     500	   3222064 ns/op
BenchmarkBig	      50	  32268023 ns/op

答案2

得分: 2

我不认为这是一个 bug。我稍微修改了你的代码,得到了以下结果:

% go test -bench=.
testing: warning: no tests to run
PASS
BenchmarkLittle 2000000000 0.00 ns/op
BenchmarkBig 2000000000 0.02 ns/op
ok _/Users/kavu/TMP/becnh_or 12.659s

代码如下:

package main

import (
	"math/rand"
	"testing"
)

const (
	little = 5000000
	big    = 50000000
)

func benchOR(a []uint32, l int) (result uint32) {
	for i := 0; i < l; i++ {
		result |= a[i]
	}
	return result
}

func BenchmarkLittle(b *testing.B) {
	var a = make([]uint32, big)

	for i := 0; i < little; i++ {
		a[i] = rand.Uint32()
	}

	b.ResetTimer()
	benchOR(a, little)
}

func BenchmarkBig(b *testing.B) {
	var a = make([]uint32, big)

	for i := 0; i < big; i++ {
		a[i] = rand.Uint32()
	}

	b.ResetTimer()
	benchOR(a, big)
}

你可以注释掉 b.ResetTimer()benchOR(a, big) 这两行代码,看看会发生什么。你还可以尝试修改 big 常量的值。当 big 的值在某个范围内(比如 10000000)时,即使不重置计时器,速度也足够快。因此,使用 rand.Uint32 生成一个大的切片会拖慢整个过程。

英文:

I don't think it's a bug. I modified your code a bit, and that's what I got:

% go test -bench=.
testing: warning: no tests to run
PASS
BenchmarkLittle 2000000000           0.00 ns/op
BenchmarkBig  2000000000           0.02 ns/op
ok    _/Users/kavu/TMP/becnh_or 12.659s

Code:

	package main

	import (
		&quot;math/rand&quot;
		&quot;testing&quot;
	)

	const (
		little = 5000000
		big    = 50000000
	)

	func benchOR(a []uint32, l int) (result uint32) {
		for i := 0; i &lt; l; i++ {
			result |= a[i]
		}
		return result
	}

	func BenchmarkLittle(b *testing.B) {
		var a = make([]uint32, big)

		for i := 0; i &lt; little; i++ {
			a[i] = rand.Uint32()
		}

		b.ResetTimer()
		benchOR(a, little)
	}

	func BenchmarkBig(b *testing.B) {
		var a = make([]uint32, big)

		for i := 0; i &lt; big; i++ {
			a[i] = rand.Uint32()
		}

		b.ResetTimer()
		benchOR(a, big)
	}

You can comment out b.ResetTimer() and benchOR(a, big) things and see what happens. Also you can experiment with big constant. Somewhere 10000000 it's fast enough, even without reseting the timer. So, generating a big slice with rand.Uint32 slows everything.

huangapple
  • 本文由 发表于 2014年2月11日 02:02:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/21684632.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定