英文:
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 (
"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)
}
EDIT: must be a bug in go test -bench. Using a manual timing I don't reproduce
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))
}
答案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 (
"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)
}
}
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 (
"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)
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论