如何将一个数值类型的切片转换为另一种类型

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

How to convert a slice of one numeric type to another type

问题

我正在尝试使用Go语言,并且对它还很陌生。我已经成功完成了教程,并且现在正在编写一个小程序来评估它在我通常进行的操作类型上的性能。我有一个很长的float32类型的切片,需要将其转换为尽可能高效的float64类型的切片。除了通过迭代切片的元素并显式地转换每个元素的类型(例如output[i] = float64(data[i])),是否有其他方法可以在不需要迭代的情况下转换整个切片?我尝试搜索解决方案,但没有找到直接相关的内容。

英文:

I'm experimenting with the Go language and am quite new to it. I have successfully gone through the tutorials and am now writing a little program to evaluate its performance for the type of operations that I typically do. I have a lengthy slice of float32 type and need to convert it to a slice of type float64 as efficiently as possible. Other than iterating through the elements of the slice and explicitly converting types of individual elements via output[i] = float64(data[i]), is there method that I can use to convert the entire slice without need for iteration? I've tried searching for a solution but have not found anything directly related.

答案1

得分: 5

Go是一种相当底层的语言,这意味着通过切片进行迭代是最高效的方法。其他语言可能有内置函数来处理这种情况,但它们只是通过切片进行迭代,没有办法避免迭代。但是有一些技巧,特别是使用range并避免对切片进行索引,因为在越界检查中会有开销。这将是最高效的方法:

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)

请注意,在range循环中使用:=会效率低下,因为在当前版本的Go中,变量每次都会被丢弃并重新创建,而不是被重用。使用range而不是for i=0; i<n; i++更高效,因为它可以节省对ar进行边界检查的开销。

英文:

Go is quite low-level, this means that iterating through the slice is the most efficient method. Other languages may have built-in functions for such things, but all they do is iterate through the slice, there is no way to do it without the iteration. But there are some tricks, specifically use range and avoid indexing the slice as there is overhead in the out of bounds check. This would be the most efficient:

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)

Note that the use of := in the range loop would be inefficient because in the current version of Go the variable is thrown away and recreated each time instead of being reused. Using range instead of for i=0; i&lt;n; i++ is more efficient because it saves bounds checks on ar.

答案2

得分: 2

对于性能的声明,始终保持怀疑是明智的。例如,“在范围循环中使用:=可能效率低下,因为...变量每次都被丢弃和重新创建,而不是被重用。”

让我们看一下连续三次运行的基准测试结果。

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
$
英文:

It's always wise to be skeptical of performance claims. For example, "the use of := in the range loop would be inefficient because ... the variable is thrown away and recreated each time instead of being reused."

Let's look at benchmark results from three successive runs.

floats_test.go:

package main

import &quot;testing&quot;

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 &lt; 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 &lt; b.N; i++ {
		slice64 = RangeVar(f32)
	}
}

func main() {}

Output:

<pre>
$ 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
$
</pre>


As requested by a comment that has been deleted. Here's a benchmark using just the system time.

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

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 &lt; N; i++ {
		slice64 = FuncVar(f32)
	}
	t2 := time.Now()
	fmt.Println(&quot;FuncVar&quot;, 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 &lt; N; i++ {
		slice64 = RangeVar(f32)
	}
	t2 := time.Now()
	fmt.Println(&quot;RangeVar&quot;, t2.Sub(t1))
}

func main() {
	BenchmarkFuncVar()
	BenchmarkRangeVar()
}

Output:

<pre>
$ 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
$
</pre>

huangapple
  • 本文由 发表于 2014年11月16日 21:42:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/26957619.html
匿名

发表评论

匿名网友

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

确定