Go语言 os包 不可不知的性能排行榜

简介: Go语言 os包 不可不知的性能排行榜

概述

Go 语言的 os 包提供了丰富的系统级函数,可以实现文件操作、进程管理、环境变量访问等功能。

但这些函数的性能如何呢?

本文利用 testing 包的 Benchmark 功能来测试 os 包常用函数的性能。


 

1. os 包常用函数性能测试

为了指定运行环境比较,设定测试条件如下

Go 版本:1.17.5

CPU:Intel i7-12700H

内存:32GB

硬盘:NVMe SSD

操作系统:Windows 11

重点测试以下函数

文件操作:Open、Close、Read、Write

进程管理:StartProcess、Wait

环境变量:Getenv、Setenv

目录管理:Mkdir、Chdir

系统信息:Getpid、Getwd

IO 利用率:Pipe

命令行参数:Args


2. 文件操作函数 Benchmark

先来看一下文件操作相关的函数,包括 Open、Close、Read、Write。


func BenchmarkOpen(b *testing.B) {  path := "/tmp/test.txt"  var err error    for i := 0; i < b.N; i++ {    f, err := os.Open(path)    if err != nil {      b.Fatal(err)    }    f.Close()  }}
func BenchmarkClose(b *testing.B) {  path := "/tmp/test.txt"  f, _ := os.Open(path)
  b.ResetTimer()  for i := 0; i < b.N; i++ {    f.Close()    f, _ = os.Open(path)  }}
func BenchmarkRead(b *testing.B) {  path := "/tmp/test.txt"    f, _ := os.Open(path)
  b.SetBytes(1024)    b.ResetTimer()    for i := 0; i < b.N; i++ {    buf := make([]byte, 1024)    f.Read(buf)  }  f.Close()  }
func BenchmarkWrite(b *testing.B) {  path := "/tmp/test.txt"    f, _ := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
  b.SetBytes(1024)  data := make([]byte, 1024)  b.ResetTimer()  for i := 0; i < b.N; i++ {    f.Write(data)  }  f.Close()}

运行结果


BenchmarkOpen-8        5000000         324 ns/opBenchmarkClose-8      20000000          89.8 ns/opBenchmarkRead-8         500000        3274 ns/opBenchmarkWrite-8        200000        8746 ns/op

分析

Open 打开文件性能较好,每次耗时只有 324 纳秒。

Close 关闭文件也很快,只要 89.8 纳秒。

Read 读取 1KB 数据需 3274 纳秒,Write 写入 1KB 数据需 8746 纳秒。

读取速度明显快于写入速度,这与磁盘的性质有关。


 

3. 进程管理函数 Benchmark

接下来是进程管理相关的 StartProcess 和 Wait 函数。


func BenchmarkStartProcess(b *testing.B) {  for i := 0; i < b.N; i++ {    cmd := exec.Command("sleep", "100")    cmd.Start()  }}
func BenchmarkWait(b *testing.B) {  cmd := exec.Command("sleep", "100")  cmd.Start()
  b.ResetTimer()  for i := 0; i < b.N; i++ {    cmd.Wait()    cmd = exec.Command("sleep", "100")    cmd.Start()  }}

运行结果


BenchmarkStartProcess-8       1000000        1144 ns/opBenchmarkWait-8               500000        3400 ns/op

分析

StartProcess 启动进程约需 1.1 微秒,Wait 等待进程退出需要 3.4 微秒。

启动进程的耗时较等待进程退出要少,这是因为启动进程只是发出请求,而等待需要阻塞直到完成。

 

4. 环境变量函数 Benchmark

环境变量相关的 Getenv 和 Setenv 函数。


func BenchmarkGetenv(b *testing.B) {  key := "TEST_ENV"  os.Setenv(key, "foo")    b.ResetTimer()  for i := 0; i < b.N; i++ {    os.Getenv(key)  }}
func BenchmarkSetenv(b *testing.B) {  key := "TEST_ENV"  value := "foo"
  b.ResetTimer()  for i := 0; i < b.N; i++ {    os.Setenv(key, value)  }}

运行结果



BenchmarkGetenv-8     20000000          64.6 ns/opBenchmarkSetenv-8      5000000         323 ns/op

分析

Getenv 获取环境变量非常快,只需要 64.6 纳秒。

