Hacker News new | ask | show | jobs
by mcronce 1516 days ago
Serializing a request structure, making an IPC/network call, deserializing the request structure, serializing the response structure, sending it back, and deserializing it ... isn't really a solution when the purpose of an FFI call is typically to fix some performance issue.

Lots of garbage-collected languages make FFI not only easy but plenty fast. Go does neither.

1 comments

I started out thinking that fast and easy FFI was ideal and being disappointed that Go's FFI was neither. I've since changed my opinion as it's really nice that one can usually get away without pulling any C dependencies into their dependency tree. I wrote more in the sibling comment: https://news.ycombinator.com/item?id=31194347
That would be great if Go provided better performance. With its awful FFI, you have no recourse when you hit its limits other than to rewrite the entire codebase in something else.

As with many things, there's nothing stopping you from just sticking with pure Go if you don't like C toolchains. While C build issues are a valid theoretical concern, in practice I've never had any Python package fail to install because of a C dependency problem that wasn't trivially resolved, nor any Rust project fail to compile because of a C dependency problem at all.

> That would be great if Go provided better performance. With its awful FFI, you have no recourse when you hit its limits other than to rewrite the entire codebase in something else.

I wouldn't know. I've never run into an issue where Go's performance was a real bottleneck, and anyway every mainstream language with easy FFI still has significant FFI overhead (so much so that many programs actually run slower with FFI). This isn't really true for Rust (Rust makes it easy to define types which are essentially C structs and thus require little/no marshaling), but performance also isn't the reason you FFI out of Rust.

> As with many things, there's nothing stopping you from just sticking with pure Go if you don't like C toolchains.

Right, that's my point. You viably can stick with pure Go because such a large share of the Go ecosystem is pure because FFI is rarely worth the hassle.

> While C build issues are a valid theoretical concern, in practice I've never had any Python package fail to install because of a C dependency problem that wasn't trivially resolved

Try building a significant Python project on anything except a recent version of RHEL, Debian, MacOS, or Windows. For example, try getting your Python project running on something like a scratch Docker container. Or try packaging a Python package (which depends even transitively on a C library, especially one which isn't already packaged for Nix) with Nix.

> every mainstream language with easy FFI still has significant FFI overhead (so much so that many programs actually run slower with FFI). This isn't really true for Rust

This isn't true for almost any language to the extent it's true for Go, and for many compiled languages it isn't really true at all.

> Try building a significant Python project on anything except a recent version of RHEL, Debian, MacOS, or Windows. For example, try getting your Python project running on something like a scratch Docker container. Or try packaging a Python package (which depends even transitively on a C library, especially one which isn't already packaged for Nix) with Nix.

While these are legitimate theoretical problems, none of them are really problems in practice. Containers don't need to be scratch, and if you're building a Python project, you're already not running a scratch container, so the addition of FFI doesn't change that. Nix is not an environment I've ever seen a requirement to support, let alone had a requirement.

It really seems to me that you like Go and you like Go's design decisions, but "I like this" is not the same as "this is better than that". I'm not particularly interested in rehashing the same conversation over and over again.

> This isn't true for almost any language to the extent it's true for Go

Go's object structure is much closer to C's. In many cases, it's just casting pointers (e.g., you can convert a Go slice to a C array by taking a pointer to its first element and casting it to a pointer of the C element type, provided the element types are binary-compatible). This means fewer allocations than a language like Java or Python where you would have to allocate a new slice and chase pointers around the heap for every field in every element in the array. In my experience, most of Go's overhead comes from function call bookkeeping at the FFI boundary rather than marshaling data.

> for many compiled languages it isn't really true at all.

I'm pretty sure it's still true for many compiled languages (e.g., Java, Haskell, etc).

> While these are legitimate theoretical problems, none of them are really problems in practice.

They were problems for me in practice.

> Containers don't need to be scratch, and if you're building a Python project, you're already not running a scratch container, so the addition of FFI doesn't change that.

Containers don't need to be scratch, but they often need to be lightweight with low cold-start latencies (including pulling). Not having to pull in a whole distro is advantageous. And a pure-Python project certainly could run in a scratch container (i.e., a container with just the interpreter, the program, and the program's transitive dependencies).

> Nix is not an environment I've ever seen a requirement to support, let alone had a requirement.

"Reproducible builds" is the requirement. Our customers' security teams vetted our dependencies individually, and if a dependency changed we would have to have that dependency re-vetted.

> It really seems to me that you like Go and you like Go's design decisions, but "I like this" is not the same as "this is better than that".

Clearly, no one here is conflating those things. I'm arguing that Go has a particular strength, not that that strength is the only factor.

> I've since changed my opinion as it's really nice that one can usually get away without pulling any C dependencies into their dependency tree.

That trophy is owned entirely by the Java ecosystem. Thanks to that, once Loom arrives, basically the whole ecosystem will automagically become reactive-aware.