Why Discord is switching from Go to Rust

1,381 views
Skip to first unread message

Everton Marques

unread,
Feb 7, 2020, 12:24:50 PM2/7/20
to golang-nuts
I think Go is way better than Rust, but it is amusing to see why people pick one over another.

"Remarkably, we had only put very basic thought into optimization as the Rust version was written. Even with just basic optimization, Rust was able to outperform the hyper hand-tuned Go version. This is a huge testament to how easy it is to write efficient programs with Rust compared to the deep dive we had to do with Go."

https://blog.discordapp.com/why-discord-is-switching-from-go-to-rust-a190bbca2b1f

Lutz Horn

unread,
Feb 7, 2020, 4:13:45 PM2/7/20
to golang-nuts
> "Remarkably, we had only put very basic thought into optimization as the Rust version was written. Even with just basic optimization, Rust was able to outperform the hyper hand-tuned Go version. This is a huge testament to how easy it is to write efficient programs with Rust compared to the deep dive we had to do with Go."

This article does not tell use when the switch from Go to Rust was made. But it is interesting to see that they compare the ancient Go version 1.9 with bleeding endge Rust. I am no expert for GC in Go but I would strongly assume that since Go 1.9 much has been improved to handle situations like those described in the article.

Lutz

Amnon Baron Cohen

unread,
Feb 7, 2020, 5:35:57 PM2/7/20
to golang-nuts

Manlio Perillo

unread,
Feb 7, 2020, 6:24:01 PM2/7/20
to golang-nuts
On Friday, February 7, 2020 at 5:13:45 PM UTC+1, Lutz Horn wrote:
> "Remarkably, we had only put very basic thought into optimization as the Rust version was written. Even with just basic optimization, Rust was able to outperform the hyper hand-tuned Go version. This is a huge testament to how easy it is to write efficient programs with Rust compared to the deep dive we had to do with Go."

This article does not tell use when the switch from Go to Rust was made. But it is interesting to see that they compare the ancient Go version 1.9 with bleeding endge Rust. I am no expert for GC in Go but I would strongly assume that since Go 1.9 much has been improved to handle situations like those described in the article.


The article also does not tell us how many years of experience with Go they had when they started the project.
And how many years of experience with Rust they had when they started the reimplementation. 

 
Manlio

Tyler Compton

unread,
Feb 7, 2020, 6:40:19 PM2/7/20
to Everton Marques, golang-nuts
It would have been nice to see performance measurements from a more recent Go version. That said, it makes sense to me that Rust would be a better choice for this kind of application, where the main performance bottleneck is the sheer number of objects in memory. A language where you get to express more about the life-cycle of these objects should perform better. I say this as someone that knows very little about Rust, so I'm probably greatly oversimplifying.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/c3d9fc18-b750-48d7-b0b8-fd78afdbbf29%40googlegroups.com.

Marcin Romaszewicz

unread,
Feb 7, 2020, 6:57:51 PM2/7/20
to Tyler Compton, Everton Marques, golang-nuts
You're not oversimplifying. GC incurs a price, and that's performance. If you have a program which manages its own memory optimally for its usage pattern, it will be more efficient than a generic GC that has to handle all usage patterns. I use Go everywhere I can, since the tradeoff between programmer work and performance is phenomenal, but where latency or throughput really matter, it's still a compiled language without generic garbage collection. I'm sure there are many things that Rust will perform at better than Go, but that's not a statement about one language being in any way better than the other, just different.

Robert Engels

unread,
Feb 7, 2020, 7:44:11 PM2/7/20
to Marcin Romaszewicz, Tyler Compton, Everton Marques, golang-nuts
I didn’t look into the project but reading between the lines here I am betting that Java would perform as well as Rust in this case. This is an example of where not having  generational GC hurts badly - since it appears that most of their heap would be very long lived objects. 

On Feb 7, 2020, at 12:57 PM, Marcin Romaszewicz <mar...@gmail.com> wrote:



Marcin Romaszewicz

unread,
Feb 7, 2020, 9:29:20 PM2/7/20
to Robert Engels, Tyler Compton, Everton Marques, golang-nuts
On Fri, Feb 7, 2020 at 11:43 AM Robert Engels <ren...@ix.netcom.com> wrote:
I didn’t look into the project but reading between the lines here I am betting that Java would perform as well as Rust in this case. This is an example of where not having  generational GC hurts badly - since it appears that most of their heap would be very long lived objects. 


