我想在Golang中编写控制器逻辑并处理Golang中的json和数据库,同时在C中使用数学处理模型。我认为调用C函数的开销必须与设置寄存器rcx,rdx,rsi,rdi,doin一样低fastcall和摆脱rax价值。但是我听说过CGO的开销很大
假设我有通用的fastcall x64 c函数int64 f(int64 a,b,c,d){return a+b+c+d}
如何在go testing.B
基准测试中从头开始调用它,以获得最高的潜在基准测试分数?
PS没有指针传递,没有技巧,只是对如何以最可靠的方式访问C接口感兴趣
在我看来,调用C函数的开销必须与设置寄存器rcx,rdx,rsi,rdi,进行一些快速调用并获取rax值一样低。但是我听说过cgo <...>的开销很大
您的意见是没有根据的。
从Go到C的调用具有明显的开销的原因是由于以下原因。
尽管语言没有任何要求,但由典型的编译器编译并作为常规进程在典型OS上运行的典型C程序在很大程度上依赖于OS来执行其运行时环境的某些方面。
所谓的最可见和最重要的方面是堆栈:内核负责在加载和初始化程序映像之后以及将执行转移到新生进程的代码入口点之前对其进行设置。
同样,另一个关键点是,尽管不是严格要求,但大多数C程序都依赖于OS本地线程来通过程序代码实现多个并发执行的流。
在C代码中执行的函数调用通常使用与操作系统和硬件实现的目标组合相同的ABI进行编译(当然,除非程序员已明确设法告诉编译器否则进行此操作-例如,标记特定的具有不同的调用约定)。
C没有自动方法来管理非堆栈内存(“堆”)。
这种管理通常是通过C系列的标准库功能完成的malloc(3)
。这些函数管理堆,并将通过它们分配的任何内存都视为“它们”(这很合逻辑)。
C不提供自动垃圾收集。
让我们回顾一下:一个用C编译的典型程序:使用OS提供的线程,并在这些线程中使用OS提供的堆栈。大多数情况下,函数调用遵循平台的ABI;堆内存由特殊的库代码管理;没有GC。
以上几点自然导致goroutines具有自己的堆栈,这些堆栈完全独立于操作系统为其线程提供的堆栈。
与C不同,这些堆栈是可增长和可重新分配的。
堆内存由Go运行时自动管理,并直接完成,因此不使用C stdlib。
Go具有GC,并且该GC是并发的,因为它与执行程序代码的goroutines完全同时运行。
让我们来回顾一下:goroutine具有自己的堆栈,使用与平台的ABI或C都不兼容的调用约定,并且可能在不同的操作系统线程上执行。
Go运行时直接管理堆内存,并具有完全并行的GC。
正如您现在应该看到的那样,运行Go和C代码的运行时环境的“世界”足够不同,以至于存在很大的“阻抗不匹配”,这在执行FFI时需要一定的网关连接,而成本不为零。
特别是,当Go代码将要调用C时,必须执行以下操作:
cgo
机器必须验证将被传递给目标C调用不包含指向围棋管理的其他内存块的任何记忆,递归这是为了让Go的GC继续工作的同时。正如您可能会看到的那样,不可避免的成本是很大的,而在某些CPU寄存器中放置值则是最微不足道的。
通常,有两种方法可以解决此问题:
很少调用C。
也就是说,如果每次对C的调用都执行大量的CPU密集型计算,则可以认为执行这些调用的开销与使这些调用执行的计算速度更快的收益相形见。
在汇编中编写关键功能。
Go允许直接在目标硬件平台的程序集中编写代码。
一种“技巧”可能会让您获得两全其美的效果,那就是利用大多数工业编译器的功能来输出其编译函数的汇编语言形式。因此,您可以使用C编译器提供的核心功能,例如自动矢量化(针对SSE)和积极的优化,然后抓住生成的所有内容并将其包装在薄薄的汇编层中,这基本上使生成的代码适应本机代码去的ABI。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句