go string 实现

在go中string是不可变的,这意味着对string发生改变的操作实际上都是通过分配新的string去实现的

在string内存分配上,对于小对象分配到栈,大对象分配到堆中

string在go中的结构其实很简单,就是一个指向实际数据的指针以及字符串的长度。

type stringStruct struct {str unsafe.Pointerlen int
}

创建

先创建string Struct,然后转换为string

以字符指针为字符串指针,到第一个null byte偏移量为长度

func gostringnocopy(str *byte) string {ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)}s := *(*string)(unsafe.Pointer(&ss))return s
}

找到第一个非null byte的位置

func findnull(s *byte) int {// 若指针为nil,则返回0if s == nil {return 0}// Avoid IndexByteString on Plan 9 because it uses SSE instructions// on x86 machines, and those are classified as floating point instructions,// which are illegal in a note handler.// 对于plan9系统,IndexByteString是非法的// 因此需要转换为字符数组,遍历直到发现null byteif GOOS == "plan9" {p := (*[maxAlloc/2 - 1]byte)(unsafe.Pointer(s))l := 0for p[l] != 0 {l++}return l}// 最小页大小const pageSize = 4096offset := 0ptr := unsafe.Pointer(s)// IndexByteString uses wide reads, so we need to be careful// with page boundaries. Call IndexByteString on// [ptr, endOfPage) interval.// 计算到达下页剩余的byte数量safeLen := int(pageSize - uintptr(ptr)%pageSize)for {// 以本页剩余byte数量为长度,指针为字符数组转换为stringt := *(*string)(unsafe.Pointer(&stringStruct{ptr, safeLen}))// 尝试找到本页字符串中的null byte位置,如果找到,返回之前遍历过的偏移量加找到的位置if i := bytealg.IndexByteString(t, 0); i != -1 {return offset + i}// 移动指针到下页ptr = unsafe.Pointer(uintptr(ptr) + uintptr(safeLen))// 更新偏移量和剩余byte数量为页大小offset += safeLensafeLen = pageSize}
}

指定大小进行创建string

func rawstring(size int) (s string, b []byte) {// 分配指定大小的字符数组。// 小对象分配到每个p cache的空闲list,大对象(>32kB)分配到堆中p := mallocgc(uintptr(size), nil, false)// 转换为指定大小string和字符数组return unsafe.String((*byte)(p), size), unsafe.Slice((*byte)(p), size)
}

C string转换为Go string

func gostring(p *byte) string {// 找到字符串长度l := findnull(p)if l == 0 {return ""}// 分配对应长度的strings, b := rawstring(l)// 复制字符指针的内容到新分配的stringmemmove(unsafe.Pointer(&b[0]), unsafe.Pointer(p), uintptr(l))return s
}

拼接

在缓存能承载拼接字符串时,直接用缓存去储存拼接字符串。这能防止频繁的堆分配,并减少额外的gc开销

缓存不行才会重新分配内存

// concatstrings implements a Go string concatenation x+y+z+...
// The operands are passed in the slice a.
// If buf != nil, the compiler has determined that the result does not
// escape the calling function, so the string data can be stored in buf
// if small enough.
func concatstrings(buf *tmpBuf, a []string) string {idx := 0l := 0count := 0for i, x := range a {// 判断拼接字符串长度是否合法n := len(x)if n == 0 {continue}if l+n < l {throw("string concatenation too long")}// 递增总字符串长度l += n// 递增非空字符串数量count++// 记录最后非空字符串索引idx = i}// 如果非空字符串数量为0,则返回空if count == 0 {return ""}// If there is just one string and either it is not on the stack// or our result does not escape the calling frame (buf != nil),// then we can return that string directly.// 如果仅有一个非空字符串,并且它不需要转义当前帧(buf != nil)或者它不在栈上(!stringDataOnStack(a[idx])),则直接返回该字符串if count == 1 && (buf != nil || !stringDataOnStack(a[idx])) {return a[idx]}// 分配总字符串长度的string,并将拼接字符串复制到新string的字符数组中s, b := rawstringtmp(buf, l)for _, x := range a {copy(b, x)b = b[len(x):]}return s
}func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) {if buf != nil && l <= len(buf) {b = buf[:l]s = slicebytetostringtmp(&b[0], len(b))} else {s, b = rawstring(l)}return
}// stringDataOnStack reports whether the string's data is
// stored on the current goroutine's stack.
func stringDataOnStack(s string) bool {ptr := uintptr(unsafe.Pointer(unsafe.StringData(s)))stk := getg().stackreturn stk.lo <= ptr && ptr < stk.hi
}