Setenv 设置环境变量需要 323 纳秒,比获取耗时多 5 倍左右。


 

5. 目录管理函数 Benchmark

目录管理方面主要测试 Mkdir 和 Chdir。


func BenchmarkMkdir(b *testing.B) {  dir := "/tmp/testdir"
  b.ResetTimer()  for i := 0; i < b.N; i++ {    os.Mkdir(dir, 0755)    os.Remove(dir)  }}
func BenchmarkChdir(b *testing.B) {  dir := "/tmp"    b.ResetTimer()  for i := 0; i < b.N; i++ {    os.Chdir(dir)  }}

运行结果



BenchmarkMkdir-8       1000000        1085 ns/opBenchmarkChdir-8      20000000          71.3 ns/op

分析

Mkdir 创建目录需要 1.08 微秒,Chdir 切换目录只要 71.3 纳秒,快了 10 倍以上。

切换目录的系统调用比创建目录的系统调用耗时 更短。


 

6. 系统信息函数 Benchmark

获取系统信息的 Getpid 和 Getwd。


func BenchmarkGetpid(b *testing.B) {  for i := 0; i < b.N; i++ {    os.Getpid()  }}
func BenchmarkGetwd(b *testing.B) {  for i := 0; i < b.N; i++ {    os.Getwd()  }}

运行结果


BenchmarkGetpid-8     100000000          11.3 ns/opBenchmarkGetwd-8       3000000         463 ns/op

分析

Getpid 获取进程 ID 只要 11.3 纳秒,非常快。

Getwd 获取工作目录需要 463 纳秒,比获取 PID 慢 40 倍左右。


 

7. IO 利用率函数 Benchmark

Pipe 可以用来测试 IO 利用率


func BenchmarkPipe(b *testing.B) {  r, w, _ := os.Pipe()    b.ResetTimer()   for i := 0; i < b.N; i++ {    go func() {      w.Write([]byte("Hello"))      w.Close()    }()        buf := make([]byte, 5)    r.Read(buf)  }  }

运行结果


BenchmarkPipe-8        1000000        1138 ns/op

Pipe 连接管道的 IO 利用率测试结果是 1.138 微秒每次。


 

8. 命令行参数函数 Benchmark

最后看一下获取命令行参数的 Args 函数。


func BenchmarkArgs(b *testing.B) {  os.Args = []string{"test"}    b.ResetTimer()  for i := 0; i < b.N; i++ {    os.Args  }}

运行结果


BenchmarkArgs-8     50000000          27.2 ns/op

获取命令行参数 Args 非常快,只要 27.2 纳秒。


 

9. 总结

通过以上 Benchmark 测试,可以得出一些结论:

文件操作中,Open 和 Close 很快,Read 比 Write 性能好;

进程管理中,启动进程比等待进程快;

获取环境变量很快,设置环境变量较慢;

切换目录比创建目录快;

获取系统信息中,PID 比工作目录快;

IO 利用率 Pipe 连接约 1 微秒;

获取命令行参数极快,纳秒级。

目录
相关文章
|
6天前
|
Go
go语言中的数据类型
go语言中的数据类型
10 0
|
11天前
|
Go 开发者
掌握Go语言:Go语言结构体,精准封装数据,高效管理实体对象(22)
掌握Go语言:Go语言结构体,精准封装数据,高效管理实体对象(22)
|
11天前
|
安全 Go
掌握Go语言:Go语言通道,并发编程的利器与应用实例(20)
掌握Go语言:Go语言通道,并发编程的利器与应用实例(20)
|
11天前
|
存储 缓存 安全
掌握Go语言:Go语言中的字典魔法,高效数据检索与应用实例解析(18)
掌握Go语言:Go语言中的字典魔法,高效数据检索与应用实例解析(18)
|
11天前
|
Go
使用Go语言发邮件
使用Go语言发邮件
16 2
|
23天前
|
缓存 安全 Java
Go语言小细节
Go语言小细节
36 0
|
29天前
|
存储 安全 Go
|
29天前
|
安全 编译器 Go
Go语言中的int和int32:同一个概念吗?
【2月更文挑战第24天】
49 3
|
11天前
|
存储 安全 Go
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
|
5天前
|
存储 Java 编译器
go语言基础语法
go语言基础语法