I've been running Go, Java, C++ and Python code in wide scale production for years, so have some background for comparison here. Java's garbage collector is not great either. Pretty much the biggest challenge of running Java services is constantly tuning JVM args to keep it from either taking down your machine, or sabotaging its own performance. It's a never ending battle. A non-GC language app can have a memory leak, but it's obvious, and it's a bug, while with Java's GC, and to a much lesser extent Go, you can have runaway memory usage which isn't necessarily the result of a bug, but a language behavior. Currently, I'm predominantly running services in Java, which interact with Hadoop, and Go, which make REST API's backed by databases and some limited interaction with Hadoop. With the Go services, most of our engineers time is spent writing the initial code and tests. With Java, most of their time is spent chasing Java memory allocation issues. I think the place the effort goes speaks volumes about how well the GC works in both languages.

Robert Engels

unread,
Feb 7, 2020, 10:12:22 PM2/7/20
to Marcin Romaszewicz, Tyler Compton, Everton Marques, golang-nuts
I think if you look into Zing or Shenandoah you’ll find a different experience. Literally zero tuning required.  

On Feb 7, 2020, at 3:29 PM, Marcin Romaszewicz <mar...@gmail.com> wrote:



Eric S. Raymond

unread,
Feb 8, 2020, 6:45:28 AM2/8/20
to Tyler Compton, Everton Marques, golang-nuts
Tyler Compton <xav...@gmail.com>:
> It would have been nice to see performance measurements from a more recent
> Go version. That said, it makes sense to me that Rust would be a better
> choice for this kind of application, where the main performance bottleneck
> is the sheer number of objects in memory. A language where you get to
> express more about the life-cycle of these objects should perform better. I
> say this as someone that knows very little about Rust, so I'm probably
> greatly oversimplifying.

I had a good hard swing at learning Rust, and I don't think you are
oversimplifying at all. It's a better fit for that deployment, no question,
and nothing in that article surprised me even a little.

On the other hand, for me to have tried to port reposurgeon to Rust
would have been a mind-numbingly stupid idea. And that will be true of
any application above a certain complexity of internal data-structure
management, where having GC moves from bein convenient to an essential tool
for holding dowb your defect rate.

Different jobs, different tools. Engineering is like that.
--
<a href="http://www.catb.org/~esr/">Eric S. Raymond</a>


Bakul Shah

unread,
Feb 8, 2020, 7:05:05 AM2/8/20
to e...@thyrsus.com, golang-nuts
> On Feb 7, 2020, at 10:44 PM, Eric S. Raymond <e...@thyrsus.com> wrote:
>
> On the other hand, for me to have tried to port reposurgeon to Rust
> would have been a mind-numbingly stupid idea. And that will be true of
> any application above a certain complexity of internal data-structure
> management, where having GC moves from bein convenient to an essential tool
> for holding dowb your defect rate.

Did you consider using nim? It is much closer to python. You could've even
selectively replaced python bits with nim bits based on profiling.

https://github.com/nim-lang/Nim/wiki/Nim-for-Python-Programmers

In the past I have "rewritten" programs by simply observing their
behavior and modeling in a toy program, taking care to keep the code
as simple as possible. This allows quick evolution and pretty soon
the toy can do most everything the original program does. Not really
surprising since the new program didn't have to make the mistakes
the old program made and has the old program as a reference. I found
literal translation to be much more painful.

Eric S. Raymond

unread,
Feb 8, 2020, 8:38:40 AM2/8/20
to Bakul Shah, golang-nuts
Bakul Shah <ba...@bitblocks.com>:
> Did you consider using nim? It is much closer to python. You could've even
> selectively replaced python bits with nim bits based on profiling.

I did, very briefly. At the time I had to make the decision - a little
over a year ago now - Nim did not seem quite well-established enough
yet for me to bet on.

I'm picky about that; as recently as three years ago, *Go* probably
wouldn't have passed that filter for me. And Rust still wouldn't
either, today, even if it were a better feature fit - the Rust people
haven't stabilized enough of a standard environment to prevent me from
feeling twitchy about forward compatibility, and they don't manifest enough
understanding that this is a problem.

I'm abandoning Python for Go at anything larger than small-shellscript
scale. This is probably a good time to for me to be explicit that my
main drivers for that move are a great deal like those I can deduce
from Google's economic/business context. I'm after better performance
and the option to exploit multiple CPUs, yes, but I also have a
top-line issue about minimizing my downstream costs by choosing a
language with low likelihood of breaking underneath me in N years.

Nim is clever and interesting but it has years of proving itself to do
before I'll lean heavily on it. Go, on the other hand...I'm an old
hand from the same culture the Go devs exemplify. The promises they
make about long-term stability are made from philosophical commitments
I understand and share. This makes up for the fact that in calendar
time Go would *still* not be quite seasoned enough for my taste if I
did not have confidence in their priorities.

stephen.i...@gmail.com

unread,
Feb 8, 2020, 4:01:05 PM2/8/20
to golang-nuts


On Saturday, 8 February 2020 08:38:40 UTC, Eric S. Raymond wrote:
 
 Go, on the other hand...I'm an old
