# Fast and Small C++
- When Efficiency Matters
- [Fast and Small C++](https://www.youtube.com/watch?v=rNl591__9zY)
- Author: Andreas Fertig (C++ Insight Author)
---
### Topic
* std::unique_ptr
* Small String Optimization(SSO)
* Constexpr
* std::initializer_list
---
### std::unique_ptr
zero cost abstract - like a raw pointer
```cpp
sizeof(std::unique_ptr<int>) == 8
```
---
### std::unique_ptr
If you set a deleter to the unique_ptr,
the size is two times.
```cpp
auto f = std::unique_ptr<FILE, decltype(&fclose)>{fopen(SomeFile.txt, "r"), &fclose};
// sizeof(f) == 16
```
---
### std::unique_ptr - Compressed Pair
[no_unique_address](https://en.cppreference.com/w/cpp/language/attributes/no_unique_address): Allows this data member to be overlapped.
```cpp
template<typename T>
class default_deleter {
void operator()(T* ptr) {
delete ptr;
}
};
template<typename T, typename U>
struct CompressedPair {
[[no_unique_address]] T first;
[[no_unique_address]] U second;
};
template<typename T, typename Deleter = default_deleter<T>>
class unique_ptr {
CompressedPair<Del, T*> mPair;
};
```
---
### std::unique_ptr
Learn the default_deleter
```cpp
// auto f = std::unique_ptr<FILE, decltype(&fclose)>{fopen(SomeFile.txt, "r"), &fclose};
template<typename T, auto DeleteFn>
usinhg unique_ptr_deleter =
std::unique_ptr<T, decltype([](T* obj){DeleteFn(obj);})>;
auto f = unique_ptr_deleter<FILE, fclose> {
fopen("SomeFile.txt", "r")
};
// sizeof(f) = 8
static_assert(sizeof(f) == sizeof(void*));
```
---
### std::unique_ptr - gcc
```
template<typename T, typename Del>
class unique_ptr {
private:
tuple<pointer, _Dp> _M_t;
};
```
---
### Small String Optimization(SSO)
The purpose of SSO is to avoid allocating in heap when the string is small.
```
struct String {
size_t mSize{};
size_t mCapacity{};
char* mData{};
char MSSOBuffer[16]{};
bool mIsSSO{true};
};
static_assert(sizeof(String) == 48);
```
---
### Small String Optimization(SSO)

---
### Small String Optimization(SSO)
If you want to make `sizeof(String) == 24`, how?
* GCC (libstdc++)
* MSVC
* Clang (libc++)
* folly (facebook library)
---
#### Small String Optimization(SSO) - GCC
```cpp
struct String {
char* mPtr;
size_t mSize{};
union {
size_t mCapacity;
char mBuf[8];
}
constexpr String(): mPtr{mBuf}, mBuf{} {}
constexpr String(const char* data, size_t len):
mPtr{fits_into_sso(len)? mBuf : new char[len]},
mSize{len}, mBuf{} {
if (is_long()) {mCapacity = len;}
// copy data to mPtr
}
constexpr static size_t is_long() const {return mPtr != mBuf;}
}
```
---
#### Small String Optimization(SSO) - MSVC
```cpp
struct String {
union {
char* mPtr;
char mBuf[8];
};
size_t mSize{};
size_t mCapacity{};
constexpr static size_t sso_cap() {return sizeof(mBuf) - 1;}
constexpr bool is_long() const {return mCapacity > sso_cap();}
constexpr String() : mBuf{}, mCapacity{sso_cap()} {}
constexpr String(const char* _data, size_t len):
mBuf{}, mSize{len}, mCapacity{fits_into_sso(len) ? sso_cap() : len} {
if (is_long()) {mPtr = new char[len];}
// copy _data to mPtr
}
};
```
---
#### Small String Optimization(SSO) - Clang
* Bigger sso size, 16 bytes.
```cpp
struct String {
static constexpr unsigned BIT_FOR_CAP{sizeof(size_t) * 8 - 1};
struct normal {
size_t large : 1;
size_t capacity : BIT_FOR_CAP;
size_t size;
char* data;
};
struct sso {
uint8_t large : 1;
uint8_t size : (sizeof(uint8_t) * 8 - 1);
uint8_t padding[sizeof(size_t) - sizeof(uint8_t)];
char data[sizeof(normal) - sizeof(size_t)];
}
union {
sso small;
normal large;
} packed;
constexpr String(): packed{} {}
constexpr static size_t sso_cap() { return sizeof(sso::data) - 1; }
constexpr bool is_long() const { return packed.small.large; }
constexpr String(const char* _data, size_t len): packed{} {
if (fits_into_sso(len)) {
packed.small.size = len;
} else {
packed.large.large = true;
packed.large.size = len;
packed.large.data = new char[len];
packed.large.capacity = len;
}
// copy _data to data()
}
constexpr size_t size() const {return is_long() ? packed.large.size : packed.small.size;}
constexpr const char* data() const {return is_long() ? packed.large.data : packed.small.data;}
constexpr const char* capacity() const {return is_long() ? packed.large.capacity : sso_cap();}
}
```
---
#### Small String Optimization(SSO) - Clang

---
#### Small String Optimization(SSO) - [Folly](https://github.com/facebook/folly/blob/main/folly/FBString.h)
**23** bytes in sso mode.
```cpp
struct String {
struct normal {
char* data;
size_t size;
size_t capacity;
}
struct sso {
char data[sizeof(normal)];
}
union {
sso small;
normal large;
} packed;
};
```
---
#### Small String Optimization(SSO) - [Folly](https://github.com/facebook/folly/blob/main/folly/FBString.h)
* get_mode_byte() to return last byte.
```cpp
struct String {
struct normal {
char* data;
size_t size;
size_t capacity;
}
struct sso {
char data[sizeof(normal)];
}
union {
sso small;
normal large;
} packed;
constexpr static size_t sso_cap() { sizeof(sso) - 1; }
constexpr char& get_mode_byte() {
return packed.small.data[sizeof(normal)] - 1;
}
constexpr char get_mode_byte() const {
return packed.small.data[sizeof(normal)] - 1;
}
constexpr bool is_long() const {return get_mode_byte() & 0x40;}
constexpr String(const char* _data, size_t len): packed{} {
if (fits_into_sso(len)) {
// the bytes remaining!
get_mode_byte() = sso_cap() - len;
} else {
get_mode_byte() = 0x40;
packed.large.size() = len;
}
// copy the _data to String
}
constexpr size_t size() const {
return is_long() ? packed.large.size :
sso_cap() - get_mode_byte();
}
};
```
---
### Small String Optimization(SSO) - GCC
* sizeof(std::string) == 32
```cpp
struct _Alloc_hider : allocator_type
{
pointer _M_p; // The actual data.
};
// 8 byte
_Alloc_hider _M_dataplus;
// 8 byte
size_type _M_string_length;
enum { _S_local_capacity = 15 / sizeof(_CharT) };
// 16 bytes
union {
_CharT _M_local_buf[_S_local_capacity + 1];
size_type _M_allocated_capacity;
};
```
---
#### Small String Optimization(SSO) - [Folly](https://github.com/facebook/folly/blob/main/folly/FBString.h)
* small < 23 bytes
* medium < 254 bytes
* Large -> Copy On Write
```cpp
enum class Category : category_type {
isSmall = 0,
isMedium = kIsLittleEndian ? 0x80 : 0x2,
isLarge = kIsLittleEndian ? 0x40 : 0x1,
};
Category category() const {
// works for both big-endian and little-endian
return static_cast<Category>(bytes_[lastChar] & categoryExtractMask);
}
struct MediumLarge {
Char* data_;
size_t size_;
size_t capacity_;
size_t capacity() const {
return kIsLittleEndian ? capacity_ & capacityExtractMask : capacity_ >> 2;
}
void setCapacity(size_t cap, Category cat) {
capacity_ = kIsLittleEndian
? cap | (static_cast<size_t>(cat) << kCategoryShift)
: (cap << 2) | static_cast<size_t>(cat);
}
};
union {
uint8_t bytes_[sizeof(MediumLarge)]; // For accessing the last byte.
Char small_[sizeof(MediumLarge) / sizeof(Char)];
MediumLarge ml_;
};
template <class Char>
FOLLY_NOINLINE void fbstring_core<Char>::copyLarge(const fbstring_core& rhs) {
// Large strings are just refcounted
ml_ = rhs.ml_;
RefCounted::incrementRefs(ml_.data_);
assert(category() == Category::isLarge && size() == rhs.size());
}
```
---
### The Power of constexpr
* why not constexpr?

---
### The Power of constexpr
* why not constexpr?
* [GodBolt](https://godbolt.org/z/54hhz5Y93)

---
### std::initializer_list
* Define a const int array first.

---
### std::initializer_list [GodBolt](https://godbolt.org/z/ereE3TEvP)
* Question?
* Initialize the list every time, even if we don't modify it.
* Compiler assume that Fun may be called recursively.

---
### std::initializer_list [GodBolt](https://godbolt.org/z/8KfebbTaW)
* Add `static`

---
### std::initializer_list [GodBolt](https://godbolt.org/z/rf4q8Tq6c)
* Define a static const int array first after gcc 14.1

---
### Conclusion
* std::unique_ptr
* Use `Empty Object` as the deleter
* Small String Optimization
* GCC union the capacity
* MSVC union the data
* Clang union the size and data, and give a large bit.
* Folly use get_mode_byte and Copy-On-Write.
---
### Conclusion
* constexpr
* constexpr reduce the code size and avoid redundant operation
* std::initializer_list
* use `static const` in function to avoid redundant initialization.
---
{"title":"Fast and Small C++ ","slideOptions":"{\"theme\":\"white\"}","description":"When Efficiency Matters","contributors":"[{\"id\":\"09379b25-db04-47a4-8912-78e722b7a548\",\"add\":12311,\"del\":2620}]"}