On Go, Portability, and System Interfaces (2015)
garrett.damore.org
garrett.damore.org
It's not just "historical precedents in the Go community" it's "ongoing in Go itself": Go's developers will try as hard and as long as they can to perform raw syscalls even on platforms where it's not officially supported, and even on the one platform where it is genuinely actually supported they'll do raw vDSO calls (which are not) which predictably blows up (https://marcan.st/2017/12/debugging-an-evil-go-runtime-bug/).
[0] which is pretty much all of them aside from Linux, Golang 1.11 finally stops performing raw syscalls on OSX
It's also for performance reasons. Calling a C function from Go is actually quite complicated and can involve copying a lot of data. Using a syscall directly avoids that cost.
Tasking switching an the related MMU updates is slower. As you have to perform a kernel interrupt handling action just to start initializing the task switch.
I mean, I get that across the lifetime of an application, you might see that 1% of your cost is in this somehow, but then the easier response would almost certainly be to do less syscalls, since if this is taking up 1% then the syscalls are taking up 10%+? (And that is assuming this is 10% of the syscall. Ish)
Quoting the author of the article,
"That said, this is nothing compared to the cost of a SYSTEM CALL. System calls are measured in usec, usually 10s of them, depending on the complexity of the system call you're calling. (In some cases, such as disk reads, it can even go to milliseconds.) I'd guess on average the cost of the transition is less than 1% of the system call you're making."
Pre-KPTI, the cost of a system call might have been hundreds of cycles total. On a modern chip, that’s ~50ns.
Add kernel code for relevant operation. A read on a pipe might be 500ns total. A read on a cached file would be similar.
Add a few hundred cycles for KPTI.
A typically function call will be tens of cycles, but with lots of copies and stack changes can easily be hundreds.
How about people just go out to solve their problems, in their own platforms, and don't care to support all platforms?
It's not like most of those Go projects are big in the first place, and one or a couple of contributors don't have the means to test other platforms much, or the time to write it platform-agnostically.
I found it quite refreshing that the “install” process was copying one relatively small executable.
That being said, Terraform is quite popular and I would think that it’s used by Windows admins/devops locally.
They left up to the community to build up Windows support.
Certain packages are still not available on Windows, like plugins for example.
Easy to find that out searching early gonuts posts.
If the Go culture doesn't favor writing portable code, that's absolutely a problem with Go's culture.
Simple example: GNU/Hurd’s glibc works quite differently.
I've heard various stories about it over the years, so i would appreciate an authoritative source.
I don't have an authoritative source, but I think one of the big downsides of CGO for them is that it hurts their cross-compilation story. Cross-compiling a pure Go program is trivial, which is great, but by default it disables CGO.
why the Go standard library doesn't go use libc
They do you use a `libc` they use `musl-libc` [1] as apposed to `gnu-libc` (gblic) [2]. There are some differences [3], the largest (without getting into various API incompatibilities) is licensing. `musl-libc` is BSD licensed, while `gnu-libc` (glibc) is GPL licensed.The parent post does explain, about as well as any member of the go-team has to date for their usage of `musl-libc` over `gnu-libc` (glibc). Overall `musl-libc` does focus on correctness a lot more then `glibc`, and static linking is a large benefit.
Static linking isn't an issue when you expect applications to be short lived transient items. Where system images only exist for days, or hours before being redeployed by either patches, or CI-Managered triggered updates. I think this is the largest disconnect. Go expects the development pipeline to be quick so nobody expects multi-year lifespan binaries, which is directly contradictory to how many expect executable binaries to behave.
No that I'm authority on this, just stating a common disconnect I've experienced working with ex-Googlers.
[1] https://www.musl-libc.org/
[2] https://www.gnu.org/software/libc/
[3] https://wiki.musl-libc.org/functional-differences-from-glibc...
No. Historically they don't use a libc at all if you don't use the DNS subsystem. The latest version (1.11) finally started using libSystem on OSX (where performing raw syscalls has never ever been supported, and Go broke several time because they refused to acknowledge that).
Are you running the build inside a docker container which uses musl, such as alpine?
Those are the cases where this might happen.
On a normal linux distro, this will not happen.
They only default to the system's libc (which is typically gnu libc, though could be musl) if certain networking constructs are used. They otherwise default to not using any external libc library, but rather implementing one with syscalls internally.
The above is for linux. On other platforms it varies further, but to my knowledge the parent post is not right on any platform.
The authors want a world where you can just make a read syscall without using FFI.
It's possible this is a unrealistic expectation. Linux kernel interfaces are generally quite stable. On OSX and windows the kernel interface seems to be an undocumented unsupported API which is sad.