我尝试使用Go语言并且对它很陌生。我已经成功完成了这些教程,现在正在编写一个小程序来评估我通常所做的操作类型的性能。我有一个很长的float32类型的片段,需要尽可能高效地将它转换为float64类型的片。除了遍历切片的元素并通过output [i] = float64(data [i])显式转换单个元素的类型之外,是否有方法可以用来转换整个切片而无需迭代?我尝试过搜索解决方案,但没有发现任何直接相关的内容。
答案 0 :(得分:2)
Go非常低级,这意味着迭代切片是最有效的方法。其他语言可能有这样的内置函数,但他们所做的只是遍历切片,没有迭代就没有办法做到这一点。但是有一些技巧,特别是使用范围并避免索引切片,因为在越界检查中存在开销。这将是最有效的:
func convertTo64(ar []float32) []float64 {
newar := make([]float64, len(ar))
var v float32
var i int
for i, v = range ar {
newar[i] = float64(v)
}
return newar
}
slice32 := make([]float32, 1000)
slice64 := convertTo64(slice32)
请注意,在范围循环中使用:=
将是低效的,因为在当前版本的Go中,变量被抛弃并且每次都重新创建而不是被重用。使用range
代替for i=0; i<n; i++
会更有效,因为它会在ar
上保存边界检查。
答案 1 :(得分:2)
对性能声明持怀疑态度总是明智的。例如,&#34;在范围循环中使用:=将是低效的,因为......变量被抛弃并且每次都重新创建而不是被重用。&#34;
让我们看看连续三次运行的基准测试结果。
floats_test.go
:
package main
import "testing"
var slice64 []float64
func FuncVar(f32 []float32) []float64 {
f64 := make([]float64, len(f32))
var f float32
var i int
for i, f = range f32 {
f64[i] = float64(f)
}
return f64
}
func BenchmarkFuncVar(b *testing.B) {
f32 := make([]float32, 1024)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
slice64 = FuncVar(f32)
}
}
func RangeVar(f32 []float32) []float64 {
f64 := make([]float64, len(f32))
for i, f := range f32 {
f64[i] = float64(f)
}
return f64
}
func BenchmarkRangeVar(b *testing.B) {
f32 := make([]float32, 1024)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
slice64 = RangeVar(f32)
}
}
func main() {}
输出:
$ go test -v -run=! -bench=. testing: warning: no tests to run PASS BenchmarkFuncVar 100000 12260 ns/op 8192 B/op 1 allocs/op BenchmarkRangeVar 100000 12125 ns/op 8192 B/op 1 allocs/op ok so/test 2.703s $ go test -v -run=! -bench=. testing: warning: no tests to run PASS BenchmarkFuncVar 100000 12620 ns/op 8192 B/op 1 allocs/op BenchmarkRangeVar 100000 12623 ns/op 8192 B/op 1 allocs/op ok so/test 2.782s $ go test -v -run=! -bench=. testing: warning: no tests to run PASS BenchmarkFuncVar 100000 12730 ns/op 8192 B/op 1 allocs/op BenchmarkRangeVar 100000 12971 ns/op 8192 B/op 1 allocs/op ok so/test 2.852s $
根据已删除的评论的要求。这是一个仅使用系统时间的基准。
package main
import (
"fmt"
"time"
)
const N = 1e6
var f32 = make([]float32, 1024)
var slice64 []float64
func FuncVar(f32 []float32) []float64 {
f64 := make([]float64, len(f32))
var f float32
var i int
for i, f = range f32 {
f64[i] = float64(f)
}
return f64
}
func BenchmarkFuncVar() {
t1 := time.Now()
for i := 0; i < N; i++ {
slice64 = FuncVar(f32)
}
t2 := time.Now()
fmt.Println("FuncVar", t2.Sub(t1))
}
func RangeVar(f32 []float32) []float64 {
f64 := make([]float64, len(f32))
for i, f := range f32 {
f64[i] = float64(f)
}
return f64
}
func BenchmarkRangeVar() {
t1 := time.Now()
for i := 0; i < N; i++ {
slice64 = RangeVar(f32)
}
t2 := time.Now()
fmt.Println("RangeVar", t2.Sub(t1))
}
func main() {
BenchmarkFuncVar()
BenchmarkRangeVar()
}
输出:
$ go build floata.go && ./floata FuncVar 10.479653966s RangeVar 10.208178244s $ go build floata.go && ./floata FuncVar 10.123357283s RangeVar 10.173007394s $ go build floata.go && ./floata FuncVar 9.935580721s RangeVar 10.109644784s $ go build floata.go && ./floata FuncVar 10.070552761s RangeVar 10.317730473s $ go build floata.go && ./floata FuncVar 10.075578601s RangeVar 10.012273678s $