转载

让 cgo 调用可以绕过线程池

go 语言中,可以通过 cgo 来调用 C 库。但是由于 goroutine 的机制,外部的 C 函数调用可能能够很快返回,也可能执行很长时间。为了 goroutine 调度不被阻塞,就一律对每个 cgo 调用都从线程池中取一个线程来执行,完成后再返回原 goroutine。这样一来,每个 cgo 调用都带来了巨大的额外开销。所以 go 的很多库在实现时,都没有通过包装 C 库,而是选择完全用 go 来实现。这就使得 go 少了大量现有的 C 库可以利用。

读过 go 的代码后发现,要让 cgo 调用不通过线程池调用其实并不算很麻烦,所以就自己修改了一下 cgo 命令。如下面的代码中

package cgo  // // int add(int a, int b) { //   int ret = a + b; //   return ret; // } // import "C"  func CAdd(a, b int) int {   return int(C.add(C.int(a), C.int(b))) }  func AsmCAdd(a, b int) int {   return int(c.add(C.int(a), C.int(b))) }

C.add 是传统的 cgo 调用方式。c.add 则是修改后不经过线程池的方式。两者可以并存,程序员可以自己判断 C 函数的执行时间,来考虑使用哪种方式。

性能测试代码:

import "testing"  func BenchmarkNormal(b *testing.B) {   for i:= 0; i < b.N; i++ {     CAdd(i, i);   } }  func BenchmarkDirect(b *testing.B) {   for i:= 0; i < b.N; i++ {     AsmCAdd(i, i);   } }

测试结果:

testing: warning: no tests to run PASS BenchmarkNormal 5000000       307 ns/op BenchmarkDirect 50000000        31.0 ns/op ok   cgo 3.437s

可以看出,直接调用的性能大约是传统调用方式的 10 倍。

虽然目前的实现不算很漂亮,但是这玩意给了 go 更强的能力。而且只修改了 cgo 工具,并没有影响 go runtime 和标准库,不破坏兼容性。

代码在此,基于 go 1.4 修改。

https://github.com/xiezhenye/go/tree/directly-cgo/src/cmd/cgo

go nut 帖子 https://groups.google.com/forum/#!topic/golang-nuts/B0cOaADkXy4

正文到此结束
Loading...