string常见实现方式对比

eager copy

在每次拷贝时将原string对应内容以及所持有的动态资源完整复制

优点

  • 实现简单
  • string互相独立,不会相互影响

缺点

  • 字符串较大时,比较浪费空间
copy on write

写时复制只有在string需要对对象进行修改时才会执行复制

在实现中,需要记录string引用的数量refCount,每当string被引用一次,refCount++。而当需要对string做修改时,则重新申请空间并复制原字符串,refCount–。最终当refCount为0时回收内存

优点

  • 字符串空间较大时,减少了分配、复制字符串时间和空间

缺点

  • 记录string引用的数量需要原子操作,带来性能损耗
  • 在某些情况下反而会带来额外开销。比如线程1访问字符串A,线程2访问字符串B,字符串A、B共享同一片内容,当线程1、2都修改同一片字符串,就会都进行两次复制,外加最开始的分配和最后的内存释放,这比eager copy的两次内存分配的代价更高
Small String Optimization

基于字符串大多数较短的特性,利用string本身的栈空间来储存短字符串;当字符串长度大于临界点时,则使用eager copy

优点

  • 短字符串时,无动态内存分配

缺点

  • string对象占用空间更大

Ref

  1. https://www.cnblogs.com/promise6522/archive/2012/03/22/2412686.html
  2. https://www.zhihu.com/question/54664311/answer/1978680475

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

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

相关文章

用于与 HTTP 服务器通信的函数

用于与 HTTP 服务器通信的函数 Plant Simulation 提供了许多使用 HTTP 协议与 HTTP 服务器通信的函数。可使用这些函数来发送 HTTP 请求、发送数据和从 HTTP 响应中接收数据&#xff0c;以及在 HTTP 服务器上创建和删除资源&#xff1a; httpGetRequest 发送 GET 请求。请求…

在 Visual Studio 2022 (VS2022) 中删除 Git 分支的步骤如下

git branch -r PS \MauiApp1> git push origin --delete “20240523备份” git push origin --delete “20240523备份”

PCL 常用小知识

