不要使用fmt.Sprintf转换数据类型

2,882 阅读1分钟

日常编程中类型转化不可避免,将其他数据类型转化为字符串类型特别常见。使用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.Itoaint32,int64,int8等:使用strconv.FormatInt

  • 浮点型

strconv.FormatFloat执行时间为fmt.Sprintf二分之一

  • 16进制

hex.EncodeToString执行时间为fmt.Sprintf三分之一

总之,数据转化不建议使用fmt.Sprintf