What is Cgo

Cgo is an amazing technology that allows Go programs to interoperate with C libraries, which is a very useful feature.

Without it, Go wouldn't be where it is today. cgo is the key to running Go programs on Android and iOS.

overused

I personally think that cgo is overused in Go projects. When faced with reimplementing a large section of C language code in Go, programmers will choose to use cgo to wrap the library, thinking that this is an easier problem to solve. But I think this is a wrong choice.
Obviously, there are situations where cgo is unavoidable, most obviously where you have to interoperate with a graphics driver or window system, which is only available as a binary blob. In these scenarios, the use of cgo justifies its trade-offs, which are far less than many people are prepared to admit.
The following is an incomplete list of trade-offs that you may not be aware of when basing your Go project on the cgo library.
You need to think about this.

Build time becomes longer

When you import "C" in a Go package, go build needs to do more work to build your code.
Building your package is no longer simply a matter of passing a list of all .go files in scope to a single call to go tool compilation, but instead consists of the following work items:

The cgo tool needs to be called to generate C to Go and Go to C related codes.
The C compiler in the system makes a call for each C file in the package.
Individual compilation units are combined into one .o file.
The generated .o file will pass through the system's linker to correct the shared objects it references.

All of this work happens every time you compile or test your package, which is often the case if you're actively working on the package.
Go tools will parallelize this work where possible (including a complete rebuild of all C code), and the package's compilation time will increase and grow accordingly.
You also need to debug your C language code on each major platform to avoid compilation failures due to compatibility.

complex build

One of the goals of Go is to produce a language whose build process is self-describing; your program's source code contains enough information to allow a tool to build the project. That's not to say that using Makefiles to automate your build workflow is bad, but until cgo is introduced into your project, you probably won't need anything other than go tools to build and test.
After importing cgo, you need to set all the environment variables and keep track of shared objects and header files that may be installed in strange places.
Also note that Go supports many platforms, while cgo does not. So you have to spend some time to come up with a solution for your Windows users.
Now your users must install the C compiler, not just the Go compiler. They must also install the C language libraries that your project depends on, and you have to bear the cost of this technical support.

Cross-assembly is thrown out the window

Go's support for cross-compilation is best in class. Starting with Go 1.5, you can support cross-compiling from any platform to any other platform through the official installer on the Go project website.
By default, cgo is disabled when cross-compiling. Normally, if your project is pure Go, this isn't a problem.
When you mix in a dependency on a C library, you either give up on cross-compiling your code, or you have to invest time in finding and maintaining a cross-compiled C toolchain for all targets in order to achieve cross-compilation.
The number of platforms supported by Go continues to grow. Go 1.5 adds support for 64-bit ARM and PowerPC. Go 1.6 adds support for 64-bit MIPS, and IBM's s390 architecture is touted as Go 1.7. RISC-V is under development.
If your product depends on a C library, not only do you have all the issues with cross-compilation mentioned above, you also have to make sure that the C code you depend on works reliably on new platforms supported by Go – and you have to do that in a C/Go mix Do this with the limited debugging capabilities the language provides you.

You lose access to all tools

Go has great tools; we have race detector, pprof for analyzing code, coverage, fuzz testing and source code analysis tools. But none of these tools work in cgo (that is, there is no way to troubleshoot).
In contrast, great tools like valgrind don't understand Go's calling convention or stack layout. At this point, Ian Lance Taylor's work is integrating clang's memory sanitizer to debug dangling pointers on the C side, which will be of benefit to cgo users in Go 1.6.
The result of combining Go code and C code is the intersection, not the union, of two worlds; the memory safety of C and the debugability of Go programs. But the use of many core tools is lost.

Performance will always be an issue

C code and Go code live in two different worlds, cgo crosses the border between them, and this conversion is not free. And depending on where it lives in your code, the cost can be inconsequential or huge.
C knows nothing about Go's calling conventions or growable stacks, so a call to C code must log all details of the goroutine stack, switch to the C stack, and run the C code with knowledge of how it was called. , or the larger Go runtime responsible for the program knows nothing about it.
To be fair, Go doesn't know anything about the world of C either. This is why the rules for passing data between the two become more and more cumbersome over time, as the compiler gets better and better at spotting stack data that is no longer considered valid, and the garbage collector gets better and better at finding stack data that is no longer considered valid. Be good at doing the same with heaps.
If a failure occurs in the C world, the Go code must recover enough state to at least print a stack trace and exit the program cleanly, without exposing the core file information.
Managing this transition across the call stack, especially when signals, threads, and callbacks are involved, is not easy (Ian Lance Taylor also did a lot of work in Go 1.6 to improve signal handling interoperability with C ).
In the final analysis, it is not easy to convert between C language and Go language. Both of them are ignorant of each other, and there will be obvious performance overhead.

C calls the shots, not your code

It doesn't matter which language you write binding or wrapper C code in; Python, Java using JNI, some language using libFFI, or Go via cgo; this is the world of C, you just live in it.
Go code and C code must agree on how to share resources such as address space, signal handlers, and thread TLS slots – and by agreement, I really mean that Go must work around the assumptions of C code. C code may assume that it always runs on a single thread, or simply not be prepared to work in a multi-threaded environment.
You are not writing a Go program that uses the logic of a C library, you are writing a Go program that must coexist with mutually uncontrollable C code that is difficult to replace, has the upper hand in negotiations, and does not care about your question.

Deployment becomes more complex

Any Go talk to a general audience will include at least one slide with these words: Single, static binary.
This is one of Go's trump cards, making it the poster child for moving away from virtual machines and runtime management. With cgo, you give up this and give up an area of ​​Go's strength.
Depending on your environment, you might compile your Go project to deb or rpm, and assuming your other dependencies are packaged as well, add them as installation dependencies, pushing the issue to your operating system's package manager . But there are several significant changes to the previous straightforward build and deployment process like go build && scp.
It is possible to compile Go programs completely statically, but it is by no means trivial, which shows that the impact of adding cgo to a project ripples throughout the build and deployment lifecycle.

Wise choice

To be clear, I'm not saying you shouldn't use cgo. But before you make this design, consider carefully the many qualities of Go that you will be giving up.

You need to carefully consider the pros and cons and then think about whether it is worth doing it.

Guess you like

Origin blog.csdn.net/m0_73728511/article/details/133325726