[ox/std] Add a form of small string and small vector optimization

This commit is contained in:
2021-05-28 02:46:09 -05:00
parent b36cd1694b
commit 1df1e3809f
8 changed files with 421 additions and 283 deletions

View File

@ -17,8 +17,61 @@
namespace ox {
namespace detail {
template<typename T, std::size_t Size = 100>
struct SmallVector {
private:
AllocAlias<T> m_data[Size] = {};
protected:
constexpr void initItems(T **items, std::size_t cap) noexcept {
if (cap <= Size) {
*items = bit_cast<T*>(m_data);
} else {
*items = bit_cast<T*>(new AllocAlias<T>[cap]);
}
}
constexpr void moveItemsFrom(T **items, SmallVector &src, const std::size_t count, const std::size_t cap) noexcept {
if (cap <= Size) {
const auto dstItems = bit_cast<T*>(m_data);
const auto srcItems = bit_cast<T*>(src.m_data);
for (auto i = 0u; i < count; ++i) {
dstItems[i] = move(srcItems[i]);
}
*items = bit_cast<T*>(m_data);
}
}
constexpr void clearItems(AllocAlias<T> *items) noexcept {
if (items != m_data) {
delete[] items;
}
}
};
template<typename T>
class Vector {
struct SmallVector<T, 0> {
protected:
constexpr void initItems(T **items, std::size_t cap) noexcept {
*items = bit_cast<T*>(new AllocAlias<T>[cap]);
}
constexpr void moveItemsFrom(T**, SmallVector&, const std::size_t, const std::size_t) noexcept {
}
constexpr void clearItems(AllocAlias<T> *items) noexcept {
delete[] items;
}
};
}
template<typename T, std::size_t SmallVectorSize = 0>
class Vector: detail::SmallVector<T, SmallVectorSize> {
public:
using value_type = T;
@ -232,45 +285,46 @@ class Vector {
};
template<typename T>
Vector<T>::Vector(std::size_t size) noexcept {
template<typename T, std::size_t SmallVectorSize>
Vector<T, SmallVectorSize>::Vector(std::size_t size) noexcept {
m_size = size;
m_cap = m_size;
m_items = bit_cast<T*>(new AllocAlias<T>[m_cap]);
for (std::size_t i = 0; i < size; i++) {
this->initItems(&m_items, m_cap);
for (std::size_t i = 0; i < size; ++i) {
m_items[i] = {};
}
}
template<typename T>
Vector<T>::Vector(const Vector<T> &other) {
template<typename T, std::size_t SmallVectorSize>
Vector<T, SmallVectorSize>::Vector(const Vector &other) {
m_size = other.m_size;
m_cap = other.m_cap;
m_items = bit_cast<T*>(new AllocAlias<T>[m_cap]);
for (std::size_t i = 0; i < m_size; i++) {
this->initItems(&m_items, other.m_cap);
for (std::size_t i = 0; i < m_size; ++i) {
m_items[i] = move(other.m_items[i]);
}
}
template<typename T>
Vector<T>::Vector(Vector<T> &&other) noexcept {
template<typename T, std::size_t SmallVectorSize>
Vector<T, SmallVectorSize>::Vector(Vector &&other) noexcept {
m_size = other.m_size;
m_cap = other.m_cap;
m_items = other.m_items;
this->moveItemsFrom(&m_items, other, m_size, m_cap);
other.m_size = 0;
other.m_cap = 0;
other.m_items = nullptr;
}
template<typename T>
Vector<T>::~Vector() {
template<typename T, std::size_t SmallVectorSize>
Vector<T, SmallVectorSize>::~Vector() {
clear();
delete[] bit_cast<AllocAlias<T>*>(m_items);
this->clearItems(bit_cast<AllocAlias<T>*>(m_items));
m_items = nullptr;
}
template<typename T>
bool Vector<T>::operator==(const Vector<T> &other) const {
template<typename T, std::size_t SmallVectorSize>
bool Vector<T, SmallVectorSize>::operator==(const Vector &other) const {
if (m_size != other.m_size) {
return false;
}
@ -282,14 +336,15 @@ bool Vector<T>::operator==(const Vector<T> &other) const {
return true;
}
template<typename T>
constexpr Vector<T> &Vector<T>::operator=(const Vector<T> &other) {
template<typename T, std::size_t SmallVectorSize>
constexpr Vector<T, SmallVectorSize> &Vector<T, SmallVectorSize>::operator=(const Vector &other) {
if (this != &other) {
clear();
delete[] bit_cast<AllocAlias<T>*>(m_items);
this->clearItems(bit_cast<AllocAlias<T>*>(m_items));
m_items = nullptr;
m_size = other.m_size;
m_cap = other.m_cap;
m_items = bit_cast<T*>(new AllocAlias<T>[m_cap]);
this->initItems(&m_items, other.m_cap);
for (std::size_t i = 0; i < m_size; i++) {
m_items[i] = other.m_items[i];
}
@ -297,14 +352,15 @@ constexpr Vector<T> &Vector<T>::operator=(const Vector<T> &other) {
return *this;
}
template<typename T>
constexpr Vector<T> &Vector<T>::operator=(Vector<T> &&other) noexcept {
template<typename T, std::size_t SmallVectorSize>
constexpr Vector<T, SmallVectorSize> &Vector<T, SmallVectorSize>::operator=(Vector &&other) noexcept {
if (this != &other) {
clear();
delete[] bit_cast<AllocAlias<T>*>(m_items);
this->clearItems(bit_cast<AllocAlias<T>*>(m_items));
m_size = other.m_size;
m_cap = other.m_cap;
m_items = other.m_items;
this->moveItemsFrom(&m_items, other, m_size, m_cap);
other.m_size = 0;
other.m_cap = 0;
other.m_items = nullptr;
@ -312,18 +368,18 @@ constexpr Vector<T> &Vector<T>::operator=(Vector<T> &&other) noexcept {
return *this;
}
template<typename T>
constexpr T &Vector<T>::operator[](std::size_t i) noexcept {
template<typename T, std::size_t SmallVectorSize>
constexpr T &Vector<T, SmallVectorSize>::operator[](std::size_t i) noexcept {
return m_items[i];
}
template<typename T>
constexpr const T &Vector<T>::operator[](std::size_t i) const noexcept {
template<typename T, std::size_t SmallVectorSize>
constexpr const T &Vector<T, SmallVectorSize>::operator[](std::size_t i) const noexcept {
return m_items[i];
}
template<typename T>
Result<T&> Vector<T>::front() noexcept {
template<typename T, std::size_t SmallVectorSize>
Result<T&> Vector<T, SmallVectorSize>::front() noexcept {
if (!m_size) {
AllocAlias<T> v;
return {*bit_cast<T*>(&v), OxError(1)};
@ -331,8 +387,8 @@ Result<T&> Vector<T>::front() noexcept {
return m_items[0];
}
template<typename T>
Result<const T&> Vector<T>::front() const noexcept {
template<typename T, std::size_t SmallVectorSize>
Result<const T&> Vector<T, SmallVectorSize>::front() const noexcept {
if (!m_size) {
AllocAlias<T> v;
return {*bit_cast<T*>(&v), OxError(1)};
@ -340,8 +396,8 @@ Result<const T&> Vector<T>::front() const noexcept {
return m_items[0];
}
template<typename T>
Result<T&> Vector<T>::back() noexcept {
template<typename T, std::size_t SmallVectorSize>
Result<T&> Vector<T, SmallVectorSize>::back() noexcept {
if (!m_size) {
AllocAlias<T> v;
return {*bit_cast<T*>(&v), OxError(1)};
@ -349,8 +405,8 @@ Result<T&> Vector<T>::back() noexcept {
return m_items[m_size - 1];
}
template<typename T>
Result<const T&> Vector<T>::back() const noexcept {
template<typename T, std::size_t SmallVectorSize>
Result<const T&> Vector<T, SmallVectorSize>::back() const noexcept {
if (!m_size) {
AllocAlias<T> v;
return {*bit_cast<T*>(&v), OxError(1)};
@ -358,18 +414,18 @@ Result<const T&> Vector<T>::back() const noexcept {
return m_items[m_size - 1];
}
template<typename T>
constexpr std::size_t Vector<T>::size() const noexcept {
template<typename T, std::size_t SmallVectorSize>
constexpr std::size_t Vector<T, SmallVectorSize>::size() const noexcept {
return m_size;
}
template<typename T>
bool Vector<T>::empty() const noexcept {
template<typename T, std::size_t SmallVectorSize>
bool Vector<T, SmallVectorSize>::empty() const noexcept {
return !m_size;
}
template<typename T>
void Vector<T>::clear() {
template<typename T, std::size_t SmallVectorSize>
void Vector<T, SmallVectorSize>::clear() {
if constexpr(is_class<T>()) {
for (std::size_t i = 0; i < m_size; ++i) {
m_items[i].~T();
@ -378,8 +434,8 @@ void Vector<T>::clear() {
m_size = 0;
}
template<typename T>
constexpr void Vector<T>::resize(std::size_t size) {
template<typename T, std::size_t SmallVectorSize>
constexpr void Vector<T, SmallVectorSize>::resize(std::size_t size) {
if (m_cap < size) {
expandCap(size);
}
@ -395,8 +451,8 @@ constexpr void Vector<T>::resize(std::size_t size) {
m_size = size;
}
template<typename T>
bool Vector<T>::contains(const T &v) const {
template<typename T, std::size_t SmallVectorSize>
bool Vector<T, SmallVectorSize>::contains(const T &v) const {
for (std::size_t i = 0; i < m_size; i++) {
if (m_items[i] == v) {
return true;
@ -405,8 +461,8 @@ bool Vector<T>::contains(const T &v) const {
return false;
}
template<typename T>
void Vector<T>::insert(std::size_t pos, const T &val) {
template<typename T, std::size_t SmallVectorSize>
void Vector<T, SmallVectorSize>::insert(std::size_t pos, const T &val) {
// TODO: insert should ideally have its own expandCap
if (m_size == m_cap) {
expandCap(m_cap ? m_cap * 2 : 100);
@ -418,9 +474,9 @@ void Vector<T>::insert(std::size_t pos, const T &val) {
++m_size;
}
template<typename T>
template<typename T, std::size_t SmallVectorSize>
template<typename... Args>
void Vector<T>::emplace_back(Args&&... args) {
void Vector<T, SmallVectorSize>::emplace_back(Args&&... args) {
if (m_size == m_cap) {
expandCap(m_cap ? m_cap * 2 : 100);
}
@ -428,8 +484,8 @@ void Vector<T>::emplace_back(Args&&... args) {
++m_size;
}
template<typename T>
void Vector<T>::push_back(const T &item) {
template<typename T, std::size_t SmallVectorSize>
void Vector<T, SmallVectorSize>::push_back(const T &item) {
if (m_size == m_cap) {
expandCap(m_cap ? m_cap * 2 : 100);
}
@ -437,14 +493,14 @@ void Vector<T>::push_back(const T &item) {
++m_size;
}
template<typename T>
void Vector<T>::pop_back() {
template<typename T, std::size_t SmallVectorSize>
void Vector<T, SmallVectorSize>::pop_back() {
--m_size;
m_items[m_size].~T();
}
template<typename T>
Error Vector<T>::erase(std::size_t pos) {
template<typename T, std::size_t SmallVectorSize>
Error Vector<T, SmallVectorSize>::erase(std::size_t pos) {
if (pos >= m_size) {
return OxError(1);
}
@ -455,21 +511,21 @@ Error Vector<T>::erase(std::size_t pos) {
return OxError(0);
}
template<typename T>
Error Vector<T>::unordered_erase(std::size_t pos) {
template<typename T, std::size_t SmallVectorSize>
Error Vector<T, SmallVectorSize>::unordered_erase(std::size_t pos) {
if (pos >= m_size) {
return OxError(1);
}
m_size--;
--m_size;
m_items[pos] = move(m_items[m_size]);
return OxError(0);
}
template<typename T>
void Vector<T>::expandCap(std::size_t cap) {
template<typename T, std::size_t SmallVectorSize>
void Vector<T, SmallVectorSize>::expandCap(std::size_t cap) {
auto oldItems = m_items;
m_cap = cap;
m_items = bit_cast<T*>(new AllocAlias<T>[m_cap]);
this->initItems(&m_items, cap);
if (oldItems) { // move over old items
const auto itRange = cap > m_size ? m_size : cap;
for (std::size_t i = 0; i < itRange; i++) {
@ -478,7 +534,7 @@ void Vector<T>::expandCap(std::size_t cap) {
for (std::size_t i = itRange; i < m_cap; i++) {
new (&m_items[i]) T;
}
delete[] bit_cast<AllocAlias<T>*>(oldItems);
this->clearItems(bit_cast<AllocAlias<T>*>(oldItems));
}
}