日常编程中类型转化不可避免,将其他数据类型转化为字符串类型特别常见。使用fmt.Sprintf确实相当方便,因为接收的参数是interface{},所以什么类型都能通过该函数做转换。其底层使用了反射来实现该功能,正因如此,在转化效率上就比较低效。
到底转化效率差距有多大,可以通过函数对比来实现。
测试代码
func TestFmtStrconv(t *testing.T) {
loopTotalNum := 20000
// 对比 fmt.Sprintf 和 strconv.FormatInt
start := time.Now()
for i := 0; i < loopTotalNum; i++ {
fmt.Sprintf("%d", i)
}
fmt.Println("fmt elapsed for int: ", time.Since(start))
start = time.Now()
for i := 0; i < loopTotalNum; i++ {
strconv.FormatInt(int64(i), 10)
}
fmt.Println("strconv elapsed for int: ", time.Since(start))
// 对比 fmt.Sprintf 和 strconv.FormatInt
start = time.Now()
for i := 0; i < loopTotalNum; i++ {
fmt.Sprintf("%f", 2.0)
}
fmt.Println("fmt elapsed for double: ", time.Since(start))
start = time.Now()
for i := 0; i < loopTotalNum; i++ {
strconv.FormatFloat(2.0, 'e', 2, 32)
}
fmt.Println("strconv elapsed for double: ", time.Since(start))
hexBytes := []byte("hello world")
start = time.Now()
for i := 0; i < loopTotalNum; i++ {
fmt.Sprintf("%x", hexBytes)
}
fmt.Println("fmt elapsed for hex: ", time.Since(start))
start = time.Now()
for i := 0; i < loopTotalNum; i++ {
hex.EncodeToString(hexBytes)
}
fmt.Println("strconv elapsed for hex: ", time.Since(start))
}
测试数据
=== RUN TestFmtStrconv
fmt elapsed for int: 1.823708ms
strconv elapsed for int: 589.985µs
fmt elapsed for double: 4.610381ms
strconv elapsed for double: 2.074034ms
fmt elapsed for hex: 2.701938ms
strconv elapsed for hex: 916.221µs
--- PASS: TestFmtStrconv (0.01s)
PASS
ok go-demo/x-demo 0.020s
测试结论
- 整形
strconv.FormatInt执行时间为fmt.Sprintf的三分之一。
strconv.Itoa是对strconv.FormatInt做了封装,转化时间相当。
整型转化为字符串使用两种方式,
int:使用strconv.Itoa。
int32,int64,int8等:使用strconv.FormatInt。
- 浮点型
strconv.FormatFloat执行时间为fmt.Sprintf的二分之一。
- 16进制
hex.EncodeToString执行时间为fmt.Sprintf的三分之一。
总之,数据转化不建议使用fmt.Sprintf。