当前位置: 首页 > news >正文

深圳网站建设定制开发 .超凡科技百度手机助手网页版

深圳网站建设定制开发 .超凡科技,百度手机助手网页版,博客做资讯类网站,网站是不是要用代码做前面提到过,Go原生支持通过/操作符来连接多个字符串以构造一个更长的字符串,并且通过/操作符的字符串连接构造是最自然、开发体验最好的一种。 但Go还提供了其他一些构造字符串的方法,比如: ● 使用fmt.Sprintf; ● 使…

前面提到过,Go原生支持通过+/+=操作符来连接多个字符串以构造一个更长的字符串,并且通过+/+=操作符的字符串连接构造是最自然、开发体验最好的一种。

但Go还提供了其他一些构造字符串的方法,比如:
● 使用fmt.Sprintf;
● 使用strings.Join;
● 使用strings.Builder;
● 使用bytes.Buffer。
在这些方法中哪种方法最为高效呢?我们使用基准测试的数据作为参考:

var sl []string = []string{
"Rob Pike ",
"Robert Griesemer ",
"Ken Thompson ",
}
func concatStringByOperator(sl []string) string {
var s string
for _, v := range sl {
s += v
}
return s
}
func concatStringBySprintf(sl []string) string {
var s string
for _, v := range sl {
s = fmt.Sprintf("%s%s", s, v)
}
return s
}
func concatStringByJoin(sl []string) string {
return strings.Join(sl, "")
}
func concatStringByStringsBuilder(sl []string) string {
var b strings.Builder
for _, v := range sl {
b.WriteString(v)
}
return b.String()
}
func concatStringByStringsBuilderWithInitSize(sl []string) string {
var b strings.Builder
b.Grow(64)for _, v := range sl {
b.WriteString(v)
}
return b.String()
}
func concatStringByBytesBuffer(sl []string) string {
var b bytes.Buffer
for _, v := range sl {
b.WriteString(v)
}
return b.String()
}
func concatStringByBytesBufferWithInitSize(sl []string) string {
buf := make([]byte, 0, 64)
b := bytes.NewBuffer(buf)
for _, v := range sl {
b.WriteString(v)
}
return b.String()
}
func BenchmarkConcatStringByOperator(b *testing.B) {
for n := 0; n < b.N; n++ {
concatStringByOperator(sl)
}
}
func BenchmarkConcatStringBySprintf(b *testing.B) {
for n := 0; n < b.N; n++ {
concatStringBySprintf(sl)
}
}
func BenchmarkConcatStringByJoin(b *testing.B) {
for n := 0; n < b.N; n++ {
concatStringByJoin(sl)
}
}
func BenchmarkConcatStringByStringsBuilder(b *testing.B) {
for n := 0; n < b.N; n++ {
concatStringByStringsBuilder(sl)
}
}
func BenchmarkConcatStringByStringsBuilderWithInitSize(b *testing.B) {
for n := 0; n < b.N; n++ {
concatStringByStringsBuilderWithInitSize(sl)
}
}
func BenchmarkConcatStringByBytesBuffer(b *testing.B) {
for n := 0; n < b.N; n++ {
concatStringByBytesBuffer(sl)
}
}
func BenchmarkConcatStringByBytesBufferWithInitSize(b *testing.B) {
for n := 0; n < b.N; n++ {
concatStringByBytesBufferWithInitSize(sl)
}
}

运行该基准测试:

$go test -bench=. -benchmem ./string_concat_benchmark_test.go
goos: darwin
goarch: amd64
BenchmarkConcatStringByOperator-8 11744653 89.1 ns/op 80 B/op 2 allocs/op
BenchmarkConcatStringBySprintf-8 2792876 420 ns/op 176 B/op 8 allocs/op
BenchmarkConcatStringByJoin-8 22923051 49.1 ns/op 48 B/op 1 allocs/op
BenchmarkConcatStringByStringsBuilder-8 11347185 96.6 ns/op 112 B/op 3 allocs/op
BenchmarkConcatStringByStringsBuilderWithInitSize-8 26315769 42.3 ns/op 64 B/op 1 allocs/op
BenchmarkConcatStringByBytesBuffer-8 14265033 82.6 ns/op 112 B/op 2 allocs/op
BenchmarkConcatStringByBytesBufferWithInitSize-8 24777525 48.1 ns/op 48 B/op 1 allocs/op
PASS
ok command-line-arguments 8.816s

