注:本文根据几篇网文和 GitHub 资源综合编译,链接在文末给出。
本文主要介绍:
2.3.4p301
go1.9.2 darwin/amd64
共享库和静态库都属于库文件,可以理解为编译好的程序,但是自己没有执行入口,供其它程序调用来执行。
可参考:http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
作为类型语言的 Go 对于 Ruby 的优势更多的在 CPU 密集计算方面,所以我们先来比较一下 Ruby 和 Go 实现的 fibonacci 函数的效率。为了简化比较的考虑,我们采用同样的算法。
# fib.ruby
def fib(n)
return n if n <= 1
fib(n - 1) + fib(n - 2)
end
puts fib(40)
// fib.go
package main
import "fmt"
func fib(n uint) uint {
if n <= 1 {
return n
}
return fib(n-1) + fib(n-2)
}
func main() {
fmt.Println(fib(40))
}
Ruby 的用时:
$ time ruby fib.rb
102334155
real 0m14.027s
user 0m13.827s
sys 0m0.067s
直接运行 go run
执行的用时:
$ time go run fib.go
102334155
real 0m1.691s
user 0m0.957s
sys 0m0.362s
可以看到 Go 编译 + 运行的时间也要 10 倍于 Ruby 的速度。下面是直接执行 Go 二进制程序的速度(直接执行使用 go build fib.go
编译生成的 fib
二进制文件,相比 go run 省去了编译时间):
$ time ./fib
102334155
real 0m0.753s
user 0m0.741s
sys 0m0.007s
差不多是 Ruby 程序的 18 倍。可以看到执行效率的差距还是很大的。
使用 Go 编写共享库和写普通的 Go 程序差别不大,主要是在代码里 import 一个名为 "C" 的伪包,然后在执行 go build 编译的时候加上 -buildmode=c-shared
参数。这样在编译的时候会自动调用 Cgo 进行相应的操作。有关 Cgo 的一些细节,可以阅读 C? Go? Cgo!。
我们先来写一下作为共享库的 fibonacci 函数:
// fib_lib.go
package main
import "C"
//export fib
func fib(n uint) uint {
if n <= 1 {
return n
}
return fib(n-1) + fib(n-2)
}
func main() {}
注:可调用的函数是通过添加注释 //export fib
来实现的,因此这行注释是必须的。
然后我们来编译生成共享库,因为我是在 macOS 上执行,macOS 和 Linux 生成的命令略有不同,分别是:
# macOS
go build -buildmode=c-shared -ldflags -s -o fib.dylib fib_lib.go
# Linux
go build -buildmode=c-shared -o fib.so fib_lib.go
我们使用 FFI 这个 gem 提供的接口来从 Ruby 调用外部函数。有关 Foreign Function Interface 和 FFI Gem 可以点击相应链接了解。我们先来安装 FFI Gem:
gem install ffi
然后我们使用 Ruby FFI 来调用我们用 Go 编写的 fib 函数:
# go-fib.rb
require "ffi"
module Fib
extend FFI::Library
# 下面是我在 macOS 上运行的写法,如果在 Linux 上调用的文件为 "fib.so"
ffi_lib "fib.dylib"
attach_function :fib, [:uint], :uint
end
puts Fib.fib(40)
这里需要关注的是 attach_function
这个函数的参数,分别是要从外部引用的 函数的名称
, 该函数的参数类型的数组
以及 该函数的返回值类型
。这个可以和我们写的 Go 的函数对应起来:
func fib(n uint) uint
:fib, [:uint], :uint
Ruby 的类型和外部函数的类型对照可以参考 FFI Gem 的 wiki。
接下来我们比较一下 Ruby 原生的 fibonacci 和通过 FFI 调用 Go 共享库的运行效率:
$ time ruby fib.rb
102334155
real 0m13.673s
user 0m13.600s
sys 0m0.042s
$ time ruby go_lib.rb
102334155
real 0m0.883s
user 0m0.843s
sys 0m0.032s
可以看出差距也在 15 倍左右。
本文主要翻译自 1,同时参考了 2,3。
勉为编译,不妥之处还请多多指教。