文章目录 一、时间计算二、实现类似`pcl::PointCloud::Ptr`和`pcl::PointCloud`的两个类相互转换三、查找点云的x,y,z的极值四、知道需要保存点的索引,从原点云中拷贝点到新点云五、从点云里删除和添加点六、对点云进行全局或局部变换七、链接两个点云字段(两点云大小必须相…

若依 ruoyi-vue 用户账号前后端参数校验密码 手机号 邮箱

前端 <el-dialog :title"title" :visible.sync"open" width"800px" append-to-body><el-form ref"form" :model"form" :rules"rules" label-width"120px"><el-row><el-col :span…

Vue3骨架屏(Skeleton)

效果如下图&#xff1a;在线预览 APIs 参数说明类型默认值必传animated是否展示动画效果booleantruefalsebutton是否使用按钮占位图boolean | SkeletonButtonPropsfalsefalseavatar是否显示头像占位图boolean | SkeletonAvatarPropsfalsefalseinput是否使用输入框占位图boolea…

SOLIDWORKS二次开发服务商 慧德敏学

SOLIDWORKS是一套三维设计软件, 采用特征建模、变量化驱动可方便地实现三维建模、装配和生成工程图。SOLIDWORKS软件本身所具有的交互方式, 可以使用户对已生成模型的尺寸、几何轮廓和相互约束关系随时进行修改, 而不需要编程。但要实现设计意义上的变量化绘图和系列化设计, 需…

java-查询字符串当中是否包含中文

文章目录 前言java-查询字符串当中是否包含中文 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&#xff0c;那欢迎常来啊…

软考系统架构师一些知识点记录-1

个人随笔 (Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu) 引言 准备去参加软考的考试&#xff0c;但对一些概念掌握的还不够&#xff0c;借此机会&#xff0c;整理记录一二&#xff0c;便于自己理解掌握。 知识范围 感觉不够清晰的部分主要是第三篇和第四篇的部分。…

国际顶会认可!KaiwuDB 论文入选 ICDE 2024

导 读 近日&#xff0c;KaiwuDB 与中国人民大学合作的论文 FOSS: A Self-Learned Doctor for Query Optimizer 被数据库领域顶会The 40th IEEE International Conference on Data Engineering (ICDE 2024) 录用啦! 论文中提出了具备自学习、自诊断能力的查询优化器 FOSS&…

USB官方文档怎么下载

直接登录USB官网"https://usb.org/" 如&#xff0c;我需要查找与USB device class相关的文档 点击搜索后就能找到。 学习还是要以官方文档为主&#xff0c;博客上的介绍不可信&#xff0c;USB协议规范很重要!

商品发布功能

文章目录 1.SPU和SKU介绍1.SPU2.SKU3.两者之间的关系 2.完成商品发布界面1.组件引入1.commoditylaunch.vue 引入到 src/views/modules/commodity下2.multiUpload.vue 引入到 src/components/upload/multiUpload.vue 2.创建菜单1.创建目录2.创建菜单&#xff0c;注意菜单路由要匹…

go语言中同一for循环体内的多个初始变量和多个自增变量用法示例

在go语言的for循环体中&#xff0c;我们可以同时初始多个变量&#xff0c; 也可以同时多多个变量进行自增/自减操作&#xff0c; 用法如下&#xff1a; for 后面的多个初始化变量使用的是逗号分隔的批量赋值操作&#xff0c;多个变量自增自减使用 加减运算符和逗号分隔 字符…

MySQL之性能剖析和Schema与数据类型优化(一)

性能剖析总结 1.定义性能最有效的方法是响应时间2.如果无法测量就无法有效地优化&#xff0c;所以性能优化工作需要基于高质量、全方位及完整的响应时间测量3.测量的最佳开始点是应用程序&#xff0c;而不是数据库。即使问题出在底层的数据库&#xff0c;借助良好的测量也可以…

C++系列-友元

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 我们在之前的文章有提到友元&#xff0c;我们先来看下面的这段包含了友元的代码&#xff1a; ​​#define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace…

CLIP论文学习

学习来自B站bryanyzhu

jdk17安装教程详细(jdk17安装超详细图文)

2021年9月14日JDK17 发布&#xff0c;其中不仅包含很多新语言功能&#xff0c;而且与旧版 JDK 相比&#xff0c;性能提升也非常明显。与之前 LTS 版本的 JDK 8 和 JDK 11 相比&#xff0c;JDK17 的性能提升尤为明显&#xff0c;本文将教你如何安装 相比于JDK1.8&#xff0c;JD…

虚拟机网络设置为桥接模式后未显示网络

本方法为&#xff0c;VMware配置正确&#xff0c;但在尝试其他办法后未能成功解决的人提供一种方法 本机的虚拟机使用NAT模式正常使用 但是使用桥接模式后重启&#xff0c;未发现虚拟机内网络设置,详见下图&#xff1a; 使用 ifconfig 查看网络详情 发现没有ens33接口 查看硬…

双非本科,逆袭中大厂的 Java 学习路线

从零基础入门 Java&#xff0c;到最后秋招上岸&#xff0c;笔者也是花费了不少的经历&#xff0c;也走了很多弯路。这一篇文章会记录下真正有用的学习路线。 为什么要强调真正有用&#xff1f;网上的很多所谓从入门到求职&#xff0c;推荐的路线都超级长&#xff0c;零基础的同…

LeetCode198:打家劫舍

题目描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个房屋存…

【学习笔记】Windows GDI绘图(六)图形路径GraphicsPath详解(中)

上一篇【学习笔记】Windows GDI绘图(五)图形路径GraphicsPath详解(上)介绍了GraphicsPath类的构造函数、属性和方法AddArc添加椭圆弧、AddBezier添加贝赛尔曲线、AddClosedCurve添加封闭基数样条曲线、AddCurve添加开放基数样条曲线、基数样条如何转Bezier、AddEllipse添加椭圆…