gpt4 book ai didi

performance - golang 中的类型转换是如何工作的?

转载 作者:IT王子 更新时间:2023-10-29 02:31:07 24 4
gpt4 key购买 nike

我的问题陈述是加载和保存带有数字的二进制文件,这些数字可以很容易地存储在 uint32/float32 中。这将在磁盘上超过 2GB,并且都需要在内存中。

我的程序需要大量的数学运算,golang 标准库函数/方法需要 int/float64 参数,我必须将我的数字转换为 int/float64

一个简单的基准 (https://play.golang.org/p/A52-wBo3Z34) 测试给出了以下输出:

$: go test -bench=. 
goos: linux
goarch: amd64
pkg: gotrade
BenchmarkCast-4 1000 1519964 ns/op
BenchmarkNoCast-4 3000 373340 ns/op
PASS
ok gotrade 2.843s

这清楚地表明类型转换是相当昂贵的。

int 和 float64 的缺点:

  • 它几乎需要两倍的内存

int 和 float64 的优点:

  • 避免大量类型转换操作

请建议我处理这种情况的方法,我在这里遗漏了什么吗?

如果我们需要通过标准库进行外部计算,我们是否应该始终选择 intfloat64

最佳答案

您的逻辑、基准和假设有几个错误。

至于转换,您的结果显示您的 for 循环运行了 1000 次。因为你循环了 100 万次,这实际上进行了 10 亿次转换操作……还不算太差。

实际上,我已经稍微修改了你的代码:

const (
min = float64(math.SmallestNonzeroFloat32)
max = float64(math.MaxFloat32)
)

func cast(in float64) (out float32, err error) {

// We need to guard here, as casting from float64 to float32 looses precision
// Therefor, we might get out of scope.
if in < min {
return 0.00, fmt.Errorf("%f is smaller than smallest float32 (%f)", in, min)
} else if in > max {
return 0.00, fmt.Errorf("%f is bigger than biggest float32 (%f)", in, max)
}

return float32(in), nil
}

// multi64 uses a variadic in parameter, in order to be able
// to use the multiplication with arbitrary length.
func multi64(in ...float64) (result float32, err error) {

// Necessary to set it to 1.00, since float64's null value is 0.00...
im := float64(1.00)

for _, v := range in {
im = im * v
}

// We only need to cast once.
// You DO want to make the calculation with the original precision and only
// want to do the casting ONCE. However, this should not be done here - but in the
// caller, as the caller knows on how to deal with special cases.
return cast(im)
}

// multi32 is a rather non-sensical wrapper, since the for loop
// could easily be done in the caller.
// It is only here for comparison purposes.
func multi32(in ...float32) (result float32) {
result = 1.00
for _, v := range in {
result = result * v
}
return result
}

// openFile is here for comparison to show that you can do
// a... fantastic metric ton of castings in comparison to IO ops.
func openFile() error {
f, err := os.Open("cast.go")
if err != nil {
return fmt.Errorf("Error opening file")
}
defer f.Close()

br := bufio.NewReader(f)
if _, _, err := br.ReadLine(); err != nil {
return fmt.Errorf("Error reading line: %s", err)
}

return nil
}

使用以下测试代码

func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
func BenchmarkCast(b *testing.B) {
b.StopTimer()

v := rand.Float64()
var err error

b.ResetTimer()
b.StartTimer()

for i := 0; i < b.N; i++ {
if _, err = cast(v); err != nil {
b.Fail()
}
}
}

func BenchmarkMulti32(b *testing.B) {
b.StopTimer()

vals := make([]float32, 10)

for i := 0; i < 10; i++ {
vals[i] = rand.Float32() * float32(i+1)
}

b.ResetTimer()
b.StartTimer()

for i := 0; i < b.N; i++ {
multi32(vals...)
}
}
func BenchmarkMulti64(b *testing.B) {

b.StopTimer()

vals := make([]float64, 10)
for i := 0; i < 10; i++ {
vals[i] = rand.Float64() * float64(i+1)
}

var err error
b.ResetTimer()
b.StartTimer()

for i := 0; i < b.N; i++ {
if _, err = multi64(vals...); err != nil {
b.Log(err)
b.Fail()
}
}
}

func BenchmarkOpenFile(b *testing.B) {
var err error
for i := 0; i < b.N; i++ {
if err = openFile(); err != nil {
b.Log(err)
b.Fail()
}
}
}

你得到这样的东西

BenchmarkCast-4         1000000000           2.42 ns/op
BenchmarkMulti32-4 300000000 5.04 ns/op
BenchmarkMulti64-4 200000000 8.19 ns/op
BenchmarkOpenFile-4 100000 19591 ns/op

因此,即使使用这种相对愚蠢且未优化的代码,肇事者还是 openFile 基准测试。

现在,让我们来正确看待这个问题。 19,562ns 等于 0,019562 毫秒。普通人可以感知大约 20 毫秒的延迟。因此,即使是那 100,000(“十万”)次文件打开、行读取和文件关闭也比人类感知快约 1000 倍。

与此相比,转换要快几个数量级 - 所以随心所欲地转换,瓶颈将是 I/O。

编辑

这留下了一个问题,为什么您不首先将值导入为 float64?

关于performance - golang 中的类型转换是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53913098/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com