thinkeridea / go-extend

go语言扩展包,收集一些常用的操作函数,辅助更快的完成开发工作,并减少重复代码

Home Page:https://pkg.go.dev/github.com/thinkeridea/go-extend

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

exstrings.Reverse 性能改进

thinkeridea opened this issue · comments

该方法收集于官网 How to write Go code 中的一个代码片段,因曾经面试遇到这个面试题而留心。

近期回答 Go Forum 中关于 [SOLVED] String size of 20 character 中字符串长度截取使用 []rune 类型与字符串类型转换,期望处理安全的 unicode 字符。

我对此方法提出了性能质疑,编写了基于 utf8.DecodeRuneInString 的计算版本,并做了性能测试 相关回复在这里 发现两个种方法性能差距惊人。

我想到了 go-extend 中的 exstrings.Reverse 方法也是用了类似的类型转换,所以我做了一个小的实验来测试不同实现的性能。

测试的代码片段在这里:

package test

import (
	"testing"
	"unicode/utf8"

	"github.com/thinkeridea/go-extend/exbytes"
)

func Reverse(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

func ReverseRangeString(s string) string {
	n := len(s)
	buf := make([]byte, n)
	var start, end int
	for _, r := range s {
		l := utf8.RuneLen(r)
		n -= l
		end = start + l
		copy(buf[n:], s[start:end])
		start = end
	}

	return exbytes.ToString(buf)
}

func ReverseUTF8DecodeRuneInString(s string) string {
	n := len(s)
	buf := make([]byte, n)
	var start, size, end int
	for i := 0; i < len(s[start:]); {
		_, size = utf8.DecodeRuneInString(s[start:])
		n -= size
		end = start + size
		copy(buf[n:], s[start:end])
		start = end
	}

	return exbytes.ToString(buf)
}

func BenchmarkReverse(b *testing.B) {
	s := "Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。为了方便搜索和识别,有时会将其称为Golang。"
	for i := 0; i < b.N; i++ {
		Reverse(s)
	}
}

func BenchmarkReverseRangeString(b *testing.B) {
	s := "Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。为了方便搜索和识别,有时会将其称为Golang。"
	for i := 0; i < b.N; i++ {
		ReverseRangeString(s)
	}
}

func BenchmarkReverseUTF8DecodeRuneInString(b *testing.B) {
	s := "Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。为了方便搜索和识别,有时会将其称为Golang。"
	for i := 0; i < b.N; i++ {
		ReverseUTF8DecodeRuneInString(s)
	}
}

这里是测试结果:

goos: darwin
goarch: amd64
BenchmarkReverse-8                                695608              1692 ns/op             480 B/op          2 allocs/op
BenchmarkReverseRangeString-8                    1439391               823 ns/op             192 B/op          1 allocs/op
BenchmarkReverseUTF8DecodeRuneInString-8         1735574               692 ns/op             192 B/op          1 allocs/op
PASS
ok      command-line-arguments  5.978s

这样的结果令我非常的吃惊,我暂时没有找到utf8获取指定字符的字节数方法,我只得到了逐个字符解析的方法,但这已经令我非常的吃惊,我想这样还有很大的改善空间

哇!你已经把它放入仓库了? XD good job good job!

@hollowaykeanho 这只是应用 utf8.DecodeRuneInString 调整了 exstrings.Reverse 的函数,我并没有把截断多字节字符串的方法放入到仓库中,暂时还不明确如何提供函数接口,但我有这个计划近期会实现这一目标。