4 ms·
TBH, while I can think of many layout optimizations that Rust does and C does not, I can't think of any that C or C++ do that Rust does not. In particular when
by volta83 5y ago
TBH, while I can think of many layout optimizations that Rust does and C does not, I can't think of any that C or C++ do that Rust does not.
In particular when it comes to unions which are heavily used in low-level code, Rust does optimizations that C and C++ can't even dream of (like all the niche optimizations).
- gpderetta 5y agowhich optimizations on unions does rust do?
- volta83 5y agoIn C++, you have `std::optional<T>`. That's a type that either contains a `T` or contains nothing. In C++, sizeof(optional<T>) > sizeof(T) because the discriminat has to be stored somewhere. This is true even if, e.g., you do something like `optional<T&>`. You know that T& is a non-null pointer, and you only have two variants, and one of the variants has no state, so you technically can encode this as 0x0 is the "no reference" variant, and the != 0x0 is the reference variant, and have `optional<T&>` have the same size as `T&`. Rust does these layout optimizations of compressing the discriminant into gaps in the values of discriminated unions automatically. So: enum Option<T> { Some(T), None } for `Option<T&>` has the same size as `T&` in Rust, as opposed to C++. In C, an example would be: struct DU { enum { A, B } discriminant; union { bool A; bool B; } }; You could encode that into 3 bits (i.e. have sizeof(DU) == 1), but instead you'll have at least sizeof(DU) == 2, because you need one byte for the discriminant, and one byte for the payload. It is very easy to create values with gaps in Rust, but C doesn't really support doing this. Another optimization are alignment optimizations. In C, if you write: struct S { uint8_t a; uint32_t b; uint8_t c; }; that ends up being 12 bytes long. In Rust, by default, that gets reordered as uint32_t, uint8_t, uint8_t, so it only ends up being 8 bytes. If you want that instead to be laid out like in C, you can write: #[repr(C)] struct S { a: u8, b: u32, c: u8 } and then you get the same 12 bytes as in C. There are many supported `repr(...)` options supported for algebraic data types, e.g., you can use repr(u32) for the Rust enum above to store the discriminant in a u32, and get the same layout as DU in C.
- gpderetta 5y agoRight, c++ has no builtin discriminated union, and std::optional<T&> is actually not valid in C++ (unfortunately). But discriminant-less custom optionals classes with 'zero' type support via traits are easy to do (and I have done it many times). You do not have to rely on the compiler identifying a safe empty state and you can define any application specific one. For example if for one specific (and common) use case strings are always non null, optional<string, non_null_trait> has an obvious implementation. So, yes, these sorts of optimizations are not done by the compiler has it has no notion of discriminated types (one day maybe...), but can be done generically by the programmer. In fact you could in principle optimize multiple layers of variant<optional<...>,... > and collapse everything in one discriminant,as long as you do not provide reference access to the sub variants; the required metaprogram is not going to be pretty though. One of the advantages of C++ is that it allows this sort of control.
- cogman10 5y ago> One of the advantages of C++ is that it allows this sort of control. But I believe what the OP showed is that Rust also allows you to have that level of control. However, the default behavior for rust is to do the optimization that you have to go out of your way to manually implement in C++. Rust isn't taking away control from what you can do in C++, instead, it's made the idiomatic approach one that is well optimized by the compiler.
- lenkite 5y agoThere are compiler directives for packed structs in C++. struct __attribute__ ((packed))
- steveklabnik 5y agoRust also has a packed attribute; this is something different. The re-ordering still includes padding, whereas packed removes the padding.
- fxtentacle 5y agoDepending on the values in b, you could do bit-packing in C++ and munch it down to just 1 byte.