不同写法的性能差异(1)

简介: 不同写法的性能差异(1)

达到相同目的,可以有多种写法,每种写法有性能、可读性方面的区别,本文旨在探讨不同写法之间的性能差异




len(str)  vs  str == ""


本部分参考自:

问个 Go 问题,字符串 len == 0 和 字符串== "" ,有啥区别?

微信截图_20230801192935.png

package gotest
func Test1() bool {
  var v string
  if v == "" {
    return true
  }
  return false
}
func Test2() bool {
  var v string
  if len(v) == 0 {
    return true
  }
  return false
}
package gotest
import (
  "testing"
)
func BenchmarkTest1(b *testing.B) {
  for i := 0; i < b.N; i++ {
    Test1()
  }
}
func BenchmarkTest2(b *testing.B) {
  for i := 0; i < b.N; i++ {
    Test2()
  }
}

执行 go test -test.bench=".*"

goos: darwin
goarch: amd64
pkg: note/performance
BenchmarkTest1-8        1000000000               0.467 ns/op
BenchmarkTest2-8        1000000000               0.464 ns/op
PASS
ok      note/performance        1.290s

第4行显示了BenchmarkTest1执行了1000000000次,每次的执行平均时间是0.467纳秒,

第5行显示了BenchmarkTest2也执行了1000000000次,每次的平均执行时间是0.464 纳秒。

最后一行显示总共的执行时间为 1.290s


可使用-count来指定执行多少次 go test -test.bench=".*" -count=5:

goos: darwin
goarch: amd64
pkg: note/performance
BenchmarkTest1-8        1000000000               0.485 ns/op
BenchmarkTest1-8        1000000000               0.484 ns/op
BenchmarkTest1-8        1000000000               0.464 ns/op
BenchmarkTest1-8        1000000000               0.497 ns/op
BenchmarkTest1-8        1000000000               0.479 ns/op
BenchmarkTest2-8        1000000000               0.490 ns/op
BenchmarkTest2-8        1000000000               0.476 ns/op
BenchmarkTest2-8        1000000000               0.482 ns/op
BenchmarkTest2-8        1000000000               0.469 ns/op
BenchmarkTest2-8        1000000000               0.474 ns/op
PASS
ok      note/performance        5.791s

go test --bench=. -benchmem

(添加 -benchmem 参数,可以提供每次操作分配内存的次数,以及每次操作分配的字节数。参考 go benchmark 性能测试)

goos: darwin
goarch: amd64
pkg: note/performance
BenchmarkTest1-8        1000000000               0.471 ns/op           0 B/op          0 allocs/op
BenchmarkTest2-8        1000000000               0.462 ns/op           0 B/op          0 allocs/op
PASS
ok      note/performance        1.457s

经过多次测试,可知:

<1>. 性能几乎没有差别

<2>. 均不涉及内存申请和操作,均为 0 allocs/op。(也说明变量并不是声明了,就有初始化动作. Go 编译器有做优化)


进一步看两者的汇编代码,以细究具体区别在哪里:

go tool compile -S gotest.go:

"".Test1 STEXT nosplit size=6 args=0x8 locals=0x0
        0x0000 00000 (gotest.go:3)      TEXT    "".Test1(SB), NOSPLIT|ABIInternal, $0-8
        0x0000 00000 (gotest.go:3)      PCDATA  $0, $-2
        0x0000 00000 (gotest.go:3)      PCDATA  $1, $-2
        0x0000 00000 (gotest.go:3)      FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (gotest.go:3)      FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (gotest.go:3)      FUNCDATA        $2, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (gotest.go:6)      PCDATA  $0, $0
        0x0000 00000 (gotest.go:6)      PCDATA  $1, $0
        0x0000 00000 (gotest.go:6)      MOVB    $1, "".~r0+8(SP)
        0x0005 00005 (gotest.go:6)      RET
        0x0000 c6 44 24 08 01 c3                                .D$...
"".Test2 STEXT nosplit size=6 args=0x8 locals=0x0
        0x0000 00000 (gotest.go:11)     TEXT    "".Test2(SB), NOSPLIT|ABIInternal, $0-8
        0x0000 00000 (gotest.go:11)     PCDATA  $0, $-2
        0x0000 00000 (gotest.go:11)     PCDATA  $1, $-2
        0x0000 00000 (gotest.go:11)     FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (gotest.go:11)     FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (gotest.go:11)     FUNCDATA        $2, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (gotest.go:14)     PCDATA  $0, $0
        0x0000 00000 (gotest.go:14)     PCDATA  $1, $0
        0x0000 00000 (gotest.go:14)     MOVB    $1, "".~r0+8(SP)
        0x0005 00005 (gotest.go:14)     RET
        0x0000 c6 44 24 08 01 c3                                .D$...
