对 Go 程序进行可靠的性能测试对 Go 程序进行可靠的性能测试 Changkun Ou https://changkun.de/s/gobench/ Go 夜读系列 |talkgo.org|Talk Go|第 83 期 March 26, 2020 # Go 1.13 / 1.14 2020 © Changkun Ou · Go 夜读 · 对 Go 程序进行可靠的性能测试 主要内容 ● 可靠的测试环境 ● benchstat 例2: Benchmark 的正确性分析 ○ 例3: 其他的影响因素 ● 假设检验的原理 ● 局限与应对措施 ● 总结 2020 © Changkun Ou · Go 夜读 · 对 Go 程序进行可靠的性能测试 教科书式的性能测试方法论 3 在《Software Testing: Principles and Practices》一书中归纳的性能测试方法论: 1. 搜集需求 2 性能调优 7. 性能基准测试(Performance Benchmarking) 8. 向客户推荐合适的配置 可靠的测试环境 2020 © Changkun Ou · Go 夜读 · 对 Go 程序进行可靠的性能测试 什么是可靠的性能基准测试环境 5 影响测试环境的软硬件因素 ● 硬件:CPU 型号、温度、IO 等 ● 软件:操作系统版本、当前系统调度的负载等 指导思想 ● 单次0 码力 | 37 页 | 1.23 MB | 1 年前3
如何消除程序中的数据竞争-周光远如何消除程序中的数据竞争 周光远 华为 从一些问题说起 1 2 3 什么是数据竞争 Go语言中的数据竞争(data race): data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write. 数据竞争(data 数据竞争(data race):在程序中,多线程 (至少两个线程)并发访问同一个内存地址, 且至少其中一次访问是写操作。 data Thread1 Thread2 data goroutine1 goroutine2 从微观看数据竞争 时间上:多个并发的读写操作被观察到的顺序无法预知。 空间上:并发读写时观察到非预期的数据。 a:1 b:2 a:2 b:1 a:1 b:2 a:2 b:1 也被写入。 2. 在main中,不保证一定能观察到done被写入。 来源:https://golang.org/ref/mem 消除数据竞争的误区:基础数据类型的数据竞争 硬件对存在数据竞争的程序的行为不做任何保证。 对于int,float,bool,指针系列的数据类型,这些数据类型上出现的数据竞争意味着什么? 示意代码,复现需要增加额外处理。 这些数据类型的大小小于等于寄存器的宽度,并且go语言0 码力 | 30 页 | 1.92 MB | 1 年前3
Go 入门指南(The way to Go)前言 原文出处:https://github.com/Unknwon/the-way-to-go_ZH_CN 用更少的代码,更短的编译时间,创建运行更快的程序,享受更多 的乐趣 对于学习 Go 编程语言的爱好者来说,这本书无疑是最适合你的一本书籍,这里包含了当前最全面的学习 资源。本书通过对官方的在线文档、名人博客、书籍、相关文章以及演讲的资料收集和整理,并结合我自 身在软件工程、编程语 goroutine 和 channel 时有多少种不同的模 Go入门指南 - 1 - 本文档使用 看云 构建 式,如何在 Go 语言中使用谷歌 API,如何操作内存,如何在 Go 语言中进行程序测试和如何使用模板来 开发 Web 应用这些高级概念和技巧。 在本书的第一部分,我们将会讨论 Go 语言的起源(第 1 章),以及如何安装 Go 语言(第 2 章)和开发 环境(第 3 章)。 时都是极其荒谬的主意, 根本没有人在乎。C++ 向大型项目开发迈出了重要的第一步,带领我们走进这个广袤无垠的世界。很庆幸 Stroustrup 做了让 C++ 兼容 C 语言以能够让其编译 C 程序这个正确的决定。我们当时需要 C++ 的出 现。” “之后我们学到了更多。我们毫无疑问地接受了垃圾回收,异常处理和虚拟机这些当年人们认为只有疯子 才会想的东西。C++ 的复杂程度(新版的 C++0 码力 | 380 页 | 2.97 MB | 1 年前3
Go 入门指南(The way to Go)开发环境的基本要求 3.2 编辑器和集成开发环境 3.3 调试器 3.4 构建并运行 Go 程序 3.5 格式化代码 3.6 生成代码文档 3.7 其它工具 3.8 Go 性能说明 3.9 与其它语言进行交互 第4章:基本结构和基本数据类型 4.1 文件名、关键字与标识符 4.2 Go 程序的基本结构和要素 4.3 常量 4.4 变量 4.5 基本类型和运算符 4.6 字符串 go install 安装自定义包 9.8 自定义包的目录结构、go install 和 go test 9.9 通过 Git 打包和安装 9.10 Go 的外部包和项目 9.11 在 Go 程序中使用外部库 第10章:结构(struct)与方法(method) 10.1 结构体定义 10.2 使用工厂方法创建结构体实例 10.3 使用自定义包中的结构体 10.4 带标签的结构体 13.4 自定义包中的错误处理和 panicking 13.5 一种用闭包处理错误的模式 13.6 启动外部命令和程序 13.7 Go 中的单元测试和基准测试 13.8 测试的具体例子 13.9 用(测试数据)表驱动测试 13.10 性能调试:分析并优化 Go 程序 第 14 章 协程(goroutine)与通道(channel) 14.1 什么是协程 14.2 协程间的信道0 码力 | 466 页 | 4.44 MB | 1 年前3
Go Web编程GO环境配置 欢迎来到Go的世界,让我们开始探索吧! Go是一种新的语言,一种并发的、带垃圾回收的、快速编译的语言。它具有以下特点: 它可以在一台计算机上用几秒钟的时间编译一个大型的Go程序。 Go为软件构造提供了一种模型,它使依赖分析更加容易,且避免了大部分C风格include文件与库的开头。 Go是静态类型的语言,它的类型系统没有层级。因此用户不需要在定义类型之间的关系上花费时间,这样感 安装完之后,我们可以进入如下目录 sh cd $GOPATH/pkg/${GOOS}_${GOARCH} //可以看到如下文件 mymath.a 这个.a文件是应用包,那么我们如何进行调用呢? 接下来我们新建一个应用程序来调用 新建应用包mathapp sh cd $GOPATH/src mkdir mathapp cd mathapp vim main.go // $GOPATH/src/mathapp/main "fmt" ) func main() { fmt.Printf("Hello, world. Sqrt(2) = %v\n", mymath.Sqrt(2)) } 12 如何编译程序呢?进入该应用目录,然后执行`go build`,那么在该目录下面会生成一个mathapp的可执行文件 sh ./mathapp 输出如下内容 sh Hello, world. Sqrt(2) =0 码力 | 295 页 | 5.91 MB | 1 年前3
Hello 算法 1.0.0b1 Golang版=== File: time_complexity.go === /* 平方阶(冒泡排序) */ func bubbleSort(nums []int) int { count := 0 // 计数器 // 外循环:待排序元素数量为 n-1, n-2, ..., 1 for i := len(nums) - 1; i > 0; i-- { // 内循环:冒泡操作 for j := 0; j ‧「暂存数据」用于保存算法运行中的各种 常量、变量、对象 等。 ‧「栈帧空间」用于保存调用函数的上下文数据。系统每次调用函数都会在栈的顶部创建一个栈帧,函数返 回时,栈帧空间会被释放。 ‧「指令空间」用于保存编译后的程序指令,在实际统计中一般忽略不计。 2. 复杂度分析 hello‑algo.com 28 Figure 2‑9. 算法使用的相关空间 /* 结构体 */ type node struct { (1) ;但是当 ? > 10 时,初始化的数组 nums 使 用 ?(?) 空间;因此最差空间复杂度为 ?(?) ; ‧ 以算法运行过程中的峰值内存为准。程序在执行最后一行之前,使用 ?(1) 空间;当初始化数组 nums 时, 程序使用 ?(?) 空间;因此最差空间复杂度为 ?(?) ; func algorithm(n int) { a := 0 // O(1) b := make([]int0 码力 | 190 页 | 14.71 MB | 1 年前3
Hello 算法 1.0.0b2 Golang版=== File: time_complexity.go === /* 平方阶(冒泡排序) */ func bubbleSort(nums []int) int { count := 0 // 计数器 // 外循环:待排序元素数量为 n-1, n-2, ..., 1 for i := len(nums) - 1; i > 0; i-- { // 内循环:冒泡操作 for j := 0; j ‧「暂存数据」用于保存算法运行中的各种 常量、变量、对象 等。 ‧「栈帧空间」用于保存调用函数的上下文数据。系统每次调用函数都会在栈的顶部创建一个栈帧,函数返 回时,栈帧空间会被释放。 ‧「指令空间」用于保存编译后的程序指令,在实际统计中一般忽略不计。 2. 复杂度分析 hello‑algo.com 28 Figure 2‑9. 算法使用的相关空间 /* 结构体 */ type node struct { (1) ;但是当 ? > 10 时,初始化的数组 nums 使 用 ?(?) 空间;因此最差空间复杂度为 ?(?) ; ‧ 以算法运行过程中的峰值内存为准。程序在执行最后一行之前,使用 ?(1) 空间;当初始化数组 nums 时, 程序使用 ?(?) 空间;因此最差空间复杂度为 ?(?) ; func algorithm(n int) { a := 0 // O(1) b := make([]int0 码力 | 202 页 | 15.73 MB | 1 年前3
Hello 算法 1.0.0b4 Golang版=== File: time_complexity.go === /* 平方阶(冒泡排序) */ func bubbleSort(nums []int) int { count := 0 // 计数器 // 外循环:未排序区间为 [0, i] for i := len(nums) - 1; i > 0; i-- { // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 对象等。 ‧「栈帧空间」用于保存调用函数的上下文数据。系统在每次调用函数时都会在栈顶部创建一个栈帧,函 数返回后,栈帧空间会被释放。 ‧「指令空间」用于保存编译后的程序指令,在实际统计中通常忽略不计。 因此,在分析一段程序的空间复杂度时,我们一般统计 暂存数据、输出数据、栈帧空间 三部分。 Figure 2‑9. 算法使用的相关空间 2. 复杂度 hello‑algo.com 29 /* (1) ;但当 ? > 10 时,初始化的数组 nums 占 用 ?(?) 空间;因此最差空间复杂度为 ?(?) 。 ‧ 以算法运行过程中的峰值内存为准。例如,程序在执行最后一行之前,占用 ?(1) 空间;当初始化数组 nums 时,程序占用 ?(?) 空间;因此最差空间复杂度为 ?(?) 。 func algorithm(n int) { a := 0 // O(1) b := make([]int0 码力 | 347 页 | 27.40 MB | 1 年前3
Hello 算法 1.1.0 Go版在算法中,重复执行某个任务是很常见的,它与复杂度分析息息相关。因此,在介绍时间复杂度和空间复杂 度之前,我们先来了解如何在程序中实现重复执行任务,即两种基本的程序控制结构:迭代、递归。 2.2.1 迭代 迭代(iteration)是一种重复执行某个任务的控制结构。在迭代中,程序会在满足一定的条件下重复执行某段 代码,直到这个条件不再满足。 1. for 循环 for 循环是最常见的迭代形式之一,适合在预先知道迭代次数时使用。 成正比,或者说成“线性关系”。实际上,时间复杂度描述的就是 这个“线性关系”。相关内容将会在下一节中详细介绍。 2. while 循环 与 for 循环类似,while 循环也是一种实现迭代的方法。在 while 循环中,程序每轮都会先检查条件,如果条 件为真,则继续执行,否则就结束循环。 下面我们用 while 循环来实现求和 1 + 2 + ⋯ + ? : // === File: iteration.go === 关系”,以此类推。 2.2.2 递归 递归(recursion)是一种算法策略,通过函数调用自身来解决问题。它主要包含两个阶段。 1. 递:程序不断深入地调用自身,通常传入更小或更简化的参数,直到达到“终止条件”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。0 码力 | 383 页 | 18.48 MB | 1 年前3
Hello 算法 1.0.0 Golang版在算法中,重复执行某个任务是很常见的,它与复杂度分析息息相关。因此,在介绍时间复杂度和空间复杂 度之前,我们先来了解如何在程序中实现重复执行任务,即两种基本的程序控制结构:迭代、递归。 2.2.1 迭代 「迭代 iteration」是一种重复执行某个任务的控制结构。在迭代中,程序会在满足一定的条件下重复执行某 段代码,直到这个条件不再满足。 1. for 循环 for 循环是最常见的迭代形式之一,适合在预先知道迭代次数时使用。 成正比,或者说成“线性关系”。实际上,时间复杂度描述的就是 这个“线性关系”。相关内容将会在下一节中详细介绍。 2. while 循环 与 for 循环类似,while 循环也是一种实现迭代的方法。在 while 循环中,程序每轮都会先检查条件,如果条 件为真,则继续执行,否则就结束循环。 下面我们用 while 循环来实现求和 1 + 2 + ⋯ + ? : // === File: iteration.go === 关系”,以此类推。 2.2.2 递归 「递归 recursion」是一种算法策略,通过函数调用自身来解决问题。它主要包含两个阶段。 1. 递:程序不断深入地调用自身,通常传入更小或更简化的参数,直到达到“终止条件”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。0 码力 | 382 页 | 17.60 MB | 1 年前3
共 50 条
- 1
- 2
- 3
- 4
- 5













