Repository: rust
Issue: Enum field align cause performance degradation about 10x
Labels: I-slow,T-compiler,A-layout,C-optimization

Hi, I'm the author of `FastStr` crate, and recently I found a wired problem that the clone cost of `FastStr` is really high. For example, an empty `FastStr` clone costs about 40ns on amd64 compared to about 4ns of a normal String.

The `FastStr` itself is a newtype of the inner `Repr`, which previously has the following layout:

```rust
#[derive(Clone)]
pub type FastStr(Repr);

#[cfg(all(test, target_pointer_width = "64"))]
mod size_asserts {
    static_assertions::assert_eq_size!(super::FastStr, [u8; 40]); // 40 bytes
}

const INLINE_CAP: usize = 38;

#[derive(Clone)]
enum Repr {
    Empty,
    Bytes(Bytes),
    ArcStr(Arc<str>),
    ArcString(Arc<String>),
    StaticStr(&'static str),
    Inline { len: u8, buf: [u8; INLINE_CAP] },
}
```

[Playground link for old version](https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=b04ccd351a48d83797be731dfddd4306)

After some time of investigation, I found that this is because the `Repr::Inline` part has really great affect on the performance. And after I added a padding to the `Repr::Inline` variant(change the type of `len` from `u8` to `usize`), the performance of clone a `Repr::Empty`(and other variants all) boosts about 9x from 40ns to 4ns. But the root cause is still not clear:

```rust
const INLINE_CAP: usize = 24; // This is becuase I don't want to enlarge the size of FastStr

#[derive(Clone)]
enum Repr {
    Empty,
    Bytes(Bytes),
    ArcStr(Arc<str>),
    ArcString(Arc<String>),
    StaticStr(&'static str),
    Inline { len: usize, buf: [u8; INLINE_CAP] },
}
```

[Playground link for new version](https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=b22b5f11735861f231929309321f6479)

A simple criterion benchmark code for the old version:

```rust
use bytes::Bytes;
use std::sync::Arc;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

const INLINE_CAP: usize = 38;

#[derive(Clone)]
enum Repr {
    Empty,
    Bytes(Bytes),
    ArcStr(Arc<str>),
    ArcString(Arc<String>),
    StaticStr(&'static str),
    Inline { len: u8, buf: [u8; INLINE_CAP] },
}

fn criterion_benchmark(c: &mut Criterion) {
    let s = Repr::Empty;
    c.bench_function("empty repr", |b| b.iter(|| black_box(s.clone())));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
```

For a full benchmark, you may refer to: https://github.com/volo-rs/faststr/blob/main/benches/faststr.rs

Related PR: https://github.com/volo-rs/faststr/pull/6
And commit: https://github.com/volo-rs/faststr/commit/342bdc95e6d4f599911ce9b5bc566d77b1ca75a7

Furthermore, I've tried the following methods, but none helps:
1. only change `INLINE_CAP` to 24
2. change `INLINE_CAP` to 22 and added a padding to the Inline variant: `Inline {_pad: u64,len: u8,buf: [u8; INLINE_CAP],},`
3. change `INLINE_CAP` to 22 and add a new struct `Inline` without the `_pad` field

To change the `INLINE_CAP` to 22 is only for not increasing the size of `FastStr` itself when add an extra padding, so the performance is nothing to do with it.

Edit: related discussions [users.rust-lang.org](https://users.rust-lang.org/t/manually-adding-padding-to-faststr-gains-9x-boost-on-performance-but-why/104342), [reddit](https://www.reddit.com/r/rust/comments/18p3izv/manually_adding_padding_to_faststr_gains_9x_boost/)