But if you want better performance, you use active mode. In active mode, the runtime is receiving data on your behalf, as fast as possible, and sending it to the socket's owning process (think a goroutine) just as fast. Data is often waiting there for you, not just already in user space, but in your Elixir process's mailbox. (Also, this doesn't block your process the way recv/2 does, so you could handle other messages to this process.)
You could imagine doing something similar with 2 goroutines and a channel. Where 1 goroutine is constantly recv from the socket and writing to a buffered channel, and the other is processing the data.
One problem with active mode, and to some degree how messages work in Elixir in general, is that there's no back pressure. Messages can accumulate much faster than you're able to process them. So, instead of active mode, you can use "once" mode or {active, N} mode (once being like {active, 1}. In these modes you get N messages sent to you, at which point it turns into passive mode so you can recv manually. You can put the socket into active/once mode at any point in the future.