go.cuinfo.packagename. SDWARFINFO dupok size=0
        0x0000 67 6f 74 65 73 74                                gotest
go.loc."".Test1 SDWARFLOC size=0
go.info."".Test1 SDWARFINFO size=46
        0x0000 03 22 22 2e 54 65 73 74 31 00 00 00 00 00 00 00  ."".Test1.......
        0x0010 00 00 00 00 00 00 00 00 00 00 01 9c 00 00 00 00  ................
        0x0020 01 0f 7e 72 30 00 01 03 00 00 00 00 00 00        ..~r0.........
        rel 0+0 t=24 type.bool+0
        rel 10+8 t=1 "".Test1+0
        rel 18+8 t=1 "".Test1+6
        rel 28+4 t=30 gofile../Users/dashen/go/src/note/performance/gotest.go+0
        rel 40+4 t=29 go.info.bool+0
go.range."".Test1 SDWARFRANGE size=0
go.debuglines."".Test1 SDWARFMISC size=11
        0x0000 04 02 14 06 41 04 01 03 7b 06 01                 ....A...{..
go.loc."".Test2 SDWARFLOC size=0
go.info."".Test2 SDWARFINFO size=46
        0x0000 03 22 22 2e 54 65 73 74 32 00 00 00 00 00 00 00  ."".Test2.......
        0x0010 00 00 00 00 00 00 00 00 00 00 01 9c 00 00 00 00  ................
        0x0020 01 0f 7e 72 30 00 01 0b 00 00 00 00 00 00        ..~r0.........
        rel 0+0 t=24 type.bool+0
        rel 10+8 t=1 "".Test2+0
        rel 18+8 t=1 "".Test2+6
        rel 28+4 t=30 gofile../Users/dashen/go/src/note/performance/gotest.go+0
        rel 40+4 t=29 go.info.bool+0
go.range."".Test2 SDWARFRANGE size=0
go.debuglines."".Test2 SDWARFMISC size=13
        0x0000 04 02 03 08 14 06 41 04 01 03 73 06 01           ......A...s..
gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
        0x0000 01 00 00 00 00 00 00 00                          ........

编译出来的汇编代码是完全一致的,可以明确 Go 编译器对此做了优化(应该是直接比对了)


----- EOF选看: -----

生成pprof:

go test -bench=".*" -cpuprofile=cpu.profile ../xxx文件夹

此时会在文件夹下,生成一个 xxx.test

go tool pprof xxx.test cpu.profile :

File: performance.test
Type: cpu
Time: Apr 12, 2021 at 5:20pm (CST)
Duration: 1.23s, Total samples = 970ms (78.99%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) 
(pprof) o
  call_tree                 = false                
  compact_labels            = true                 
  cumulative                = flat                 //: [cum | flat]
  divide_by                 = 1                    
  drop_negative             = false                
  edgefraction              = 0.001                
  focus                     = ""                   
  granularity               = filefunctions        //: [addresses | filefunctions | files | functions | lines]
  hide                      = ""                   
  ignore                    = ""                   
  mean                      = false                
  nodecount                 = -1                   //: default
  nodefraction              = 0.005                
  noinlines                 = false                
  normalize                 = false                
  output                    = ""                   
  prune_from                = ""                   
  relative_percentages      = false                
  sample_index              = cpu                  //: [samples | cpu]
  show                      = ""                   
  show_from                 = ""                   
  tagfocus                  = ""                   
  taghide                   = ""                   
  tagignore                 = ""                   
  tagshow                   = ""                   
  trim                      = true                 
  trim_path                 = ""                   
  unit                      = minimum 

执行 go tool pprof -web xxx.test cpu.profile

微信截图_20230801193209.png

----- EOF -----




几种 int转string 方法的性能差异

package shuang
import (
  "fmt"
  "strconv"
  "testing"
)
func BenchmarkSprintf(b *testing.B) {
  n := 10
  b.ResetTimer()
  for i := 0; i < b.N; i++ {
    fmt.Sprintf("%d", n)
  }
}
func BenchmarkItoa(b *testing.B) {
  n := 10
  b.ResetTimer()
  for i := 0; i < b.N; i++ {
    strconv.Itoa(n)
  }
}
func BenchmarkFormatInt(b *testing.B) {
  n := int64(10)
  b.ResetTimer()
  for i := 0; i < b.N; i++ {
    strconv.FormatInt(n, 10)
  }
}

执行 go test -test.bench=".*" -benchmem

goos: darwin
goarch: amd64
pkg: dashen
BenchmarkSprintf-8      14417409                75.9 ns/op            16 B/op          2 allocs/op
BenchmarkItoa-8         452276205                2.64 ns/op            0 B/op          0 allocs/op
BenchmarkFormatInt-8    492620018                2.42 ns/op            0 B/op          0 allocs/op
PASS
ok      dashen  4.518s

第4行显示了BenchmarkSprintf-8 执行了14417409次,每次的执行平均时间是75.9纳秒, 每次操作有两次内存分配,每次分配了16Byte大小的内存空间

第5行显示了BenchmarkItoa-8 执行了452276205次,每次的平均执行时间是2.64 纳秒, 无内存分配

第6行显示了BenchmarkFormatInt-8 执行了492620018次,每次的平均执行时间是2.42 纳秒, 无内存分配

最后一行显示总共的执行时间为 4.518s


可见, strconv.FormatInt(n, 10)strconv.Itoa(n) 性能差不多, fmt.Sprintf() 性能最差

Golang 中整数转字符串


目录
相关文章
|
JavaScript
成功解决:如何通过this.$router.push(“/Login“)的方式传参,在另外一个页面接收数据的问题
这篇文章介绍了如何在Vue框架中通过路由跳转传递参数,并在另一个页面接收这些参数。具体方法是使用`this.$router.push`方法的`params`属性传递对象,然后在目标页面通过`this.$route.params`接收传递的参数。
成功解决:如何通过this.$router.push(“/Login“)的方式传参,在另外一个页面接收数据的问题
|
11月前
|
SQL JSON 关系型数据库
17.6K star!后端接口零代码的神器来了,腾讯开源的ORM库太强了!
"🏆 实时零代码、全功能、强安全 ORM 库 🚀 后端接口和文档零代码,前端定制返回 JSON 的数据和结构"
215 1
|
存储 Oracle 关系型数据库
ORACLE 动态游标的使用
ORACLE 动态游标的使用
253 4
|
机器学习/深度学习 并行计算 调度
构建高效GPU算力平台:挑战、策略与未来展望
【8月更文第5天】随着深度学习、高性能计算和大数据分析等领域的快速发展,GPU(图形处理器)因其强大的并行计算能力和浮点运算速度而成为首选的计算平台。然而,随着模型规模的增长和技术的进步,构建高效稳定的GPU算力平台面临着新的挑战。本文旨在探讨这些挑战、应对策略以及对未来发展的展望。
890 1
|
域名解析 Kubernetes 负载均衡
在K8S中,外部访问容器服务,比如说提供了一个域名,链路怎么走?数据经过哪些组件?
在K8S中,外部访问容器服务,比如说提供了一个域名,链路怎么走?数据经过哪些组件?
|
关系型数据库 MySQL 数据安全/隐私保护
【MySQL】手把手教你MySQL各版本忘记密码如何处理
【MySQL】手把手教你MySQL各版本忘记密码如何处理
|
消息中间件 监控 Kafka
深入解析:Kafka 为何不支持全面读写分离?
**Kafka 2.4 引入了有限的读写分离,允许Follower处理只读请求,以缓解Leader压力。但这不适用于所有场景,特别是实时数据流和日志分析,因高一致性需求及PULL同步方式导致的复制延迟,可能影响数据实时性和一致性。在设计系统时需考虑具体业务需求。**
270 1
|
Java 数据处理 API
Filnk的概念优缺点以及应用实战
【5月更文挑战第29天】Apache Flink 是一个高性能、灵活且完整的流处理平台,它支持数据的实时处理和分析。Flink 的设计和架构包含了多个关键技术点,这些技术点共同构成了 Flink 强大的数据处理能力。
380 1
|
存储 负载均衡 安全
MQTT常见问题之MQTT使用共享订阅失败如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总: