9 ms·
Rustgo: Calling Rust from Go with near-zero overhead
- dm319 9y agoGo strives to find defaults that are good for its core use cases, and only accepts features that are fast enough to be enabled by default, in a constant and successful fight against knobs Made me chuckle.
- dilap 9y agoI loved that line too. "A constant and successful fight against knobs" really gets at a lot of what makes Go (often) a joy to use.
- masklinn 9y ago> But to be clear, rustgo is not a real thing that you should use in production. For example, I suspect I should be saving g before the jump, the stack size is completely arbitrary, and shrinking the trampoline frame like that will probably confuse the hell out of debuggers. Also, a panic in Rust might get weird. > To make it a real thing I'd start by calling morestack manually from a NOSPLIT assembly function to ensure we have enough goroutine stack space (instead of rolling back rsp) with a size obtained maybe from static analysis of the Rust function (instead of, well, made up). > It could all be analyzed, generated and built by some "rustgo" tool, instead of hardcoded in Makefiles and assembly files. Maybe define a Go target to teach Rust about the Go calling conventions? You may also want to use "xargo", which is specially built for stripping or customising "std" and to work with targets without binary stdlib support.
- valarauca1 9y agoThis is more complex then just a target. Rust has to communicate to the Go Runtime Environment. Two main points: -Go uses a very small stack for GoRoutines to make them dirt cheap. When you exceed this stack, Go maps in more stack for you transparently. Rust generated ASM is running on the Go Stack, but it is expecting when it exceeds its stack to explode, like a C program. As that should be the OS Stack. Larger problem then you think, Rust likes to put a ton of stuff on the stack. This is one of the nice things about Rust is putting _a ton_ of data on the stack is cheap, and makes ownership simpler. -Go's system calls, and concurrency primitives are cooperative with its runtime. When make they communicate the routine can yield to the runtime. Targeted Rust code would _also_ have to make these calls, as well as 3rd party crates. Again none of this is impossible, linker directives and FFI magic could import these functions/symbols. But this would also require Go have a stabilized runtime environment for other languages to link against. Currently just stating Go has a runtime is controversial, so I expect this won't happen soon.
- vvanders 9y agoThings may have changed from when I last looked at it but there's another issue which is pinning. If you ever want to do more than the most trivial FFI you'll eventually want to be able to pass types back and forth(usually opaque to either side). AFAIK Go doesn't offer any pinning if it's GC'd types so you can have the collector move them from under you. C# has this beautiful thing where you can pass a delegate as a raw C fn pointer. It makes building interop a wonderful thing but you have to make sure to pin/GCHandle it appropriately.
- FiloSottile 9y agoFolks, stop, you are reinventing cgo now. Defining a Go target for Rust actually makes sense in the context of replacing assembly (which has no runtime, GC or concurrency connotations), I was just too lazy to do it that way :)
- vvanders 9y agoI mean, kinda but not? Based on https://github.com/golang/go/issues/12416 https://github.com/golang/go/issues/12416 / https://golang.org/cmd/cgo/ https://golang.org/cmd/cgo/ : > However, C code may not keep a copy of the Go pointer after the call returns. It looks like they've just punted on the pinning issue and not allowed code that does it. Having written a ton of FFI code across a wide range of languages this type of restriction means that you're not going to be able to implement certain things which is unfortunate.
- valarauca1 9y ago> Folks, stop, you are reinventing cgo now. This is a good thing because `cgo` is really bad. No ALSR, always forking. These are completely _insane_ defaults, it manages to be slower then the JNI [1] which is an FFI from a completely managed stack based VM universe! Not a _compiled_ language. Somehow a compile language calling a static binary manages to be slower then a dynamic language's runtime calling a static binary... `cgo` isn't doing _anything right_. It is doing a lot of things wrong. [1] https://lwn.net/Articles/446701/ https://lwn.net/Articles/446701/
- uluyol 9y agoBuilding the rust code into a syso file might make the (user) build process easier here. This is used for the race detector (based on tsan) and there's an example of building and using one in the dev.boringcrypto branch. This would require a package author to create syso files for all GOOS, GOARCH combinations they care about. Although GOOS might not matter depending on whether any syscalls can be made from rust. https://go-review.googlesource.com/c/55472 https://go-review.googlesource.com/c/55472
- drej 9y agoThis is crazy. I love it.
- artursapek 9y agoIs rust actually faster than go? I had no idea.
- b4ux1t3 9y agoYou can tweak Go to make it as fast or faster, but, out of the box, the runtime is a bit slower. Building is faster, though, so that's nice.
- ice109 9y agowhat are the things that you can tweak?
- nemo1618 9y agoMost Go programs can benefit a lot from smarter memory management, which puts less strain on the GC and allows for certain optimizations. Things like refactoring your function to write into a pre-allocated buffer instead of returning a newly-allocated object. Or using a concrete type like `*os.File` instead of `io.Writer` so that the compiler can eliminate a heap allocation. You can also eliminate some slice bounds-checking by "asserting" the size of the slice outside the hot path; see http://www.tapirgames.com/blog/go-1.7-bce http://www.tapirgames.com/blog/go-1.7-bce Go also has a great profiler for discovering exactly which functions/statements are causing slowdown or allocating excessive memory. At the end of the day, though, I don't think most Go programs can be optimized to run as fast as their Rust equivalents (without dropping into asm, at least). There's just too much overhead that you aren't allowed to disable.
- b4ux1t3 9y ago> At the end of the day, though, I don't think most Go programs can be optimized to run as fast as their Rust equivalents (without dropping into asm, at least) To be clear, I meant programs that don't rely on things like concurrency or networking. For example, I think editing binary blobs (like images) would be just as fast as Rust, if I remember some of the research I did correctly. I don't have any sources on that, and it was a while back, so it's kinda irrelevant, I guess.
- danenania 9y agoI haven't tried Rust yet, but I've been building libraries in Ruby, Node, and Python that call into a shared Go core, and my experience has been that the best approach is to simply compile static executable binaries for every platform, then call out to them in each language via stdin/stdout. I tried cgo, .so files, and the like, bit this was a lot more trouble and had issues on both Windows and alpine-flavored linuxes. Is there some issue with this approach that I'm missing? Is the additional process overhead really enough that it's worth bending over backwards to avoid it?
- warent 9y agoThat sounds like it could be a nightmare for error handling! How do you work with that? And aren't a great many performance benefits lost when you restrict Go to stdout? I would think it would be better overall to just create a local http server in Go and use that instead. Or sockets if you're feeling up to it.
- danenania 9y agoThe executable has really simple output--it either works and outputs json, or doesn't and outputs nothing--so there's no difficulty with error handling. I guess I can understand wanting tighter integration for more complex scenarios though... an http server is an interesting idea, but could you run into issues with ports being restricted on production servers? I'm not seeing any performance issues with stdout, but I'm also not writing much data.
- tokenizerrr 9y ago> but could you run into issues with ports being restricted on production servers Sorry, what? Just make the port configurable?
- danenania 9y agoIt was a question (note the question mark)... I don't see the need for snark. Anyway, for my purposes, this wouldn't work, since the executable is embedded in libraries that are meant to run anywhere without any configuration. But yeah I could see that being fine under other circumstances I guess.
- kmicklas 9y agoIf you're already writing Rust, why would you even bother writing Go?
- tejasmanohar 9y agoIn my and many other opinions, Go is practically superior for the 90% case.
- legendiriz68 9y agoGo is probably one of the worst and most useless language invented for the last 20 years lol.
- kmicklas 9y agoGo deserves a lot of hate because they set out to create a language from scratch without any legacy baggage, but instead of learning from the mistakes of the past and consulting people who actually know a thing about programming languages, they repeated all the same mistakes and then some.
- schmichael 9y ago-
- ewillbefull 9y agoI think you misread the parent comment. :)
- Thaxll 9y agoThe same reason why Go is used to write many applications where Rust is not. - Fast compilation - Multi platform - Great tooling - Good std lib - Easy learning curve - Fast enough for most scenarios - Good concurrency model
- kmicklas 9y ago
- smegel 9y agoWhat has this got to do with Rust? Nothing. He could have called any C library and it would have been exactly the same. I am pretty sure there is a crypto library or two written in C. He is just writing a more direct manual version of CGo in assembly that bypasses a lot of what CGo does, to be much faster. > Before anyone tries to compare this to cgo The only meaningful message in this blog is it possible to write a faster CGo, that's it. Comparing it to CGo is the only useful possible outcome, but... > But to be clear, rustgo is not a real thing that you should use in production. For example, I suspect I should be saving g before the jump, the stack size is completely arbitrary, and shrinking the trampoline frame like that will probably confuse the hell out of debuggers. Also, a panic in Rust might get weird. So when you actually fix all those things you might be back where CGo was at the beginning. This guy comes across as a classic "but i wanna be cool" hacker who discovers that when you bypass all the normal protections in a library and make some kind of direct custom call, things can be faster. I guess so what?
- legendiriz68 9y agoOnly comment which actually realized what the article was about and...downvoted. Thanks HN-retards.
- ericfrederich 9y agoLanguage interop in 2017 is still pretty dismal. C is still the common denominator, you'd think it'd be easy, but it's hard. Years ago when LLVM was showing promise and Google was going to get Python running on top of it I was hopeful. I guess nowadays it's a better design to run separate processes and have your languages communicate out of process (pipes, http, etc) rather than in-process.