从基准测试的输出结果的第三列,即每操作耗时的数值来看:

● 做了预初始化的strings.Builder连接构建字符串效率最高;

● 带有预初始化的bytes.Buffer和strings.Join这两种方法效率十分接近,分列二三位;

● 未做预初始化的strings.Builder、bytes.Buffer和操作符连接在第三档次;

● fmt.Sprintf性能最差,排在末尾。

由此可以得出一些结论:

● 在能预估出最终字符串长度的情况下,使用预初始化的strings.Builder连接构建字符串效率最高

● strings.Join连接构建字符串的平均性能最稳定,如果输入的多个字符串是以[]string承载的,那么strings.Join也是不错的选择;

● 使用操作符连接的方式最直观、最自然,在编译器知晓欲连接的字符串个数的情况下,使用此种方式可以得到编译器的优化处理;

● fmt.Sprintf虽然效率不高,但也不是一无是处,如果是由多种不同类型变量来构建特定格式的字符串,那么这种方式还是最适合的。

转换

在前面的例子中,我们看到了string到[]rune以及string到[]byte的转换,这两个转
换也是可逆的,也就是说string和[]rune、[]byte可以双向转换。下面就是从[]rune或
[]byte反向转换为string的例子:

func main() {rs := []rune{0x4E2D,0x56FD,0x6B22,0x8FCE,0x60A8,}s := string(rs)fmt.Println(s)sl := []byte{0xE4, 0xB8, 0xAD,0xE5, 0x9B, 0xBD,0xE6, 0xAC, 0xA2,0xE8, 0xBF, 0x8E,0xE6, 0x82, 0xA8,}s = string(sl)fmt.Println(s)
}
$go run string_slice_to_string.go
中国欢迎您
中国欢迎您

无论是string转slice还是slice转string,转换都是要付出代价的,这些代价的根源
在于string是不可变的,运行时要为转换后的类型分配新内存。我们以byte slice与
string相互转换为例,看看转换过程的内存分配情况:

func byteSliceToString() {
sl := []byte{
0xE4, 0xB8, 0xAD,
0xE5, 0x9B, 0xBD,
0xE6, 0xAC, 0xA2,
0xE8, 0xBF, 0x8E,
0xE6, 0x82, 0xA8,
0xEF, 0xBC, 0x8C,
0xE5, 0x8C, 0x97,
0xE4, 0xBA, 0xAC,
0xE6, 0xAC, 0xA2,
0xE8, 0xBF, 0x8E,0xE6, 0x82, 0xA8,
}
_ = string(sl)
}
func stringToByteSlice() {
s := "中国欢迎您,北京欢迎您"
_ = []byte(s)
}
func main() {
fmt.Println(testing.AllocsPerRun(1, byteSliceToString))
fmt.Println(testing.AllocsPerRun(1, stringToByteSlice))
}

运行这个例子:

$go run string_mallocs_in_convert.go
1
1

我们看到,针对“中国欢迎您,北京欢迎您”这个长度的字符串,在string与byte
slice互转的过程中都要有一次内存分配操作。

在Go运行时层面,字符串与rune slice、byte slice相互转换对应的函数如下:

slicebytetostring: []byte -> string
slicerunetostring: []rune -> string
stringtoslicebyte: string -> []byte
stringtoslicerune: string -> []rune

以byte slice为例,看看slicebytetostring和stringtoslicebyte的实现:

const tmpStringBufSize = 32
type tmpBuf [tmpStringBufSize]byte
func stringtoslicebyte(buf *tmpBuf, s string) []byte {
var b []byte
if buf != nil && len(s) <= len(buf) {
*buf = tmpBuf{}
b = buf[:len(s)]
} else {
b = rawbyteslice(len(s))
}
copy(b, s)
return b
}func slicebytetostring(buf *tmpBuf, b []byte) (str string) {l := len(b)if l == 0 {return ""}// 此处省略一些代码if l == 1 {stringStructOf(&str).str = unsafe.Pointer(&staticbytes[b[0]])stringStructOf(&str).len = 1return}var p unsafe.Pointerif buf != nil && len(b) <= len(buf) {p = unsafe.Pointer(buf)} else {p = mallocgc(uintptr(len(b)), nil, false)}stringStructOf(&str).str = pstringStructOf(&str).len = len(b)memmove(p, (*(*slice)(unsafe.Pointer(&b))).array, uintptr(len(b)))return
}

