Performance 戈朗的类型铸造是如何工作的?

Performance 戈朗的类型铸造是如何工作的?,performance,go,types,casting,Performance,Go,Types,Casting,我的问题是加载和保存带有数字的二进制文件,这些数字可以很容易地存储在uint32/float32中。这将是大约超过2GB的磁盘和所有需要的内存太多 我的程序需要很多数学运算,golang标准库函数/方法需要int/float64参数,我必须将我的数字转换为int/float64 一个简单的benchmark()测试给出了以下输出: $: go test -bench=. goos: linux goarch: amd64 pkg: gotrade BenchmarkCast-4

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

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

一个简单的benchmark()测试给出了以下输出:

$: 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的优点:

  • 避免了大量类型转换操作
请给我建议一个处理这种情况的方法,我是不是遗漏了什么


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

至于转换,您的结果显示for循环运行了1000次。既然你循环了一百万次,那实际上就有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
}
因此,即使使用这种相对愚蠢且未经优化的代码,肇事者仍然是openFile基准

现在,让我们来透视一下这一点。19562ns等于0019562毫秒。人类平均可以感知大约20毫秒的潜伏期。因此,即使是100000(“十万”)个文件打开、行读取和文件关闭的速度也比人类所能感知的快1000倍

与此相比,施放速度要快几个数量级,所以只要你想施放,你的瓶颈就是I/O

编辑
这就留下了一个问题:为什么不首先将值作为float64导入?

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

至于转换,您的结果显示for循环运行了1000次。既然你循环了一百万次,那实际上就有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
}
因此,即使使用这种相对愚蠢且未经优化的代码,肇事者仍然是openFile基准

现在,让我们来透视一下这一点。19562ns等于0019562毫秒。人类平均可以感知大约20毫秒的潜伏期。因此,即使是100000(“十万”)个文件打开、行读取和文件关闭的速度也比人类所能感知的快1000倍

与此相比,施放速度要快几个数量级,所以只要你想施放,你的瓶颈就是I/O

编辑
这就留下了一个问题:为什么不首先将值作为float64导入?

不可能看到在没有源代码的情况下您可能想要更改的内容。例如,“casting”(实际上Go没有)应该在函数顶部发生一次,并且您正在将其应用于每个操作。但这只是猜测!我们不知道也不可能知道。如果你想要一个正确的答案,那么设计一个简单的案例来说明你所面临的问题。我已经在go Playerly中添加了我的基准代码,并共享了一个链接。从我的角度来看,你应该在开始基准测试之前先开始。对程序中速度较快的部分进行基准测试是没有意义的,但是要对瓶颈进行基准测试是有意义的。感谢您提出的评测建议,我将对此进行研究,在没有源代码的情况下,不可能看到您希望更改的内容。例如,“casting”(实际上Go没有)应该在函数顶部发生一次,并且您正在将其应用于每个操作。但这只是猜测!我们不知道也不可能知道。如果你想要一个正确的答案,那么设计一个简单的案例来说明你所面临的问题。我已经在go Playerly中添加了我的基准代码,并共享了一个链接。从我的角度来看,你应该在开始基准测试之前先开始。对程序中速度更快的部分进行基准测试是没有意义的,但是要考虑到瓶颈。感谢您的评测建议,我将研究一下itThanks,感谢您花了这么多时间来编写漂亮的解释。这就留下了一个问题:为什么不首先将值作为float64导入?正如我所说,我的值永远不会超过float32,使用float64是我内存的两倍,2GB到4GB。我的真正问题是100万次的选角不会发生,但我只是做了基准测试,看看选角是否真的需要时间。我是否可以正确地假设我应该使用float32并在需要时转换为float64,因为它不会产生太大影响,直到我饿了几毫秒为止?@mkmayank它将RAM从2 GB加倍到4 GB?那又怎么样?即使本地没有RAM,AWS上16GB的实例也是20美分/小时。就我的两分钱(双关语)。是的,现在是2018年,而不是90年代,内存比CPU时间还便宜,谢谢你,谢谢你花了这么多时间来写这个漂亮的解释。这就留下了一个问题:为什么不首先将值作为float64导入?正如我所说,我的值永远不会超过float32,使用float64是我内存的两倍,2GB到4GB。我的真正问题是100万次的选角不会发生,但我只是做了基准测试,看看选角是否真的需要时间。我是否可以正确地假设我应该使用float32并在需要时转换为float64,因为它不会产生太大影响,直到我饿了几毫秒为止?@mkmayank它将RAM从2 GB加倍到4 GB?那又怎么样?即使本地没有RAM,AWS上16GB的实例也是20美分/小时。就我的两分钱(双关语)。是的,现在是2018年,不是90年代,内存比CPU时间便宜,无论如何谢谢
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