美文网首页程序员
go语言中值拷贝的成本

go语言中值拷贝的成本

作者: golang推广大使 | 来源:发表于2019-03-18 00:24 被阅读0次

在go语言中,值拷贝是常有的事情。赋值,传参和发送值给channel都有值拷贝。本文将讨论各种类型的值拷贝成本。

值的大小

大小是指值的直接部分在内存中占用的字节数。值的非直接部分不会影响值的大小。

在go语言中,如果两个值类型相同并且他们的类型不属于string,interface,数组,struct,那么他们的大小相等。
事实上,对应标准的go编译器和运行时,两个字符串类型的值大小是一样的。两个interface类型的值也是如此。
到目前为止,特定类型的值大小总是一致的。因此,通常,我们将值的大小称为值的类型的大小。
数组类型的值大小由数组中的元素类型的大小和数组的长度决定。数组类型的大小是数组长度乘以数组元素的大小。
struct的大小取决于它所有字段的的大小和顺序。因为在两个相邻的struct字段之间可能会插入一些填充字节,以保证这些字段的某些内存地址对齐要求,因此,struct的大小必须大于或者等于(并且通常大于)其字段的相应类型大小的总和。
下表列出了各种类型的值大小(对于标准Go编译器版本1.12)。在表中,一个词表示一个本地字,在32位架构上为4个字节,在64位架构上为8个字节。

Kind Of Types Value Size Required By Go Specification
bool 1 byte not specified
int8, uint8 (byte) 1 byte 1 byte
int16, uint16 2 bytes 2 bytes
int32 (rune), uint32, float32 4 bytes 4 bytes
int64, uint64, float64, complex64 8 bytes 8 bytes
complex128 16 bytes 16 bytes
int, uint 1 word architecture dependent, 4 bytes on 32bits architectures and 8 bytes on 64bits architectures
uintptr 1 word large enough to store the uninterpreted bits of a pointer value
string 2 words not specified
pointer 1 word not specified
slice 3 words not specified
map 1 word not specified
channel 1 word not specified
function 1 word not specified
interface 2 words not specified
struct the sum of sizes of all fields + number of padding bytes a struct type has size zero if it contains no fields that have a size greater than zero
array (element value size) * (array length) an array type has size zero if its element type has zero size

值拷贝的成本

一般而言,复制值的成本与值的大小成比例。但是,值大小并不是决定值复制成本的唯一因素。不同的CPU架构可以专门针对具有特定大小的值优化值复制。
在实践中,我们可以将尺寸小于不大于四个原始单词的值视为小尺寸值。复制小尺寸值的成本很小。
对于标准的Go编译器,除了大型结构和数组类型的值之外,Go中的其他类型都是小型类型。
为了避免在传参和channel值的接受和发送中的巨大的值拷贝操作,我们应该尝试避免大尺寸的struct和数组类型作为函数和方法的参数以及channel的元素,我们可以使用对应类型的指针类型作为参数。
另一方面,我们还应该考虑这样一个事实:太多的指针会增加垃圾收集器在运行时的压力。因此,是否应该使用大型结构和数组类型或它们相应的指针类型取决于具体情况。
通常,在实践中,我们很少使用基类型是slice,channel,map,function类型,string类型和interface类型的指针类型。复制这些基类型的值的成本非常小。
如果元素类型是大型类型,我们还应该尽量避免使用两次迭代变量形式来迭代数组和切片元素,因为每个元素值将被复制到迭代过程中的第二个迭代变量。
以下是对切片元素迭代的不同方式进行基准测试的示例。

package main

import "testing"

type S struct{a, b, c, d, e int64}
var sX, sY, sZ = make([]S, 1000), make([]S, 1000), make([]S, 1000)
var sumX, sumY, sumZ int64

func Benchmark_Loop(b *testing.B) {
    for i := 0; i < b.N; i++ {
        sumX = 0
        for j := 0; j < len(sX); j++ {
            sumX += sX[j].a
        }
    }
}

func Benchmark_Range_OneIterVar(b *testing.B) {
    for i := 0; i < b.N; i++ {
        sumZ = 0
        for j := range sY {
            sumZ += sY[j].a
        }
    }
}

func Benchmark_Range_TwoIterVar(b *testing.B) {
    for i := 0; i < b.N; i++ {
        sumY = 0
        for _, v := range sY {
            sumY += v.a
        }
    }
}

在测试文件的目录中运行基准测试,我们将得到类似于的结果:

Benchmark_Loop-4                500000   3228 ns/op
Benchmark_Range_OneIterVar-4    500000   3203 ns/op
Benchmark_Range_TwoIterVars-4   200000   6616 ns/op

我们可以发现,两次迭代变量形式的效率远低于其他两种形式。

相关文章

  • go语言中值拷贝的成本

    在go语言中,值拷贝是常有的事情。赋值,传参和发送值给channel都有值拷贝。本文将讨论各种类型的值拷贝成本。 ...

  • 面试题(一)

    1. Go 语言参数传递是值传递还是引用传递 Go 语言中所有的传参都是值传递,都是一个副本,一个拷贝。因为拷贝的...

  • WSL ubuntu中更新go语言

    1、删除原有go语言 2、下载最新linux版go语言https://golang.org/dl/3、拷贝到本地执...

  • Go语言中拷贝文件的几种常用的方式及性能对比

    Go语言中拷贝文件的几种常用的方式 简介 本篇文章将介绍Go语言中,最最最常用的3种拷贝文件的方法,这三种方法各有...

  • go和python的深浅拷贝理解

    go深拷贝, 就是拷贝值 go浅拷贝, 拷贝引用 go中赋值就能实现拷贝,针对引用类型(slice,map,cha...

  • 《golang高级编程》-读书笔记

    《Go语言高级编程》 作者 柴树杉 曹春晖 1.3 数组、字符串和切片 Go语言拷贝字符串,只是复制了底层字节数组...

  • golang使用技巧

    golang map赋值是引用拷贝 返回值 可以放入一行中 go tools 里面支持vscode跳转 go语言c...

  • 64行代码实现零拷贝go的TCP拆包粘包

    64行代码实现零拷贝go的TCP拆包粘包 前言 这段时间想用go写一个简单IM系统,就思考了一下go语言TCP的拆...

  • Go语言讲解深拷贝与浅拷贝

    我们在开发中会经常的把一个变量复制给另一个变量,那么这个过程,可能是深浅拷贝,那么今天帮大家区分一下这两个拷贝的区...

  • 潍坊go语言培训

    潍坊go语言培训潍坊go语言培训潍坊go语言培训潍坊go语言培训潍坊go语言培训潍坊go语言培训潍坊go语言培训潍...

网友评论

    本文标题:go语言中值拷贝的成本

    本文链接:https://www.haomeiwen.com/subject/kxhmmqtx.html