4 ms·
It's surprising to me that you apparently have to fight for memory usage for these cases when using Go. A while ago I ran a (quite naively written) nodejs appl
by phoboslab 7y ago
It's surprising to me that you apparently have to fight for memory usage for these cases when using Go.
A while ago I ran a (quite naively written) nodejs application that maxed out at ~700k WebSocket connections per server - using only 4GB of RAM. Here CPU became the bottleneck.
- sansnomme 7y agoGo's concurrency design trades off memory usage for productivity; instead of red-blue functions where you have to explicitly design for function interrupt/yield points with the async keyword, you can just write sequential code and the runtime will handle the rest. The downside to this approach is that often the stack will have to be copied during the switching process vs the stackless approach preferred by Node.js, Rust, C# etc. See the excellent Fibers Under a Magnifying Glass paper by Microsoft Research: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2018/p1364r0.pdf http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2018/p136...
- nielsbjerg 7y agoProductivity in this case being in the eye of the beholder? I’d argue that people experienced in how node works, wouldn’t have to think too much, since async is the default. I agree with your general sentiment though.
- sansnomme 7y agoAsync by default can often lead to weird race conditions if you are not careful.
- networkimprov 7y agoYes; you still need to lock/synchronize shared resources in an async context. Even if the environment is single-threaded.
- mc3 7y agoDo you have an example? If it is single threaded, and we are talking about shared variables, I thought I can assume the runtime is not going to pause my code execution, switch context, and run other code midway through my call back handler. If we are talking about shared external resources (e.g. who can update a cloud blob) then we could have a proxy for that as a variable. You might need retry logic, and it could get tricky in that respect.
- foota 7y agoThis is not the case, if your callback then goes off and does something asynchronous.
- mc3 7y agoYes sorry I was thinking in terms of code that uses callbacks, not the async keyword. What I mean is that distinct from threading, where the following code could be interrupted between the first and second line of the function, by something that updates global: var global function addTheseToGlobal(a, b) { im = a + global return im + b }
- connicpu 7y agoYou can rely on node preventing data races, but those are distinct from race conditions. A race condition in the logic of your code can happen any time two "threads" of execution (in this case a thread could be considered a chain of asynchronous callbacks) interleave their operations. It's possible for one of them to do something with a resource the other was using unless you use some kind of synchronization to prevent the other from using the resource until the first thread is done with it. For example, two callback chains could start using the same database connection object. Perhaps one chain was in the middle of setting up a transaction when it needed to wait for some other async resource to load, and the other chain comes in and does something with it. Now it's in an unintended state because the object was allowed to be used by two different "threads" of callback chains.
- nielsbjerg 7y agoI’d then have node handle the “simple” socket part, and the complexity which is race condition prone, handled by a language better suited for that, responding to the async node implementation? Edit: spelling
- Thaxll 7y agoThis is not how Go "works" overall, you're talking about the size of the goroutine stack which is by default 4KB, so in a scenario with a lot of connections yes it's going to add up if you use 1:1 connection / goroutine, but outisde of that Go uses less memory than Node / C# / Java / Python ect ... So I woudn't say "Go trades off memory usage for productivity" since Go is widely used for low memory footprint. Same reasons why Go makes sense in services like Kubernetes where each pods are in the range of 2 digits MB, it woudn't be possible whith languages mentioned above. Edit: In your edit context it makes more sense :)
- deleted 7y ago[deleted]
- apta 7y ago> where each pods are in the range of 2 digits MB, it woudn't be possible whith languages mentioned above. Not sure about NodeJS and Python, but it's certainly doable with Java and C#. It's just that people don't take the time to configure the JVM/CLR correctly. There's nothing magical about golang that you can't do in C# (and soon enough, in Java with the addition of value types). Arguably, C# and Java's value type implementations are superior anyway.
- Thaxll 7y agoKubernetes released in 2015, at that time it wasn't possible no to run some Java / C# servers with settings bellow 256MB ( -XMS ), I'm pretty sure it's still the case with Java as of today. Try to run some service with -XMX -XMS 128MB and tell us how it goes.
- apta 7y ago> Try to run some service with -XMX -XMS 128MB and tell us how it goes. What does the service do? An API call that returns the current time? A batch processor? A payment portal? The memory usage depends on the type of work performed obviously. Furthermore, there are already offerings like https://quarkus.io/ https://quarkus.io/, micronaut, and others that make use of native image compilation for even smaller footprints.
- Thaxll 7y agoYou don't have to fight for memory, it's because of the overhead of Goroutines / default HTTP connections in a scenario with a lot of connections. By default Go uses way less memory than Node. And in Node the only way to have semi decent performance is to use ultra optimized C/C++ external libraries.
- winrid 7y agoCPU was probably the bottleneck from GC. You could tune the GC to get better results probably.