Earlier quoted context omitted.
Your checklist is only covering the simplest case, direct ownership of small data structures. I'm not going to put a large array on the stack. I'm not going to pass unique_ptr (exclusive ownership) of every resource I allocate to every caller. I still need to decide between passing a copy, a unique_ptr, a reference, or a shared_ptr. When I design a data structure with interior pointers, I need to define some ownershi…
Not really irrelevant when the said GC language also does value types, e.g. // C# Span buffer = stackalloc byte[1024];
Taming Go’s memory usage, or how we avoided rewriting our client in Rust
171–180 of 231 posts
Re: Taming Go’s memory usage, or how we avoided rewriting our client in Rust
#172The big wins in this article, in what I believe was the order of impact: * They do raw packet reassembly using gopacket, and gopacket keeps TCP reassembly buffers that can grow without bound when you miss a TCP segment. They capped the buffers, and the huge 5G spikes went away. * They were reading whole buffers into memory before handing them off to YAML and JSON parsers. They passed readers instead. * They were usin…
Re: Taming Go’s memory usage, or how we avoided rewriting our client in Rust
#173Bit confused by this part of the article: > PRO-REWRITE: Rust has manual memory management, so we would avoid the problem of having to wrestle with a garbage collector because we would just deallocate unused memory ourselves, or more carefully be able to engineer the response to increased load. > ANTI-REWRITE: Rust has manual memory management, which means that whenever we’re writing code we’ll have to take the time…
Re: Taming Go’s memory usage, or how we avoided rewriting our client in Rust
#174The big wins in this article, in what I believe was the order of impact: * They do raw packet reassembly using gopacket, and gopacket keeps TCP reassembly buffers that can grow without bound when you miss a TCP segment. They capped the buffers, and the huge 5G spikes went away. * They were reading whole buffers into memory before handing them off to YAML and JSON parsers. They passed readers instead. * They were usin…
That is true. I do find however the explicitness of the Rust way of dealing with memory, whether it be lifetimes, who can and can't mutate it and who the memory belongs to, makes it much easier to reason about the right way of doing these things.
In C++ the same is often possible, but there is no way to have guarantees at the interfaces. Const is a promise that your function won't mutate something, it doesn't put any restrictions on the caller. Pass by reference doesn't guarantee that the reference will be kept alive.
Go (I guess with no experience there) probably has fewer footguns, but how explicit is memory management?
Re: Taming Go’s memory usage, or how we avoided rewriting our client in Rust
#175Earlier quoted context omitted.
> Buried in here are great examples of why rewrites don’t help That has not been my experience. Rewrites do sometimes help, because in a lot of codebases there’s too many “pet” modules or badly designed frozen interfaces. Rewrites can help in those situations, because there’s no sacred cows anymore. The issue is that a lot of people do rewrites as translations, without touching structures.
Agreed with this 100%. So many posts here over the years of examples of 'how we rewrote from x to y and saw 2000% gains', where x and y are languages. Such examples are 100% meaningless. Rewrites from the ground up -should- always be way faster, since it's all greenfield. If trying to make a language comparison, rewrite the entire thing in both languages!
I'm a huge fan of writing the first version of anything as an problem-exploration prototype, intended to be discarded and rewritten. As Fred Brooks said, "you're going to rewrite anyway, you might as well plan for it" [0]
[0] paraphrased from https://en.wikiquote.org/wiki/Fred_Brooks "The management question, therefore, is not whether to build a pilot system and throw it away. You will do that. […] Hence plan to throw one away; you will, anyhow."
Re: Taming Go’s memory usage, or how we avoided rewriting our client in Rust
#176Earlier quoted context omitted.
"developer velocity" is also, in some sense, a semantic question. I am special, of course, but basically, if you include things like "time fixing bugs that would have been prevented in Rust in the first place", my velocity is higher in Rust than in many GC'd languages I've used in the past. It just depends on so many factors it's impossible to say definitively one way or another.
I have trouble believing this, at least in any generalizable way. I'm comfortable in both Go and Rust at this point (my Rust has gotten better since last year when I was griping about it on HN), and it's simply the case that I have to think more carefully about things in Rust because Go takes care of them for me. It's not a "think more carefully and you're rewarded with a program that runs more reliably and so you ma…
To take two GC'd languages, I'm proficient in both Java and Scala. It usually takes me a little longer to write something in Scala, but when I'm done, I've almost certainly written fewer bugs in the Scala program than the Java program (I've also written many fewer lines of code, but that's another topic).
For me, it's the type system that helps the most. Given that Rust's type system is much stronger and expressive than Go's, I do expect to write fewer bugs in Rust than in Go. But it does feel like, if I had more experience with Go, I'd be significantly faster writing Go than Rust. (Then again, the more I write Rust, the fewer write-compile-fail-fix cycles I have to go through, and the compiler's ability to accept code as safe improves pretty frequently.)
Still, though (and I know this isn't the question at hand, but...), I personally value greater chances of correctness at compile time way more than development speed. While some types of bugs can be a fun adventure to track down and fix, most bugs I encounter are some mix of boring and annoying. I honestly would prefer to spend 2 weeks building and 2 days debugging over 1 week building and 1 week debugging. I really do find debugging that annoying. (Fuzzy numbers; I don't actually think I'd build 2x as fast in Go as Rust.)
Re: Taming Go’s memory usage, or how we avoided rewriting our client in Rust
#177Earlier quoted context omitted.
Not really irrelevant when the said GC language also does value types, e.g. // C# Span buffer = stackalloc byte[1024];
True, but even then you only have to decide between passing a copy of the value or a reference to it, no need to think about ownership.
So either do something like
using MyStructType something = new MyStructType ()
Or a more FP like stuff with myVar.WithXYZResource(res => { /* .... */ })
And then consider if it should be a ref struct, so that is only stack allocated.This from C# point of view, in something like D, there would be another set of considerations.
Still much easier than "in your face ownership management" though, yes.
Re: Taming Go’s memory usage, or how we avoided rewriting our client in Rust
#178After using Rust for a few years professionally it's my take that people that really want to use it haven't had much experience with it on real world projects. It just doesn't live up to the hype that surrounds it. The memory and CPU savings are negligible between Go and Rust in practice no matter what people might claim in theory. However, the side effects of making your team less productive by using Rust is a much…
Agreed. My organization has been a great testing ground for comparing Go vs Rust service development. The teams that spun up web services in Rust have almost uniformly have had poor experiences. In addition to Rust's steep learning curve, the relatively feature-poor standard library (you have to pull in a third party package to create a SHA256!), and instability of best practices/tools around service writing, in one…
nitpicking here, but this is by design - it's also true for datetimes and random numbers. it isn't a fault, it's a different packaging philosophy.
i agree with the rest - the good things about Rust just don't matter as much when developing bit-shoveling HTTP services, which is what 99% of backend seems to do nowadays.
Re: Taming Go’s memory usage, or how we avoided rewriting our client in Rust
#179The big wins in this article, in what I believe was the order of impact: * They do raw packet reassembly using gopacket, and gopacket keeps TCP reassembly buffers that can grow without bound when you miss a TCP segment. They capped the buffers, and the huge 5G spikes went away. * They were reading whole buffers into memory before handing them off to YAML and JSON parsers. They passed readers instead. * They were usin…
> They stopped compiling regexps on the fly and moved the regexps to package variables. (I actually don't know if this was a significant win; there might just be the three big wins.) Anecdotally, this could be a huge win, depending on how often it's called. A guy I was working with, new to Go, was writing a router config parser and asked why it was so slow. The first thing I did was moved regexp.Compile from a hot pa…
It would probably be a good idea to add performance hints like 'prefer to put static regular expressions in a package variable' in a linter or go vet.
Re: Taming Go’s memory usage, or how we avoided rewriting our client in Rust
#180The big wins in this article, in what I believe was the order of impact: * They do raw packet reassembly using gopacket, and gopacket keeps TCP reassembly buffers that can grow without bound when you miss a TCP segment. They capped the buffers, and the huge 5G spikes went away. * They were reading whole buffers into memory before handing them off to YAML and JSON parsers. They passed readers instead. * They were usin…
As to the issues you mentioned, there's a few 'adages' you could apply; "always use readers", "don't use reflect if you can help it", "move unchanging expressions to package level", etc.