10

I've started studying Go a couple of days ago and came by its CGO thing and the gccgo compiler. From my understanding this allows a Go program to compile using the Go compiler and compile C libraries using a C compiler and reference those libraries from inside a Go program. This is really interesting to me because this allows us to leverage C's performance (if needed) from our main program with little overhead.

However I'm not sure how little that is, so I'm asking here:

Are there cases where you would create a C library just to use it from inside your Go application? Or is this feature only to facilitate re-usability of existing C code?

P.S: I think at the moment CGO doesn't support C++ but there was a post here of someone that was able to wrap C++ code using C functions and call them successfully.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
PentaKon
  • 4,139
  • 5
  • 43
  • 80
  • 1
    Most likely just for reusability. Almost every major library (not to mention OS) has a C binding, so having the ability to call C from your language is a big win for the language's adoption. – Dan Jun 22 '16 at 06:51

2 Answers2

13

Cgo is fairly slow, because Go has to 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. It's similar to parallel, distributed, GPU and so on programming, albeit with slightly lower startup costs.

Assembly is much better, because you can write assembly that uses Go's calling convention, and is otherwise treated like native Go code, but assembly is far less portable, harder to read, and more maintenance heavy. In fact, the Go standard library writes some of the math and big packages in Plan 9-style assembly.

Gonum is an example of both of these. It uses common assembly for some functions that can be done more quickly that way, but it also leverages blas and lapack engines. It does provide a Go-blas implementation, but C-blas (which is usually ultimately Fortran-blas) is faster, and for large matrix computations almost always dwarfs the cost of leaving Go.

Generally, you want to avoid cgo when possible. Only use it when significant computation time is needed, or you need to interact with things that would be non-trivial to interact with in pure Go, such as graphics or audio drivers, or accessing common libraries like OpenCV. Even then, if you really care about performance, where possible it may be worth it to implement some sort of "call pooling" where you can schedule multiple calls from the Go side and execute them all at once with a single context switch to C.

Edit: As for C++, there are some significant issues. It can be difficult to wrap certain libraries without several layers of abstraction (since cgo cannot handle included C++ headers properly). In addition, C++ classes with destructors can't really be returned by value and have to be allocated on the heap. Since Go doesn't allow for deterministic finalization of resources, you must provide a function to explicitly free memory, and the Go user must remember to free the resource. (There is a function you may read about in the documentation called runtime.SetFinalizer but I can't say I've ever seen anyone use it, and the documentation itself comes with a bunch of caveats)

Functionality such as defer makes this more manageable, but it ruins a lot of things like RAII which make modern C++ practices safer.

Linear
  • 21,074
  • 4
  • 59
  • 70
  • "Go has to suspend its runtime in certain ways to call C functions" -- is it documented/discussed somewhere? See also [my answer](http://stackoverflow.com/a/37962500/3169754) and [this thread](https://groups.google.com/forum/#!topic/golang-nuts/RTtMsgZi88Q). – gavv Jun 22 '16 at 08:31
  • I can't find any evidence that runtime is suspended. I'm probably missing something. – gavv Jun 22 '16 at 08:34
  • 1
    @g-v It's one of those things that constantly in flux with new Go versions. I think Go used to have to outright pause the garbage collector and other things. It looks like nowadays it mostly has to record a ton of metadata to accommodate the different calling convention: http://dave.cheney.net/2016/01/18/cgo-is-not-go Also, it can't interrupt and reschedule the goroutine on another thread while it's in C code, which is absolute can and does do, often for performance reasons, in GO code. – Linear Jun 22 '16 at 08:37
  • Thanks for the link. So there is a stack switch and inefficient data exchange, not a runtime suspension. Looks sensible. – gavv Jun 22 '16 at 08:46
  • "Also, it can't interrupt and reschedule the goroutine on another thread while it's in C code, which is absolute can and does do, often for performance reasons, in GO code" - Yes, this is quite obvious. – gavv Jun 22 '16 at 08:47
  • 1
    "because you can write assembly that Go can use without suspending its runtime" -- you probably would like to fix wording here too – gavv Jun 22 '16 at 08:58
  • @jsor: a cgo call takes an OS thread, which does not count against GOMAXPROCS, therefore doesn't block execution of any other goroutines. Besides the initial overhead of the stack switch in the cgo call, there is no performance penalty for executing C code. – JimB Jun 22 '16 at 18:02
1

Unless a C library does a lot of work in one call I wouldn't write C code just for performance. I consider cgo to only be for interfacing with old code.

The reason is the cgo overhead. Currently function calls into cgo functions are between 50 and 100 times slower than function calls to Go code. The overhead of cgo function calls is shocking.

Art
  • 19,807
  • 1
  • 34
  • 60