hand from the same culture the Go devs exemplify.

I'm glad somebody else has said that. For me, Kernighan and Pike's involvement was the clinching argument in favour of Go. Coding philosophy/culture is important.
 

ffm...@web.de

unread,
Feb 9, 2020, 2:52:57 PM2/9/20
to golang-nuts
They have very strict latency requirements. So they can't use a language with a GC. That's fine, no language is totally universal. You have to pick the right tool for the job.

Aside from that it would be nice if that 2-minutes GC trigger, that is mentioned in the text, could be removed or lessened. Reminds me a bit of Oberon where the GC is triggered on every 20th mouse move. That was a smart "trick" back then, because Wirth and Gutknecht had only very little time to develop Oberon. Developing an algorithm that finds out when to trigger the GC is said to be very challenging and thus they saved the time for that and made the development of Oberon still possible (I would say Go is very much in a similar mindset than Oberon).

David Riley

unread,
Feb 9, 2020, 3:06:15 PM2/9/20
to Everton Marques, golang-nuts
On Feb 7, 2020, at 7:24 AM, Everton Marques <everton...@gmail.com> wrote:
>
> I think Go is way better than Rust, but it is amusing to see why people pick one over another.

I think they have very different use cases. Rust is fundamentally a functional language, which suits it quite well for things functional languages excel at (which definitely includes most server functionality). Go is a mostly-imperative language, which makes it excellent for general purpose (and it does pretty well at server-oriented stuff as well). Rust also has some concrete safety benefits over Go, largely down to the compile-time dataflow analysis that prevents a lot more hazards like data races, to which I've found Go is still quite susceptible.

There's been a lot of talk here about the effect of GC, and I think that's accurate, but it's worth noting that Go's GC is designed to make *initial* allocations very fast, which actually can give it a significant edge over many non-GC languages if they haven't come up with an allocator optimized for low latency (this is very popular in C/C++ game development because malloc() is not usually fast).

Correspondingly, the rather heavy requirements for the Go runtime make it a lot less practical for small embedded use cases (though not impossible, as recent list traffic indicates). If I were building a small (think <=256k RAM) embedded system these days, I'd probably go with Rust, largely because I've come to recognize that the undefined behavior in C/C++ makes writing definitively safe code impossible.

As always, use the tool appropriate for your use case, otherwise you get Perl's "when all you have is scissors, everything looks like a nail" problem. There are appropriate use cases for Forth. Language "quality" is a vector quantity, and trying to reduce it to a scalar does everyone involved a disservice.

That said, if I were to describe my ideal language for modern PCs and/or servers, it would probably be "Rust, but more stable, with channels and Goroutines", so I am somewhat biased. Maybe someone can make an LLVM target or something for the Go runtime that makes something like that possible, but I sure don't have the time.


- Dave

Robert Engels

unread,
Feb 9, 2020, 3:59:52 PM2/9/20
to David Riley, Everton Marques, golang-nuts
If you use any dynamic memory strict latency is very very difficult in any language/platform. I’m not really sure how a platform like Discord has strict latency requirements - it’s not as if the ‘read badge’ not updating is going to crash the plane.

As an example a lot of auto systems use Java - it can be done.

I’ll repeat... the problem they had with Go is due to the lack of a generational collector. They would not have that problem in today’s Java.

Please take Discord switching with a grain of salt - they didn’t really understand the cause of their problem in the first place - they will create others and wrongly switch to something else :)

> On Feb 9, 2020, at 9:06 AM, David Riley <frave...@gmail.com> wrote:
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/2A183438-E5DE-41DE-B464-78C2176F584D%40gmail.com.

Kevin Chadwick

unread,
Feb 10, 2020, 10:09:47 AM2/10/20
to golang-nuts
On 2020-02-09 15:05, David Riley wrote:
> Correspondingly, the rather heavy requirements for the Go runtime make it a lot less practical for small embedded use cases (though not impossible, as recent list traffic indicates). If I were building a small (think <=256k RAM) embedded system these days,

The bigger issues are

a) these systems have no MMU making memory fragmentation/OOM less predictable.
So a professional system will avoid dynamic memory completely. Actually Ada has
been able to do this arguably better than Rust for a long time. You could
probably do it in tinyGo/Go but then you certainly wouldn't be able to use
much/any of the standard library probably. Even in C you have to be careful with
some microchip vendors code.

b) Perhaps rust momentum will carry it through but the microchip dev
environments bundle c headers and compilers, peripheral registers in debuggers,
so I doubt it.

c) Sometimes there is no possibility of a security issue in a particular section
of code and energy performance is critical; C/assembly cannot be beaten in
performance.

> I'd probably go with Rust, largely because I've come to recognize that the undefined behavior in C/C++ makes writing definitively safe code impossible.

