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 微秒;

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

目录
相关文章
|
3天前
|
人工智能 Go 调度
掌握Go并发:Go语言并发编程深度解析
掌握Go并发:Go语言并发编程深度解析
|
19天前
|
Go
go语言中的数据类型
go语言中的数据类型
13 0
|
25天前
|
Go 开发者
掌握Go语言:Go语言结构体,精准封装数据,高效管理实体对象(22)
掌握Go语言:Go语言结构体,精准封装数据,高效管理实体对象(22)
|
6天前
|
数据采集 存储 Go
使用Go语言和chromedp库下载Instagram图片:简易指南
Go语言爬虫示例使用chromedp库下载Instagram图片,关键步骤包括设置代理IP、创建带代理的浏览器上下文及执行任务,如导航至用户页面、截图并存储图片。代码中新增`analyzeAndStoreImage`函数对图片进行分析和分类后存储。注意Instagram的反爬策略可能需要代码适时调整。
使用Go语言和chromedp库下载Instagram图片:简易指南
|
25天前
|
存储 安全 Go
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
|
2天前
|
Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第23天】Go语言的`context`包提供`Context`接口用于处理任务取消、超时和截止日期。通过传递`Context`对象,开发者能轻松实现复杂控制流。本文解析`context`包特性,讨论常见问题和解决方案,并给出代码示例。关键点包括:1) 确保将`Context`传递给所有相关任务;2) 根据需求选择适当的`Context`创建函数;3) 定期检查`Done()`通道以响应取消请求。正确使用`context`包能提升Go程序的控制流管理效率。
7 1
|
3天前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello(&quot;Alice&quot;)`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
13 1
|
3天前
|
SQL 关系型数据库 MySQL
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
|
4天前
|
Go 开发者
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
【4月更文挑战第21天】本文介绍了Go语言中的流程控制语句,包括`if`、`switch`和`for`循环。`if`语句支持简洁的语法和初始化语句,但需注意比较运算符的使用。`switch`语句提供多分支匹配,可省略`break`,同时支持不带表达式的形式。`for`循环有多种形式,如基本循环和`for-range`遍历,遍历时修改原集合可能导致未定义行为。理解并避免易错点能提高代码质量和稳定性。通过实践代码示例,可以更好地掌握Go语言的流程控制。
11 3
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
|
4天前
|
Go
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值
【4月更文挑战第21天】Go语言函数是代码组织的基本单元,用于封装可重用逻辑。本文介绍了函数定义(包括基本形式、命名、参数列表和多返回值)、调用以及匿名函数与闭包。在函数定义时,注意参数命名和注释,避免参数顺序混淆。在调用时,要检查并处理多返回值中的错误。理解闭包原理,小心处理外部变量引用,以提升代码质量和可维护性。通过实践和示例,能更好地掌握Go语言函数。
18 1
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值