2014-11-16 128 views
4

我正在嘗試使用Go語言,並且對它很陌生。我已經成功地閱讀了教程,現在正在編寫一個小程序來評估我通常所做的操作類型的性能。我有一段很長的float32類型,需要儘可能高效地將其轉換爲float64類型的片段。除了迭代切片的元素並通過輸出[i] = float64(data [i])顯式地轉換單個元素的類型外,是否有方法可以用來轉換整個切片而無需迭代?我試過尋找一個解決方案,但沒有找到任何直接相關的東西。如何將一種數字類型的切片轉換爲另一種類型

+3

不......即使這是一個內置的,它很難做的比迭代中,你的4行代碼會做同樣的方式的要素的任何其他。 –

回答

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) 

注意的是,在範圍內循環使用:=會因爲圍棋的當前版本的變量被扔掉,每一次的,而不是被重用重建效率低下。使用range而不是for i=0; i<n; i++更有效,因爲它可以節省ar上的邊界檢查。

+0

這真的很有幫助。感謝您分享您的專業知識。 – user1503949

+0

沒問題,歡迎來到Go的精彩世界! – Alasdair

+0

我在過去幾天一直在玩它,但它確實看起來很好,很容易拾起。 – user1503949

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 
$ 
+1

Hi peterSo,我在幾周前做過的基準測試中發現了:=範圍問題,並在Golang-Devs上報告了它。現在重做這個基準,我可以看出它們在兩者之間沒有區別。早期的基準很可能是有缺陷的。感謝您採取雙重檢查。 – Alasdair

相關問題