想要更高效地进行转换,唯一的方法就是减少甚至避免额外的内存分配操作。我们看
到运行时实现转换的函数中已经加入了一些避免每种情况都要分配新内存操作的优化(如
tmpBuf的复用)。
slice类型是不可比较的,而string类型是可比较的,因此在日常Go编码中,我们会经
常遇到将slice临时转换为string的情况。Go编译器为这样的场景提供了优化。在运行时中
有一个名为slicebytetostringtmp的函数就是协助实现这一优化的:

func slicebytetostringtmp(b []byte) string {if raceenabled && len(b) > 0 {racereadrangepc(unsafe.Pointer(&b[0]),uintptr(len(b)),getcallerpc(),funcPC(slicebytetostringtmp))}if msanenabled && len(b) > 0 {msanread(unsafe.Pointer(&b[0]), uintptr(len(b)))}return *(*string)(unsafe.Pointer(&b))
}

该函数的“秘诀”就在于不为string新开辟一块内存,而是直接使用slice的底层存
储。当然使用这个函数的前提是:在原slice被修改后,这个string不能再被使用了。因此
这样的优化是针对以下几个特定场景的。

1)string(b)用在map类型的key中

b := []byte{'k', 'e', 'y'}
m := make(map[string]string)
m[string(b)] = "value"
m[[3]string{string(b), "key1", "key2"}] = "value1"2string(b)用在字符串连接语句中
b := []byte{'t', 'o', 'n', 'y'}
s := "hello " + string(b) + "!"3string(b)用在字符串比较中
s := "tom"
b := []byte{'t', 'o', 'n', 'y'}
if s < string(b) {
...
}

Go编译器对用在for-range循环中的string到[]byte的转换也有优化处理,它不会为
[]byte进行额外的内存分配,而是直接使用string的底层数据。

看下面的例子:

func convert() {
s := "中国欢迎您,北京欢迎您"
sl := []byte(s)
for _, v := range sl {
_ = v
}
}
func convertWithOptimize() {
s := "中国欢迎您,北京欢迎您"
for _, v := range []byte(s) {
_ = v
}
}
func main() {
fmt.Println(testing.AllocsPerRun(1, convert))
fmt.Println(testing.AllocsPerRun(1, convertWithOptimize))
}

运行这个例子程序:

$go run string_for_range_covert_optimize.go
1
0

从结果我们看到,convertWithOptimize函数将string到[]byte的转换放在for-range
循环中,Go编译器对其进行了优化,节省了一次内存分配操作。

http://www.hkea.cn/news/600227/

相关文章:

  • 厦门加盟网站建设线上推广营销
  • 定制网站案例seo搜索引擎优化薪酬
  • 网站制作成功后怎么使用浏览器观看b站视频的最佳设置
  • 一家专门做开网店的网站北京seo专员
  • 专业企业网站搭建服务头条权重查询
  • 去哪儿网站上做民宿需要材料免费的黄冈网站有哪些平台
  • 网站建设网现在推广什么app最挣钱
  • 嘉兴装修公司做网站安装百度到桌面
  • 电商网站特点外贸营销网站建站
  • 上海市住房城乡建设管理委员会网站网络营销软文范例大全800
  • 莱芜区政协网站做网络优化的公司排名
  • 太原网站建设开发公司电商运营基本知识
  • php做企业网站seo网站推广企业
  • 万网网站备案授权书免费发布推广信息的b2b
  • 乡镇可以做门户网站seo是什么意思职业
  • 建设银行网站优点做个公司网站大概多少钱
  • 网站标题的设置方法哪家建设公司网站
  • 网站空间托管电商平台的营销方式
  • 网站制作专业的公司有哪些seo网站编辑是做什么的
  • wordpress 分栏seo怎么优化简述
  • php网站开发 多少钱推广方案策划
  • 芜湖做网站公司广州seo好找工作吗
  • 做网站找客户百度竞价推广公司
  • 深圳网站建设怎么办互联网营销的优势
  • 课程网站开发背景网站推广的几种方法
  • 商城网站建设模板一份完整的营销策划方案
  • 推广网站建设网站权重查询工具
  • t型布局网站怎么做建设网官方网站
  • 哪个建设网站推广竞价托管公司
  • 网站建设傲seo网站是什么意思