在 Go 应用程序中使用 C(++) 提高性能

Using C(++) in a Go application for performance

几天前我开始学习 Go,并了解了它的 CGO 和 gccgo 编译器。根据我的理解,这允许 Go 程序使用 Go 编译器进行编译,并使用 C 编译器编译 C 库,并从 Go 程序内部引用这些库。这对我来说真的很有趣,因为这使我们能够以很少的开销从我们的主程序中利用 C 的性能(如果需要)。

但是我不确定那是多少,所以我在这里问:

在某些情况下,您会创建一个 C 库只是为了在您的 Go 应用程序中使用它吗?或者此功能只是为了促进现有 C 代码的可重用性?

P.S:我认为目前 CGO 不支持 C++,但是 post 这里有人能够使用 C 函数包装 C++ 代码并成功调用它们。

除非 C 库在一次调用中完成大量工作,否则我不会仅仅为了性能而编写 C 代码。我认为 cgo 仅用于与旧代码交互。

原因是cgo开销。目前对 cgo 函数的函数调用是 between 50 and 100 times slower than function calls to Go code。 cgo函数调用开销惊人

Cgo 相当慢,因为 Go 必须 mess with its runtime and calling conventions in certain ways to call C functions. The only place it's really worth it is cases where the compute time significantly dwarfs this cost。它类似于并行、分布式、GPU 等编程,尽管启动成本略低。

汇编要好得多,因为你可以编写使用 Go 调用约定的汇编,否则就像本地 Go 代码一样对待,但汇编的可移植性差得多,更难阅读,而且维护工作量更大。事实上,Go 标准库在 Plan 9 风格的汇编中编写了一些 mathbig 包。

Gonum 是这两者的一个例子。它对一些可以更快完成的功能使用通用汇编,但它也利用了 blas 和 lapack 引擎。它确实提供了一个 Go-blas 实现,但是 C-blas(通常最终是 Fortran-blas)更快,并且对于大型矩阵计算几乎总是比离开 Go 的成本相形见绌。

通常,您希望尽可能避免 cgo。仅在需要大量计算时间时才使用它,您需要与纯 Go 中 non-trivial 交互的东西进行交互,例如图形或音频驱动程序,或者访问 OpenCV 等公共库。即便如此,如果你真的关心性能,在可能的情况下实现某种 "call pooling" 可能是值得的,你可以从 Go 端安排多个调用并通过单个上下文切换到 C 一次执行它们.

编辑:对于 C++,存在一些重大问题。如果没有多层抽象,可能很难包装某些库(因为 cgo 无法正确处理包含的 C++ headers)。另外,带有析构函数的C++ 类 不能真正按值返回,必须在堆上分配。由于 Go 不允许资源的确定性终结,因此您 必须 提供显式释放内存的函数,并且 Go 用户必须记住释放资源。 (您可能会在名为 runtime.SetFinalizer 的文档中读到一个功能,但我不能说我见过任何人使用过它,并且文档本身带有一堆警告)

defer 等功能使它更易于管理,但它破坏了很多东西,例如 RAII,使现代 C++ 实践更安全。