由买买提看人间百态

boards

本页内容为未名空间相应帖子的节选和存档,一周内的贴子最多显示50字,超过一周显示500字 访问原贴
Programming版 - 分享:Go语言黑魔法(内存)
相关主题
java里run curl system command的问题什么是 multi-byte string?
做大项目的话,有两个语言features是杀手haskell在生产环境的生产力到底如何?
弱问bash script, 关于IFS问题inline functions in C++
[合集] C/C++ calling function by name两个面世题
要不还是搞俱乐部算了A question about class size
go 的坑(转载)C++ Q 108: swap
在C里面怎么验证一个input数字是不是超过int的范围?python question, easy one
STL map变量的实际memory usage估算老魏老姜老霸,我出银子给你们开机器
相关话题的讨论汇总
话题: go话题: string话题: 黑魔法话题: byte话题: 数据
进入Programming版参与讨论
1 (共1页)
d****n
发帖数: 1637
1
http://zhuanlan.zhihu.com/thought/20010926
达达 · 6 天前
今天我要教大家一些无用技能,也可以叫它奇技淫巧或者黑魔法。用得好可以提升性能
,用得不好就会招来恶魔,嘿嘿。
黑魔法导论
为了让大家在学习了基础黑魔法之后能有所悟,在必要的时候能创造出本文传授之外的
属于自己的魔法,这里需要先给大家打好基础。
学习Go语言黑魔法之前,需要先看清Go世界的本质,你才能获得像Neo一样的能力。
在Go语言中,Slice本质是什么呢?是一个reflect.SliceHeader结构体和这个结构体中
Data字段所指向的内存。String本质是什么呢?是一个reflect.StringHeader结构体和
这个结构体所指向的内存。
在Go语言中,指针的本质是什么呢?是unsafe.Pointer和uintptr。
当你清楚了它们的本质之后,你就可以随意的玩弄它们,嘿嘿嘿。
第一式 - 获得Slice和String的内存数据
让我小试身手,你有一个CGO接口要调用,需要你把一个字符串数据或者字节数组数据
从Go这边传递到C那边,比如像这个:mysql/conn.go at master · funny/mysql ·
GitHub
查了各种教程和文档,它们都告诉你要用C.GoString或C.GoBytes来转换数据。
但是,当你调用这两个函数的时候,发生了什么事情呢?这时候Go复制了一份数据,然
后再把新数据的地址传给C,因为Go不想冒任何风险。
你的C程序只是想一次性的用一下这些数据,也不得不做一次数据复制,这对于一个性
能癖来说是多麽可怕的一个事实!
这时候我们就需要一个黑魔法,来做到不拷贝数据又能把指针地址传递给C。
// returns &s[0], which is not allowed in go
func stringPointer(s string) unsafe.Pointer {
p := (*reflect.StringHeader)(unsafe.Pointer(&s))
return unsafe.Pointer(p.Data)
}
// returns &b[0], which is not allowed in go
func bytePointer(b []byte) unsafe.Pointer {
p := (*reflect.SliceHeader)(unsafe.Pointer(&b))
return unsafe.Pointer(p.Data)
}
以上就是黑魔法第一式,我们先去到Go字符串的指针,它本质上是一个*reflect.
StringHeader,但是Go告诉我们这是一个*string,我们告诉Go它同时也是一个unsafe.
Pointer,Go说好吧它是,于是你得到了unsafe.Pointer,接着你就躲过了Go的监视,
偷偷的把unsafe.Pointer转成了*reflect.StringHeader。
有了*reflect.StringHeader,你很快就取到了Data字段指向的内存地址,它就是Go保
护着不想给你看到的隐秘所在,你把这个地址偷偷告诉给了C,于是C就愉快的偷看了Go
的隐私。
第二式 - 把[]byte转成string
你肯定要笑,要把[]byte转成string还不简单?Go语言初学者都会的类型转换语法:[]
byte(str)。
但是你知道这么做的代价吗?既然我们能随意的玩弄SliceHeader和StringHeader,为
什么我们不能造个string给Go呢?Go的内部会不会就是这么做的呢?
先上个实验吧:
package labs28
import "testing"
import "unsafe"
func Test_ByteString(t *testing.T) {
var x = []byte("Hello World!")
var y = *(*string)(unsafe.Pointer(&x))
var z = string(x)
if y != z {
t.Fail()
}
}
func Benchmark_Normal(b *testing.B) {
var x = []byte("Hello World!")
for i := 0; i < b.N; i ++ {
_ = string(x)
}
}
func Benchmark_ByteString(b *testing.B) {
var x = []byte("Hello World!")
for i := 0; i < b.N; i ++ {
_ = *(*string)(unsafe.Pointer(&x))
}
}
这个实验先证明了我们可以用[]byte的数据造个string给Go。接着做了两组Benchmark
,分别测试了普通的类型转换和伪造string的效率。
结果如下:
$ go test -bench="."
PASS
Benchmark_Normal 20000000 63.4 ns/op
Benchmark_ByteString 2000000000 0.55 ns/op
ok github.com/idada/go-labs/labs28 2.486s
哟西,显然Go这次又为了稳定性做了些复制数据之类的事情了!这让性能癖怎么能忍受
1 (共1页)
进入Programming版参与讨论
相关主题
老魏老姜老霸,我出银子给你们开机器要不还是搞俱乐部算了
Pattern matchinggo 的坑(转载)
python 问题在C里面怎么验证一个input数字是不是超过int的范围?
[合集] c++的题STL map变量的实际memory usage估算
java里run curl system command的问题什么是 multi-byte string?
做大项目的话,有两个语言features是杀手haskell在生产环境的生产力到底如何?
弱问bash script, 关于IFS问题inline functions in C++
[合集] C/C++ calling function by name两个面世题
相关话题的讨论汇总
话题: go话题: string话题: 黑魔法话题: byte话题: 数据