习明昊*_*习明昊 12 c performance go cgo
我正在做一个测试:比较cgo和纯Go函数的执行时间各自运行1亿次.与Golang函数相比,cgo函数需要更长的时间,我对此结果感到困惑.我的测试代码是:
package main
import (
    "fmt"
    "time"
)
/*
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void show() {
}
*/
// #cgo LDFLAGS: -lstdc++
import "C"
//import "fmt"
func show() {
}
func main() {
    now := time.Now()
    for i := 0; i < 100000000; i = i + 1 {
        C.show()
    }
    end_time := time.Now()
    var dur_time time.Duration = end_time.Sub(now)
    var elapsed_min float64 = dur_time.Minutes()
    var elapsed_sec float64 = dur_time.Seconds()
    var elapsed_nano int64 = dur_time.Nanoseconds()
    fmt.Printf("cgo show function elasped %f minutes or \nelapsed %f seconds or \nelapsed %d nanoseconds\n",
        elapsed_min, elapsed_sec, elapsed_nano)
    now = time.Now()
    for i := 0; i < 100000000; i = i + 1 {
        show()
    }
    end_time = time.Now()
    dur_time = end_time.Sub(now)
    elapsed_min = dur_time.Minutes()
    elapsed_sec = dur_time.Seconds()
    elapsed_nano = dur_time.Nanoseconds()
    fmt.Printf("go show function elasped %f minutes or \nelapsed %f seconds or \nelapsed %d nanoseconds\n",
        elapsed_min, elapsed_sec, elapsed_nano)
    var input string
    fmt.Scanln(&input)
}
结果是:
cgo show function elasped 0.368096 minutes or 
elapsed 22.085756 seconds or 
elapsed 22085755775 nanoseconds
go show function elasped 0.000654 minutes or 
elapsed 0.039257 seconds or 
elapsed 39257120 nanoseconds
结果表明,调用C函数比Go函数慢.我的测试代码有问题吗?
我的系统是:mac OS X 10.9.4(13E28)
Jam*_*dge 31
正如您所发现的那样,通过CGo调用C/C++代码会产生相当高的开销.所以一般来说,你最好尽量减少你所做的CGo通话次数.对于上面的示例,不是在循环中重复调用CGo函数,而是将循环向下移动到C可能是有意义的.
Go运行时如何设置其线程有许多方面可以打破许多C代码的期望:
libpthread线程本地存储实现正确交互.出于这些原因,CGo选择了在使用传统堆栈设置的单独线程中运行C代码的安全方法.
如果您来自像Python这样的语言,在C中重写代码热点作为加速程序的一种方式并不罕见,那么您会感到失望.但与此同时,等效C和Go代码之间的性能差距要小得多.
一般来说,我保留了CGo来连接现有的库,可能还有小的C包装函数,可以减少我需要从Go调用的次数.
gav*_*avv 15
James的回答更新:似乎当前实现中没有线程切换.
在golang-nuts上看到这个帖子:
总会有一些开销.它比简单的函数调用更昂贵,但比上下文切换要便宜得多(agl记住了早期的实现; 我们在公开发布之前切断了线程切换).现在,费用基本上只需要进行完整的寄存器设置切换(没有内核参与).我猜它可以与十个函数调用相媲美.
另请参阅此答案链接"cgo is not Go"博客文章.
C对Go的调用约定或可增长的堆栈一无所知,因此调用C代码必须记录goroutine堆栈的所有细节,切换到C堆栈,并运行不知道如何调用它的C代码,或负责程序的较大的Go运行时.
因此,cgo具有开销,因为它执行堆栈切换,而不是线程切换.
它在调用C函数时保存并恢复所有寄存器,而在调用Go函数或汇编函数时则不需要它.
除此之外,cgo的调用约定禁止将Go指针直接传递给C代码,并且常用的解决方法是使用C.malloc,因此引入额外的分配.有关详情,请参阅此问题.