22 karma · joined August 22, 2016
SoundCloud for example, had a bunch of engineers earning around that. Probably all laid off now, though.
There are companies on StackOverflow jobs openly advertising €80-90k for senior developers now, and occasionally over 100k for specialist roles like ML or performance sensitive development in C++.
I haven't looked at this for a while but I believe it's still an issue.
https://github.com/cockroachdb/cockroach/issues/2007 https://github.com/golang/go/issues/18896 https://github.com/golang/go/issues/14045
Similar to the issues you had with Redis, the kernel change to THP on by default totally destroyed CoW sharing for forked Ruby processes, despite Koichi Sasada's change to make the GC more CoW friendly. Without disabling THP, a single run of GC marking can cause the entire heap to be copied for the child.
The bigger issue is heap fragmentation. Lots of inherited pages have a couple of free slots that Ruby will happily consume, effectively causing you to pay a 4kb copy for a 40 byte object.
This means allocating a few hundred objects can cause basically the entire heap to copy. Combine this with Transparent Huge Pages, where flipping one bit causes a 2MB copy rather than 4kb, and a few hundred object allocations can cause the entire parent process memory to be duplicated into the child.
Aaron Patterson is doing some great work to bring GC compaction to MRI, which will help reduce fragmentation, but it's a huge task.
Java doesn't, or at least didn't, collect anonymous classes without an additional GC flag being enabled, which could bite you quite hard using JRuby with gems which made heavy use of anonymous classes.
The second problem is importing a huge chunk of rows at once means they have to exist in RAM at the same time. Use batched iterators to reduce peak memory usage. All GCed languages have this problem, Go included.
You'd think Go's GC was somehow revolutionary from the way they talk about it, but it's basically the same as the Ruby GC, plus a little more parallelism. What helps Go is that the compiler can optimise and use stack allocation and re-use temprory variables. If it fails, it causes a nightmare, and the Go standard library is full of tricks to convince the compiler to do stack allocation.
Java, OTOH, has compacting garbace collection, so after high peak memory usage, it can release the memory back to the OS. Aaron Patterson has been working on doing the same for Ruby. If you use JRuby, you'll get this right now, plus it's about 3x faster for shuffling data around.
Since 1.9 you can use real OS threads to achieve parallel IO, and certain parallel computations which can proceed without holding the Global Interpreter Lock.
JRuby offers completely unrestricted threading with a single process, plus a 3x performance boost, plus the advantage of an incredible amount of work put into their VM and GC. It's a really underrated option these days.
The problem with forking, is MRI Ruby is unaware what memory is actually inherited by forking, so eventually it causes the entire heap inherited from the parent to copy into the child.
I'm actually working on a patch to fix this. The solution is simple, just don't mark, collect, or allocate into inherited pages, but the implementation itself is fiddly.
What's really exacerbated this, is that most Linux distros now have Transparent Huge Pages turned on by default, and flipping a single bit in inherited causes a 2MB copy instead of a 4kb copy!
I often hear "context switching between threads is cheaper" but pthreads still have their own PID and everything, so is this really the case?
Is there really much advantage to pthreads over the way PostgreSQL does things with efficient CoW sharing between processes for the binary?
C extensions are a core part of Ruby. A big chunk of the std lib is implemented in C. You shouldn't be afraid of writing a tiny bit of C. Ruby and Python are scripting languages. They're literally designed for scripting another language.
The JIT for Ruby 3 is well underway, so in a bit it'll be a complete non-issue anyway.
The Ruby GIS libaries already have C implementations for haversine but I just wanted to show how easy it is to hook out to a C function for math heavy stuff.
Is Python/Django performance significantly worse than Ruby/Rails? The situations you describe are things I do every day in Ruby. Getting 1000 rows from the DB and performing some operation only takes a couple of milliseconds in Ruby.
Ruby/Python are meant to be glue, and you can most certainly use them to glue together "interesting things", like image processing or audio processing in a web layer.
Memory caching rarely changed, often accessed, but ultimately persisted in a DB things like exchange rates in a global object is exactly what you do in Rails. There's a specific helper for doing it. http://api.rubyonrails.org/classes/ActiveSupport/Cache/Memor...
Recent benchmarks of a properly setup Ruby environment vs Go/Gin are showing Ruby/Sinatra as having 50% of the throughput https://www.techempower.com/benchmarks/#section=data-r14&hw=...
You can also just use JRuby and use a single JVM process. For small CLI apps, MRuby can even be compiled to C, then compiled as an executable.