So, Perl is obviously a much better language. Except that it turns out it's a lot easier to write Perl itself (the interpreter) in C than to write it in Perl. So Perl is better, because it's better for "writing C", and C is better, because it's better for "writing Perl".
The irony is still funny, but it is clearly based on a feeling that language goodness is one-dimensional, even when we know that any comparison of hammer and saw on a single dimension is going to miss most of what matters.
The approach there was just to ingest a long book from Project Gutenberg, record all of the trigrams (including across word boundaries), and disqualify a candidate plaintext if it had more than some low number (3, but it was a short text) of unrecognized trigrams. This worked beautifully.
For reasons I don't recall, I wrote the assignment in C. I didn't want to do the parsing of the book into a trigram table in C. So I did that separately, and emitted C code to assign to a three-dimensional character array for every trigram I found. Then my assignment was the concatenation of some code initializing the array to zeros, the emitted code adding ones to it, and, thousands of lines later, the actual decryption code.
Small and short-lived class instances (more ideally structs) are the key to success with low-latency applications in most GC'd languages. If you are only touching gen0/1 in a .NET5 app, you will probably find the GC impact to UI latency to be negligible. In .NET, avoiding gen2 allocations is pretty easy. The biggest thing is replacing every byte[] with Stream as feasible, so you don't send objects directly to LOH (gen2). I believe the LOH threshold for byte[] is lower than the published limit of 85k, but I cannot recall the specifics (might be closer to 10k). Regardless, you really shouldn't be holding onto chunks of memory that large if you can ever avoid it. In some cases you can cheat. Pass around the path/id to a file and then stream it from disk at the very last possible moment (i.e. copy a FileStream direct to the HttpContext.Response.Body stream). If you absolutely must allocate large objects, try to reuse them as much as possible. Act as if anything entering gen2 is in memory forever, because with enough LOH fragmentation, it might as well be.
UI is one thing, but real-time process control is a wholly different animal. If you want to write a PID in a GC language, you better be prepared to eschew any notion of allocation in the process under collection. Even if you don't allocate, there are runtime components that still will, so you will almost always have some minimum amount of noise to contend with. That said, if you only need to run a PID loop at ~10khz (100uS loop delay), you probably wont have any issues. Just make sure you put the PID loop on a high-priority thread and care for its surroundings gingerly. A Raspi4+ is actually an amazing platform for this sort of thing.
Like C#, Go has value types, so controlling allocations should be pretty straightforward.
I could imagine plenty of other reasons to go with C++ though - there's still going to be a higher performance ceiling, maybe they plan to support languages other than Go, maybe they want a minimal file size compiling to WASM or to use specific C++ libraries etc. It would be interesting to see the original author's thinking behind it, on the website it's mainly comparing C++ to web technologies.
Lol.
" 30% of CPU cycles were being spent in function calls related to GC. " https://blog.twitch.tv/en/2019/04/10/go-memory-ballast-how-i...
You do have to watch out for the case where you can't use all the cores, and GC is blocking actual computation the user wants to perform.
(Also, you're at about 4000 cores when saving 30% buys you another developer. So you have to think about when the right time to unlock that money is, and how much it costs to unlock.)
I've been writing a game in Go. The GC doesn't prevent me from getting a smooth 360 frames per second. 1ms is a substantial pause, but isn't even a missed frame.
Especially considering that text editor state changes are dependent on user interaction—it’s not like a video games where the state changes continuously.
I'd wager the biggest hurdle to writing an IDE in Go isn't the language but rather the lack of mature graphics library bindings. I've seen some attempts at building GUI toolkits in Go but it's fair to say the language's strong points are still firmly in CLI and backend web development domains.
Text editors, especially with language aware features, are far from simple.
A frame drop causes little more hazard than just an unhappy gamer.
Speaking of which, many keep forgetting that Unreal C++ and Blueprint make use of GC.
In places where you have to use reference types, sometimes you can allocate everything you should ever need up-front and then pull from that pool of pinned instances throughout the day. Pre-allocating all of your required memory can provide more deterministic outcomes.
The JVM does have a “do nothing GC” for short lived apps, where not collecting is ok.
In .NET you can also change the GC policy at the thread level which can help a lot.
Pool Allocation (long held chunks of memory that can be manually repurposed) can help a lot, since you can write custom Pools where the Gc never knows it needs to clean up. It is a useful tool when you have big chunks of memory that need to be manipulated at a high rates.
I would say from experience that 500Hz-1kHz is a more reasonable loop time