4 ms·
> The only real drawback here is that there is some performance hit from boxing the futures – but I suspect it is negligible in almost all applications. I don’t
by fluffything 7y ago
> The only real drawback here is that there is some performance hit from boxing the futures – but I suspect it is negligible in almost all applications. I don’t think this would be true if we boxed the results of all async fns; there are many cases where async fns are used to create small combinators, and there the boxing costs might start to add up. But only boxing async fns that go through trait boundaries is very different.
In a situation like this:
trait Foo { async fn foo(&self) -> Bar; }
trait Baz: Foo { async fn baz(&self) -> Meow { ...await self.foo() into a Meow ... }
you'll end up with boxes of boxes of boxes. It kinds of makes the feature something for the outermost abstraction layer or you end up with boxes of boxes of boxes.
> And of course it’s worth highlighting that most languages box all their futures, all of the time. =)
It's also worth highlighting that most of those languages (1) have a GC that makes boxing a much cheaper operation than in Rust, and (2) those languages aren't advertised as "low-level" languages and that justifies implicit boxing as a trade-off.
---
I feel that async shouldn't really be special but rather just build on other features that stand on its own. GATs and impl Trait in Traits seem reasonable. But being able to do dynamic dispatch on trait methods that return a type that isn't `Sized` seems like a tough problem to solve, and the blog post didn't managed to convince me that implicit boxing is the right approach here. AFAICT, we need either some kind of boxing, or better support for unsized rvalues or similar. I think I would be more comfortable with "sugar" for boxing if the caller of the trait method were in control of where exactly the result is allocated (not necessarily a Box), but that calls for some kind of placement syntax.
- Rusky 7y agoIn that particular example you won't get boxes of boxes- `baz` gets instantiated for each impl of `Baz`, so the call to `self.foo()` is resolved statically and the return type is fully known.
- ogoffart 7y agoDepends on the (not-yet-implemented) implementation of async functions in traits. But currently, with the async_trait macro, there is only one version of Foo::foo, and it returns a Box<...>. But it is true we could imagine the compiler generating two versions: one for when the static type is known, and one if dynamic dispatch is used.
- likeliv 7y ago> most of those languages (1) have a GC that makes boxing a much cheaper operation than in Rust Why is that? I would intuitively think it is the other way. (Is a malloc/free pair not cheaper than an allocation on the GC heap + collecting its garbages?)
- steveklabnik 7y agoAs always, it depends on a lot of details. A generational garbage collector can be made to allocate extremely quickly; IIRC for the JVM it's like, seven instructions? For short lived allocations, it sort of acts like an arena, which is very high performance. malloc/free need to be quite general. It's always about details though. If a GC is faster than malloc/free, but your language doesn't tend to allocate much to begin with, the whole system can be faster even if malloc is slower. It always depends.
- geogriffin 7y agoDoesn't something like jemalloc basically give you this, but without pauses? Thread-local freelists for quick recycling of small allocations without synchronization.. funnily enough, jemalloc even uses some garbage collection mechanisms internally.
- steveklabnik 7y agoI don't know a ton about jemalloc internals, but it is true that a lot of modern mallocs use some mechanisms similar to GCs. There's some pretty major constraint differences though.
- fluffything 7y ago`malloc` + `free` are unknown function calls, they can't be inlined, don't understand the semantics of your language, the strategy behind them is quite generic, etc. A GC that's integrated with a programming language can do much much better (different heaps for short and long lived allocations, for example).