Which undefined behaviour do you mean? If you use unsigned (yes Google,
unsigned!) and understand the system and known portability isn't an issue then
I'm not sure what undefined behaviour you mean. Functions can be written that
avoid arrays being used out of bounds etc too.

It is perfectly possible to write safe C as long as you are willing to avoid
certain features.

Why you don't get safe functions by default and a safe version of C by default
is largely because elitists have historically required every feature of c left
in tact and compiler extensions have tried to permit all features rather than a
core subset. It is unfortunate but probably just an artefact of security being
historically widely considered unimportant. Linus: A bugs a bug!

One of the main drivers of Go was compiler time, how does rust compare there?

I don't know much about rust, hearing the learning curve was steep, but
development effort and readability may well make application level security
issues more likely?

It's a complex decision. Do I spend the time developing my IDE and perhaps hit a
dead end or an unexpected OOM that is beyond my understanding. Or spend the time
ensuring my C code is secure and know it will work well and read well?

p.s. Does anyone know how well Rust reads as this is highly important to me?

Kevin Chadwick

unread,
Feb 10, 2020, 10:10:37 AM2/10/20
to golang-nuts
On 2020-02-09 14:52, ffm...@web.de wrote:
>
> Aside from that it would be nice if that 2-minutes GC trigger, that is mentioned
> in the text, could be removed or lessened.

runtime.GC()?

Though I recall that they found an alternative from the text?

Brian Candler

unread,
Feb 10, 2020, 4:31:03 PM2/10/20
to golang-nuts
On Monday, 10 February 2020 10:09:47 UTC, Kevin Chadwick wrote:
p.s. Does anyone know how well Rust reads as this is highly important to me?

I have read Rust described as being in the spirit of "pragmatic Haskell".

Jake Montgomery

unread,
Feb 10, 2020, 5:02:45 PM2/10/20
to golang-nuts
runtime.GC() would not work for them. According to the docs - "GC runs a garbage collection and blocks the caller until the garbage collection is complete. It may also block the entire program." So that would be very counter productive in this case.
Message has been deleted

t hepudds

unread,
Feb 10, 2020, 5:46:31 PM2/10/20
to golang-nuts
Sorry, sending again after correcting a couple of typos. (That's what I get for trying to walk and chew gum at the same time or typing while distracted ;-)

They had said they were generating almost no garbage.

If that is true, I suspect they could have avoided the 2-minute forced GC by running with GOGC=off, or alternatively executed SetGCPercent(-1) (https://golang.org/pkg/runtime/debug/#SetGCPercent) to disable the GC programmatically after reaching steady-state.

Perhaps that would have worked for them. If needed, perhaps they could have also had a helper goroutine that ran, say, once an hour to run runtime.GC() manually, or once a day or whatever frequency might have seemed rational based on their workload.

I suspect that would have been sufficient as a simple workaround?

thepudds


On Monday, February 10, 2020 at 12:39:08 PM UTC-5, t hepudds wrote:
They had said they were generating almost no garbage.

If that is true, I suspect they could have avoided the 2-minute forced GC by running with GOGC=off, or alternatively executed SetGCPercent(-1) (https://golang.org/pkg/runtime/debug/#SetGCPercent) to disable the GC programmatically after reaching steady-state.

Perhaps what would have worked for them. If needed, perhaps they could have also had a helper goroutine that ran, say, once an hour to run runtime.GC() manually, or once a day or whatever frequency might have seemed rationale based on their workload.

I suspect that would have been sufficient as a simple workaround?

thepudds

Kevin Chadwick

unread,
Feb 10, 2020, 6:31:05 PM2/10/20
to golang-nuts
On 2020-02-10 16:31, Brian Candler wrote:
> p.s. Does anyone know how well Rust reads as this is highly important to me?
>
>
> I have read Rust described as being in the spirit of "pragmatic Haskell".
>
> https://jmmv.dev/2018/07/rust-vs-go.html
> https://jmmv.dev/series.html#Rust%20review

Thanks for the links. I didn't realise rust was so c++ like. I don't like c++ much

I also feel (whilst trying to consider my familiarity bias) like the following
written in Go, would be far more readable!

https://lib.rs/crates/unveil

I worry that if more complex code is harder to understand then implementation
bugs are unlikely to be found.

In Go, I check code like AES-SIV to see what is going on before using it. I
wonder if I would just blindly use crates, with Rust. Fairly sure now that it
would take me longer to evaluate it.

https://docs.rs/aes-siv/0.2.0/aes_siv/
https://docs.rs/aes-siv/0.2.0/src/aes_siv/siv.rs.html#1-310

Unveil came up very easily on Google though!
Reply all
Reply to author
Forward
0 new messages