函数调用栈可以算是走向秃头程序员的必备知识了,这篇文章并没有讲的很全,只是说明我为什么会遇到调用栈这个东西,只根据我的案例做一些思考,看过的具体文章我会放在最后
为啥会想到函数调用栈这个问题呢?看下面两个函数
// 提一点课外的,例如,MD5,SHA1 这种 hash 函数的使用,直接
// xxx.Sum() 就好,我开始想到了既然 hash 函数要复用,那我
// 写一个全局变量(xxx.New())用不就好了?结果发现实际并不
// 是的,如果有不知道的可以看一下源码,xxx.Sum() 内部做了
// 定义变量,初始化,写入,计算 这几步;而 New 的全局对象
// 在具体使用时也和上面是一样的效果(内存并没有少用多少,
// 结构体的 Sum 方法会进行自身拷贝),而且会有并发问题
// 观察源码就会注意到的,下面也进行了说明
func MD5(data string) string {
tmp := md5.Sum([]byte(data))
return hex.EncodeToString(tmp[:])
}
// 好,已经解释了 tmp := md5.Sum([]byte(data))
// 这一句不存在什么优化的问题了
// 我开始思考优化问题的关键是下面的几句
// hex.EncodeToString(tmp[:])
// 这个函数内部做了 初始化切片,调用 Encode 方法,返回切片转换的字符串
func MD52(data string) string {
tmp := md5.Sum([]byte(data))
tmp2 := make([]byte, 32)
hex.Encode(tmp2, tmp[:])
return string(tmp2)
}
- 上面两个函数分别是优化前和优化后的,不知道你能不能看出哪里进行了优化?
- 当然,这个优化并不大,后面经测试也就 20ns 左右😂,但从底层原理来说确实优化了
- 下面是我对上面注释的一些解释
// 使用全局的 xxx.New() 对象存在的问题
// 我刚开始打算使用一个全局变量来做
// 发现是有并发问题而且使用全局变量根本没有节省空间
// 可能人家本来也不是这样用的,我非要这样想😂
import (
"crypto/md5"
"encoding/hex"
)
var h = md5.New()
func main() {
_ = MD5("hello,world!")
}
func MD5(data string) string {
// 并发问题
// 在 Write 写入后如果别的 goroutine 刚好调用了 Reset
// 不就炸了??
// 所以 xxx.New() 不是这么用的
// 不知道的注意一下吧,知道的当我没说 -_-
h.Reset()
h.Write([]byte(data))
// Sum 函数会对结构体进行拷贝,所以这样用达不到节省内存的目的
tmp := h.Sum(nil)
return hex.EncodeToString(tmp[:])
}
/*
// Sum 函数
func (d *digest) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
// 说的结构体拷贝指这里 !!!
d0 := *d
hash := d0.checkSum()
return append(in, hash[:]...)
}
*/
// =====================================================
// 为什么想到这里可能还可以优化???
// EncodeToString 返回给上级 MD5 函数一个 string
// 而 MD5 又返回给上级一个 string
// 我就想,这里是不是多了一次拷贝呢?
// 如果把 EncodeToString 里做的事调换到 MD5 函数里去做
// 不就少了一次返回了吗?
// 就去查函数调用栈这个知识点(还好以前听过哈哈😄)
// EncodeToString returns the hexadecimal encoding of src.
func EncodeToString(src []byte) string {
dst := make([]byte, EncodedLen(len(src)))
Encode(dst, src)
return string(dst)
// 第一次返回,发生拷贝
}
func MD5(data string) string {
h.Reset()
h.Write([]byte(data))
tmp := h.Sum(nil)
return hex.EncodeToString(tmp[:])
// 第二次返回,又发生拷贝
}
- 然后我去查看函数调用栈这个知识点相关的文章,验证是否和我预想的一致,会多发生一次拷贝(依赖于返回值是如何实现的)
- 经过查找,大致解释一下调用过程,只做简单举例哦
- main 函数中发生 函数调用,此时 main 函数就叫做 caller,被调函数叫做callee
- 函数实参是在 caller 栈帧里的,返回值是在 callee 栈帧中,返回是拷贝至caller 的本地变量中
- 函数实参是从右往左进入caller 栈帧的,返回值进入 callee 栈帧是从左往右,返回时拷贝到 caller 栈帧时就是从右往左了
- 所以简单画个图
- 不知道有没有用😂,就是说少一次函数调用就可以少一次拷贝
- 测试:
package hashs_test
import (
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"testing"
)
func MD5(data string) string {
tmp := md5.Sum([]byte(data))
return hex.EncodeToString(tmp[:])
}
func MD52(data string) string {
tmp := md5.Sum([]byte(data))
tmp2 := make([]byte, 32)
hex.Encode(tmp2, tmp[:])
return string(tmp2)
}
func SHA1(data string) string {
tmp := sha1.Sum([]byte(data))
return hex.EncodeToString(tmp[:])
}
func SHA12(data string) string {
tmp := sha1.Sum([]byte(data))
tmp2 := make([]byte, 40)
hex.Encode(tmp2, tmp[:])
return string(tmp2)
}
func BenchmarkMD5(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = MD5("hello,world")
}
}
func BenchmarkMD52(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = MD52("hello,world")
}
}
func BenchmarkSHA1(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = SHA1("hello,world")
}
}
func BenchmarkSHA12(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = SHA12("hello,world")
}
}
/*
// 多次测试都稳定在 182 ns/op 左右
goos: windows
goarch: amd64
pkg: tiku/hashs
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkMD5
BenchmarkMD5-12 6508209 183.0 ns/op
PASS
// 稳定在 162-164
goos: windows
goarch: amd64
pkg: tiku/hashs
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkMD52
BenchmarkMD52-12 7254024 163.3 ns/op
PASS
// 223 - 225
goos: windows
goarch: amd64
pkg: tiku/hashs
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkSHA1
BenchmarkSHA1-12 5296179 224.6 ns/op
PASS
// 200
goos: windows
goarch: amd64
pkg: tiku/hashs
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkSHA12
BenchmarkSHA12-12 5926756 200.9 ns/op
PASS
*/
真实场景中我们不需要注意这些细枝末节(也注意不到),因为函数的发明就是要我们用的,作为业务来用不需要考虑,当我们要做一个框架,需要很多复用时我们在去考虑减少拷贝次数等等优化方法
参考:
Go 函数调用━栈和寄存器视角
函数调用栈
转载:https://blog.csdn.net/li18434/article/details/115692588
查看评论