# goalng深刻剖析——slice

news2024/11/22 12:26:36

goalng深刻剖析——slice

  切片是 Go 中的一种基本的数据结构,使用这种结构可以用来管理数据集合。切片的设计想法来源于动态数组,为了开发者可以更加方便的使一个数据结构可以自动增加和减少。但是切片本身并不是动态数据或者数组指针。切片常见的操作有 reslice、append、copy。与此同时,切片还具有可索引,可迭代的优秀特性。

1 数据结构

  根据官网源码:slice定义在src/runtime/slice.go:第15行。具体定义如下:

type slice struct {
	array unsafe.Pointer    //指向数组的指针,切片的数据实际存储在数组中,指针类型为:unsafe.Pointer;
	len   int               //切片长度,通过len()获取的就是该属性;
	cap   int               //切片容量,通过cap()获取的就是该属性,表示切片当前最多可以存储多少个元素;
}

2 slice定义与初始化

2.1 定义slice

2.1.1 方式1:语法糖

	var identifier []type  //长度和容量均为0的空切片

2.1.2 方式2:make( )

    //指定长度,默认容量与长度相同
	var slice1 []type = make([]type, len)
    slice2 := make([]type, len)   //简写 
    
    //也可以指定容量,其中 capacity 为可选参数。
    var slice3 []type=make([]T, len, capacity)
    slice2 := make([]type, len, capacity)   //简写 

2.2 初始化slice

2.2.1 直接初始化

s :=[]int {1,2,3 }  //直接初始化切片,[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3。

2.2.2 分隔符(:)方式

	arr := [10]int{1, 2, 3, 4, 5, 6, 7}
    s1 := arr[:]                    //初始化切片 s,是数组 arr 的引用。
    s2 := arr[startIndex:endIndex]  //将 arr 中从下标 startIndex 到 endIndex-1 的元素创建为一个新的切片。
    s3 := arr[startIndex:]          //默认 endIndex 时将表示一直到 arr 的最后一个元素。
    s4 := arr[:endIndex]            //默认 startIndex 时将表示从 arr 的第一个元素开始。

4. 扩容策略

  根据官网源码:slice扩容的源码实现在src/runtime/slice.go157行开始,具体实现接口growslice( )如下:

func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice {
	oldLen := newLen - num
	if raceenabled {
		callerpc := getcallerpc()
		racereadrangepc(oldPtr, uintptr(oldLen*int(et.Size_)), callerpc, abi.FuncPCABIInternal(growslice))
	}
	if msanenabled {
		msanread(oldPtr, uintptr(oldLen*int(et.Size_)))
	}
	if asanenabled {
		asanread(oldPtr, uintptr(oldLen*int(et.Size_)))
	}

	if newLen < 0 {
		panic(errorString("growslice: len out of range"))
	}

	if et.Size_ == 0 {
		// append should not create a slice with nil pointer but non-zero len.
		// We assume that append doesn't need to preserve oldPtr in this case.
		return slice{unsafe.Pointer(&zerobase), newLen, newLen}
	}

	newcap := oldCap
	doublecap := newcap + newcap
	if newLen > doublecap {
		newcap = newLen
	} else {
		const threshold = 256
		if oldCap < threshold {
			newcap = doublecap
		} else {
			// Check 0 < newcap to detect overflow
			// and prevent an infinite loop.
			for 0 < newcap && newcap < newLen {
				// Transition from growing 2x for small slices
				// to growing 1.25x for large slices. This formula
				// gives a smooth-ish transition between the two.
				newcap += (newcap + 3*threshold) / 4
			}
			// Set newcap to the requested cap when
			// the newcap calculation overflowed.
			if newcap <= 0 {
				newcap = newLen
			}
		}
	}

	var overflow bool
	var lenmem, newlenmem, capmem uintptr
	// Specialize for common values of et.Size.
	// For 1 we don't need any division/multiplication.
	// For goarch.PtrSize, compiler will optimize division/multiplication into a shift by a constant.
	// For powers of 2, use a variable shift.
	switch {
	case et.Size_ == 1:
		lenmem = uintptr(oldLen)
		newlenmem = uintptr(newLen)
		capmem = roundupsize(uintptr(newcap))
		overflow = uintptr(newcap) > maxAlloc
		newcap = int(capmem)
	case et.Size_ == goarch.PtrSize:
		lenmem = uintptr(oldLen) * goarch.PtrSize
		newlenmem = uintptr(newLen) * goarch.PtrSize
		capmem = roundupsize(uintptr(newcap) * goarch.PtrSize)
		overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize
		newcap = int(capmem / goarch.PtrSize)
	case isPowerOfTwo(et.Size_):
		var shift uintptr
		if goarch.PtrSize == 8 {
			// Mask shift for better code generation.
			shift = uintptr(sys.TrailingZeros64(uint64(et.Size_))) & 63
		} else {
			shift = uintptr(sys.TrailingZeros32(uint32(et.Size_))) & 31
		}
		lenmem = uintptr(oldLen) << shift
		newlenmem = uintptr(newLen) << shift
		capmem = roundupsize(uintptr(newcap) << shift)
		overflow = uintptr(newcap) > (maxAlloc >> shift)
		newcap = int(capmem >> shift)
		capmem = uintptr(newcap) << shift
	default:
		lenmem = uintptr(oldLen) * et.Size_
		newlenmem = uintptr(newLen) * et.Size_
		capmem, overflow = math.MulUintptr(et.Size_, uintptr(newcap))
		capmem = roundupsize(capmem)
		newcap = int(capmem / et.Size_)
		capmem = uintptr(newcap) * et.Size_
	}

	// The check of overflow in addition to capmem > maxAlloc is needed
	// to prevent an overflow which can be used to trigger a segfault
	// on 32bit architectures with this example program:
	//
	// type T [1<<27 + 1]int64
	//
	// var d T
	// var s []T
	//
	// func main() {
	//   s = append(s, d, d, d, d)
	//   print(len(s), "\n")
	// }
	if overflow || capmem > maxAlloc {
		panic(errorString("growslice: len out of range"))
	}

	var p unsafe.Pointer
	if et.PtrBytes == 0 {
		p = mallocgc(capmem, nil, false)
		// The append() that calls growslice is going to overwrite from oldLen to newLen.
		// Only clear the part that will not be overwritten.
		// The reflect_growslice() that calls growslice will manually clear
		// the region not cleared here.
		memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
	} else {
		// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
		p = mallocgc(capmem, et, true)
		if lenmem > 0 && writeBarrier.enabled {
			// Only shade the pointers in oldPtr since we know the destination slice p
			// only contains nil pointers because it has been cleared during alloc.
			bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(oldPtr), lenmem-et.Size_+et.PtrBytes)
		}
	}
	memmove(p, oldPtr, lenmem)

	return slice{p, newLen, newcap}
}

【代码分析】

  鉴于源码过于冗长,删除了与扩容无关代码,只保留扩容策略并添加中文注释:

func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice {
	oldLen := newLen - num
    //...
	newcap := oldCap                                //初始化:newcap = oldCap 
	doublecap := newcap + newcap                 
	if newLen > doublecap {                         //如果:新切片长度超过旧容量的2倍
		newcap = newLen                             //     将新容量更新为新长度;
	} else {                                        //否则:
		const threshold = 256                       //     定义阈值:threshold = 256 
		if oldCap < threshold {                     //     如果:旧容量小于阈值256
			newcap = doublecap                      //           扩容为原来2倍
		} else {                                    //     否则:通过迭代直到 newcap >= newLen,迭代公式如下:
			// Check 0 < newcap to detect overflow               newcap =newcap + (newcap + 3*threshold) / 4
			// and prevent an infinite loop.
			for 0 < newcap && newcap < newLen {     
				// Transition from growing 2x for small slices
				// to growing 1.25x for large slices. This formula
				// gives a smooth-ish transition between the two.
				newcap += (newcap + 3*threshold) / 4
			}
			// Set newcap to the requested cap when
			// the newcap calculation overflowed.
			if newcap <= 0 {
				newcap = newLen                   //特殊情况:如果原容量为0,将新容量更新为新切片长度
			}
		}
	}

    // 最后再对newcap做内存对齐,最终得到新容量
	var overflow bool
	var lenmem, newlenmem, capmem uintptr
	// Specialize for common values of et.Size.
	// For 1 we don't need any division/multiplication.
	// For goarch.PtrSize, compiler will optimize division/multiplication into a shift by a constant.
	// For powers of 2, use a variable shift.
	switch {
	case et.Size_ == 1:
		lenmem = uintptr(oldLen)
		newlenmem = uintptr(newLen)
		capmem = roundupsize(uintptr(newcap))
		overflow = uintptr(newcap) > maxAlloc
		newcap = int(capmem)
	case et.Size_ == goarch.PtrSize:
		lenmem = uintptr(oldLen) * goarch.PtrSize
		newlenmem = uintptr(newLen) * goarch.PtrSize
		capmem = roundupsize(uintptr(newcap) * goarch.PtrSize)
		overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize
		newcap = int(capmem / goarch.PtrSize)
	case isPowerOfTwo(et.Size_):
		var shift uintptr
		if goarch.PtrSize == 8 {
			// Mask shift for better code generation.
			shift = uintptr(sys.TrailingZeros64(uint64(et.Size_))) & 63
		} else {
			shift = uintptr(sys.TrailingZeros32(uint32(et.Size_))) & 31
		}
		lenmem = uintptr(oldLen) << shift
		newlenmem = uintptr(newLen) << shift
		capmem = roundupsize(uintptr(newcap) << shift)
		overflow = uintptr(newcap) > (maxAlloc >> shift)
		newcap = int(capmem >> shift)
		capmem = uintptr(newcap) << shift
	default:
		lenmem = uintptr(oldLen) * et.Size_
		newlenmem = uintptr(newLen) * et.Size_
		capmem, overflow = math.MulUintptr(et.Size_, uintptr(newcap))
		capmem = roundupsize(capmem)
		newcap = int(capmem / et.Size_)
		capmem = uintptr(newcap) * et.Size_
	}
	
    //...
    
	return slice{p, newLen, newcap}  //返回
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/832263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

黑马头条 各种踩坑合集 从0到1 欢迎留言提问

《黑马头条》SpringBootSpringCloud Nacos等企业级微服务架构项目_黑马头条项目_软工菜鸡的博客-CSDN博客 Day01 用他的 centos镜像 自动集成了 nacos 所有的配置 大部分都要改mysql密码啥的&#xff1b; 启动他的项目&#xff0c;有些时候要启动 redis 容器&#xff1b;镜像里…

超详细|ChatGPT论文润色教程

本文讲述使用中科大开源ChatGPT论文辅助工具&#xff0c;对论文进行润色 祝看到本教程的小伙伴们都完成论文&#xff0c;顺利毕业。 可以加QQ群交流&#xff0c;一群&#xff1a; 123589938 第一章 介绍 今天给大家分享一款非常不错的ChatGPT论文辅助工具&#xff0c;使用了专…

谁是全球最小ARM MCU?

先是从百度上查找的&#xff0c;找了半天&#xff0c;也找到了一部分。 大小基本在一二毫米左右&#xff0c;外设有多有少&#xff0c;但是好多都买不到货。 而且有些特别小众&#xff0c;开发环境压根没接触过。 然后去外面找了一下&#xff0c;竟然有好几个提到了HC32L110&am…

Ubuntu20.04安装踩坑记录---千万不要随便使用sudo rm -rf命令!!!

趁着从服务器下载之前备份内容的这段时间&#xff0c;写一下这阶段安装系统和配置深度学习环境的踩坑路程。作为一周之内重装7次系统的我是懂得怎么把系统搞崩溃的&#xff0c;这不必须记录一下&#xff01;&#xff01;&#xff01; 系统 ubuntu20.04 硬件 dell…

(树) 剑指 Offer 33. 二叉搜索树的后序遍历序列 ——【Leetcode每日一题】

❓剑指 Offer 33. 二叉搜索树的后序遍历序列 难度&#xff1a;中等 输入一个整数数组&#xff0c;判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true&#xff0c;否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树&#xff1…

调用feign返回错误的数据

bug描述&#xff1a; 在一个请求方法中会调用到feign去获取其他的数据。 List<Demo> list aaaFeignApi.getData(personSelectGetParam);在调用的时候&#xff0c;打断点到feign的地方&#xff0c;数据是存在的&#xff0c;并且有15条。但是返回到上面代码的时候数据就…

0139 数据链路层1

目录 3.数据链路层 3.1数据链路层的功能 3.2组帧 3.3差错控制 3.4流量控制与可靠传输机制 3.5介质访问控制 部分习题 3.数据链路层 3.1数据链路层的功能 3.2组帧 3.3差错控制 3.4流量控制与可靠传输机制 3.5介质访问控制 部分习题 1.数据链路层协议的功能不包括&…

webpack基础知识二:说说webpack的构建流程?

一、运行流程 webpack 的运行流程是一个串行的过程&#xff0c;它的工作流程就是将各个插件串联起来 在运行过程中会广播事件&#xff0c;插件只需要监听它所关心的事件&#xff0c;就能加入到这条webpack机制中&#xff0c;去改变webpack的运作&#xff0c;使得整个系统扩展…

[C++项目] Boost文档 站内搜索引擎(3): 建立文档及其关键字的正排 倒排索引、jieba库的安装与使用...

之前的两篇文章: 第一篇文章介绍了本项目的背景, 获取了Boost库文档 &#x1fae6;[C项目] Boost文档 站内搜索引擎(1): 项目背景介绍、相关技术栈、相关概念介绍…第二篇文章 分析实现了parser模块. 此模块的作用是 对所有文档html文件, 进行清理并汇总 &#x1fae6;[C项目] …

【JS】浏览器不同窗口、标签页或 iframe之间的通讯 - 技术的尽头是魔术

效果 左上↖地址: http://127.0.0.1:5500/index.html 左下↙地址: http://127.0.0.1:5500/index.html?hidden 右上↗地址: http://127.0.0.1:5500/index.html?hidden 右下↘地址: http://127.0.0.1:5500/index.html?hidden index.html <!DOCTYPE html> <html>…

【antd之tabs踩坑篇】Tabs有items时切换不起作用

<TabsdefaultActiveKey"1"tabPosition{mode}style{{ height: 220 }}items{new Array(30).fill(null).map((_, i) > {const id String(i);return {label: Tab-${id},key: id,disabled: i 28,children: Content of tab ${id},};})}/>官网上如果tabs有很多it…

jmeter使用:解决压测时获取token问题

在执行压测过程中&#xff0c;首先要执行登录接口来获取token。如果并发数比较大只需要一个用户的登录token&#xff0c;可以使用setup线程组。如果是模拟多个用户登录获取token&#xff0c;需要使用仅一次控制器。 一、添加setup thread group前置线程 1.在并发量比较高的情况…

代码随想录算法训练营day53

文章目录 Day53 最长公共子序列题目思路代码 不相交的线题目思路代码 最大子序和题目思路代码 Day53 最长公共子序列 1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09; 题目 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长公共子序列的长度…

最新AWVS 支持Windows/Linux

最新AWVS15.7.230603143 支持Windows/Linux 现只需要运行bat、sh脚本就可以一键破解。 修改hosts文件&#xff08;C:\Windows\System32\drivers\etc\hosts&#xff09; 127.0.0.1 updates.acunetix.com127.0.0.1 erp.acunetix.com127.0.0.1 bxss.me127.0.0.1 te…

【Linux命令200例】whereis用于搜索以及定位二进制文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜…

网站是如何进行访问的?在浏览器地址栏输入网址并回车的一瞬间到页面能够展示回来,经历了什么?

这个问题是检验web和计网学习程度的经典问题。 网站访问流程&#xff1a; 1.域名->ip地址 1) 在输入完一个域名之后&#xff0c;首先是检查浏览器自身的DNS缓存是否有相应IP地址映射&#xff0c;如果没有对应的解析记录&#xff0c;浏览器会查找本机的hosts配置文件&…

【Clion 2】使用技巧

一、TODO: 说明&#xff1a; 有时需要标记部分代码以供将来参考&#xff1a; 优化和改进的领域、可能的更改、要讨论的问题等等。 支持&#xff1a; TODO和FIXME小写和大写。这些模式可以在任何受支持的文件类型的行注释和块注释内使用。 创建TODO项 在要添加注释的代码行中…

数据中心这个隐藏技巧,你一定要掌握!

在数字化时代&#xff0c;数据中心成为现代社会不可或缺的基础设施&#xff0c;它们承载着海量数据的存储、处理和传输&#xff0c;为各行各业的运营提供着坚实的支持。 然而&#xff0c;数据中心的稳定性和可靠性对于确保持续性运营至关重要。在数据中心中&#xff0c;蓄电池系…

KK集团再闯港交所:引领潮流零售市场,2023年一季度业绩增势显著

撰稿|行星 来源|贝多财经 7月31日&#xff0c;KK Group Company Holdings Limited&#xff08;下称“KK集团”&#xff09;在港交所更新招股书&#xff0c;补充了截至2023年3月31日的财务数据等信息&#xff0c;继续推进上市事宜&#xff0c;摩根士丹利和瑞信为其联席保荐人。…

NeRF-SLAM: Real-Time Dense Monocular SLAM with Neural Radiance Fields 论文阅读

论文信息 题目&#xff1a;NeRF-SLAM: Real-Time Dense Monocular SLAM with Neural Radiance Fields 作者&#xff1a;Antoni Rosinol, John J. Leonard&#xff0c; Luca Carlone 代码&#xff1a;https://github.com/ToniRV/NeRF-SLAM 来源&#xff1a;arxiv 时间&#xff…