Removing static slices *increase* libstd size


#1

To improve the memory efficiency of to_ascii_uppercase(), I got rid of the ASCII_UPPERCASE_MAP by changing:

ASCII_UPPERCASE_MAP[*self as usize]

to

match *self {
    0x61 ... 0x7A => *self - 32,
    _ => *self,
}

Unfortunately, this increases the size of libstd (from 10 173 068 to 10 176 204 bytes), and I don’t understand why. When I take the code out of ascii.rs and compile it as a separate cargo projects, the resulting library with my new code is much smaller than the one with the current std code with the static slice.

[details=Minimal library using my new code]``` use std::char;

#[inline] pub fn to_ascii_uppercase(c: char) -> char { match c { ‘a’…‘z’ => unsafe { char::from_u32_unchecked(c as u32 - 32) }, _ => c, } }


[details=Minimal library copied from std]```
#[inline]
pub fn to_ascii_uppercase(c: char) -> char {
    if c as u32 <= 0x7F {
        ASCII_UPPERCASE_MAP[c as usize] as char
    } else {
        c
    }
}

static ASCII_UPPERCASE_MAP: [u8; 256] = [
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'',
    b'(', b')', b'*', b'+', b',', b'-', b'.', b'/',
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7',
    b'8', b'9', b':', b';', b'<', b'=', b'>', b'?',
    b'@', b'A', b'B', b'C', b'D', b'E', b'F', b'G',
    b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
    b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
    b'X', b'Y', b'Z', b'[', b'\\', b']', b'^', b'_',
    b'`',

          b'A', b'B', b'C', b'D', b'E', b'F', b'G',
    b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
    b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
    b'X', b'Y', b'Z',

                      b'{', b'|', b'}', b'~', 0x7f,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
];
```[/details]

My code is here: https://github.com/ariasuni/rust/commit/31b51d7dd6312577cb46eaef74286a13d5a6406b

#2

Code takes up library size just as data does. With ASCII_UPPERCASE_MAP there’s a single copy of that array in the library. With the match (if it gets inlined), there are N copies of that code throughout the library. In addition, if that function is marked inline, the body has to be serialized into the library’s metadata so it can be inlined cross crate.

Also, if you’re looking to shrink the size of libstd, there is probably lower hanging fruit than a single 256 byte array :slight_smile:


#3

I’m confused because:

  • when not inlined, the test libraries take 3 574 for my code vs 4 610 bytes for the std code.
  • when inlined, 3 336 vs 4 366 bytes (I’m not sure where the crate metadata are; the only other file which ends in .d is 152 bytes for both)

Even if it does not result in a gain, I’m really interested in why. :grin:


#4

An .rlib is an ar archive (just like .a) and I believe ar tv foo.rlib will list the contents and their size.


#5

Well, every element in the .rlib is bigger in the std example code than the one with my new code.