Live data from Hacker News

Io_uring, kTLS and Rust for zero syscall HTTPS server

blog.habets.se

101–110 of 173 posts

Re: Io_uring, kTLS and Rust for zero syscall HTTPS server

#101
post #64

Earlier quoted context omitted.

In my universe, `let` wouldn’t exist… instead there would only be 3 ways to declare variables: 1. global my_global_var: GlobalType = … 2. heap my_heap_var: HeapType = … 3. stack my_stack_var: StackType = … Global types would need to implement a global trait to ensure mutual exclusion (waves hands). So by having the location of allocation in the type itself, we no longer have to do boxing mental gymnastics

Doesn't Rust do this? `let` is always on the stack. If you want to allocate on the heap then you need a Box. So `let foo = Box::new(MyFoo::default ())` creates a Box on the stack that points to a MyFoo on the heap. So MyFoo is a stack type and Box is a heap type. Or do you think there is value in defining MyFooStack and MyFooHeap separately to support both use cases?

You may already know this, but let-bindings are not necessarily on the stack. The reference does say they are (it's important to remember that the reference is not normative), and it is often simpler to think of them that way, but in reality they don't have to be on the stack.

The compiler can perform all sorts of optimizations, and on most modern CPU architectures, it is better to shove as many values into registers as possible. If you don't take the address of a variable, you don't run out of registers, and you don't call other, non-inlined functions, then let-bindings (and function arguments/return values) need not ever spill onto the stack.

In some cases, values don't even get into registers. Small numeric constants (literals, consts, immutable lets) can simply be inlined as immediate values in the assembly/machine code. In the other direction, large constant arrays and strings don't spill onto the stack but rather the constant pool.

Re: Io_uring, kTLS and Rust for zero syscall HTTPS server

#102

Earlier quoted context omitted.

What were the original problems exactly? From what I recall they effectively boiled down to size concerns due to seeing themselves as a c/c++ successor and they didn’t want to lose any adoption in the embedded systems target audience.

I mean from an outsiders perspective on Rust this is how I saw it. Rust is in a strange place because they're a systems language directly competing with C++. Async, in general, doesn't vibe with that but green threads definitely don't. If you're gonna do green threads you might as well throw in a GC too and get a whole runtime. And now you're writing Go.

I don't think doing green threads equates to 'well might as well have a GC now!'. I think they made the wrong tradeoff too, because hardware will inevitably catch up to the language requirements, especially if its desirable to use. Not to mention over time things can be made more efficient from the Rust side as well, with compiler improvements, better programming techniques etc.

I think they made the wrong bet, personally. Having worked in enough languages that have function coloring problems I would avoid it as a language design as a line in the sand item, regardless of tradeoffs

Re: Io_uring, kTLS and Rust for zero syscall HTTPS server

#103
post #80

Where do people get the idea that one thread per core is correct on a system that deals with time slices? In my experience “oversubscribing” threads to cores (more threads than cores) provides a wall-clock time benefit. I think one thread per core would work better without preemptive scheduling. But then we aren’t talking about Unix.

Isolating a core and then pinning a single thread is the way to go to get both low latency and high throughput, sacrificing efficiency. This works fine on Linux, and common approach for trading systems where it’s fine to oversubscribe a bunch of cores for this type of stuff. The cores are mostly busy spinning and doing nothing, so it’s very inefficient in terms of actual work, but great for latency and throughput whe…

I just wish people who give this advice for 1 thread per core would "expand their reasoning" or "show the work".

It's not blanket good advice for all things.

Re: Io_uring, kTLS and Rust for zero syscall HTTPS server

#105
post #64

Earlier quoted context omitted.

Doesn't Rust do this? `let` is always on the stack. If you want to allocate on the heap then you need a Box. So `let foo = Box::new(MyFoo::default ())` creates a Box on the stack that points to a MyFoo on the heap. So MyFoo is a stack type and Box is a heap type. Or do you think there is value in defining MyFooStack and MyFooHeap separately to support both use cases?

You may already know this, but let-bindings are not necessarily on the stack. The reference does say they are (it's important to remember that the reference is not normative), and it is often simpler to think of them that way, but in reality they don't have to be on the stack. The compiler can perform all sorts of optimizations, and on most modern CPU architectures, it is better to shove as many values into registers…

In particular, let bindings within async code (and coroutines, if that feature is stabilized at some point) might easily live on the heap.

Re: Io_uring, kTLS and Rust for zero syscall HTTPS server

#106
post #64

Earlier quoted context omitted.

Doesn't Rust do this? `let` is always on the stack. If you want to allocate on the heap then you need a Box. So `let foo = Box::new(MyFoo::default ())` creates a Box on the stack that points to a MyFoo on the heap. So MyFoo is a stack type and Box is a heap type. Or do you think there is value in defining MyFooStack and MyFooHeap separately to support both use cases?

The suggestion is c# class vs struct basically, with explicit globals which are just class with synchronization

Note that items declared as `static` in Rust are already globals that require synchronization (in Rust terms, static items must implement `Sync`), although they're located in static memory rather than on the stack or heap.

Re: Io_uring, kTLS and Rust for zero syscall HTTPS server

#107
post #37

I think rusts glacial compile times prevent it from being a useful platform for web apps. Yes it's a nice language, and very performant, but it's horrible devex to have to wait seconds for your server to recompile after a change.

> but it's horrible devex to have to wait seconds for your server to recompile after a change. What a time to be alived that seconds to recompile is consider horrible devex.

At my first job out of college it took 30 minutes to recompile and launch the server. Now the kids complain about 10 seconds. It's just impossible for me to take their complaints seriously. 10 seconds isn't even enough time for a mental context-switch, its just slightly more time than "instant". Back in the day, something like this wasn't an exaggeration: https://xkcd.com/303/

Re: Io_uring, kTLS and Rust for zero syscall HTTPS server

#108

Where do people get the idea that one thread per core is correct on a system that deals with time slices? In my experience “oversubscribing” threads to cores (more threads than cores) provides a wall-clock time benefit. I think one thread per core would work better without preemptive scheduling. But then we aren’t talking about Unix.

A mistake people make with thread-per-core (TPC) architecture is thinking you can pick and choose the parts you find convenient, when in reality it is much closer to "all or nothing". It may be worse to half-ass a TPC implementation than to not use TPC at all. However, TPC is more efficient in just about all contexts if you do it correctly.

Most developers are unfamiliar with the design idioms for TPC e.g. how to properly balance and shed load between cores.

Re: Io_uring, kTLS and Rust for zero syscall HTTPS server

#109
post #60

Earlier quoted context omitted.

> But the stackfull model is The existence of advantages doesn't change anything here. The problems is that the disadvantages made this approach a non-starter, despite a lot of effort to make it work. Tradeoffs exist in language design, and the approaches were judged accordingly. What works for Go doesn't necessarily work for Rust, because they target different domains. > I am really confident that if the async syste…

What were the original problems exactly? From what I recall they effectively boiled down to size concerns due to seeing themselves as a c/c++ successor and they didn’t want to lose any adoption in the embedded systems target audience.

Have you read the article by Aaron Turon linked above? It's very informative, and if you have any questions about specific parts of it, feel free to reference them. In particular it boils down to the fact that Rust bends over backwards to avoid putting anything that requires allocation or dynamic dispatch in the core language (e.g. Rust's closures are fascinating in that they're stack-allocated, like C++'s, while also playing nicely with the borrow checker, which is quite a feat). This property extends to the current design of async, which makes async suitable for embedded devices, which is extremely cool (check out the Embassy project for the state of the art in this space).

Re: Io_uring, kTLS and Rust for zero syscall HTTPS server

#110

Earlier quoted context omitted.

> it has obvious limitations (and generally it can be called unsound, especially around thread locals) Is this really better than what we have now? I don't think async is perfect, but I can see what tradeoffs they are currently making and how they plan to address most if not all of them. "General" unsoundness seems like a rather large downside. > In future I plan to create a custom "green-thread" fork of `std` to eas…

>Is this really better than what we have now? Depends on the metric you use. Memory-wise it's a bit less efficient (our tasks usually are quite big, so relative overhead is small in our case), runtime-wise it should be on par or slightly ahead. From the source code perspective, in my opinion, it's much better. We don't have the async/await noise everywhere and after development of the `std` fork we will get async in…

If you use a custom libc and dynamic linker, you can very easily customize thread locals to work the way you want without forking the standard library.
Post reply on HN