Skip to content

Commit

Permalink
feat: Arraylist, fast new container to add front and back (opentibiab…
Browse files Browse the repository at this point in the history
…r#1677)

Arraylist is a very fast container for adding to the front and back, it
uses two vectors to do this juggling and doesn't allow you to remove the
front, as it is slow, use std::list for this case.

**Benchmark**
loop: 99999

![image](https://github.com/opentibiabr/canary/assets/2267386/ebdc1883-0249-413a-8553-8c5dab0b7ac8)
loop: 999999

![image](https://github.com/opentibiabr/canary/assets/2267386/6d0c46b3-ff6e-429a-8ae7-9cf015ed142e)

<details>

```c++
constexpr size_t LOOP = 999999;

Benchmark bm;
for (size_t i = 0; i < 999; i++) {
	stdext::arraylist<uint32_t> list;
	list.reserve(LOOP);

	for (size_t d = 0; d < LOOP; d++) {
		if (d % 2) {
			list.emplace_front(d);
		} else {
			list.emplace_back(d);
		}
	}
	for (const auto &v : list) { }
}

g_logger().info("stdext::arraylist + reserve: {}ms", bm.duration());

bm.reset();
bm.start();

for (size_t i = 0; i < 999; i++) {
	stdext::arraylist<uint32_t> list;
	for (size_t d = 0; d < LOOP; d++) {
		if (d % 2) {
			list.emplace_front(d);
		} else {
			list.emplace_back(d);
		}
	}
	for (const auto &v : list) { }
}

g_logger().info("stdext::arraylist: {}ms", bm.duration());

bm.reset();
bm.start();

for (size_t i = 0; i < 999; i++) {
	std::list<uint32_t> list;
	for (size_t d = 0; d < LOOP; d++) {
		if (d % 2) {
			list.emplace_front(d);
		} else {
			list.emplace_back(d);
		}
	}
	for (const auto &v : list) { }
}

g_logger().info("std::list: {}ms", bm.duration());

bm.reset();
bm.start();

for (size_t i = 0; i < 999; i++) {
	std::deque<uint32_t> list;
	for (size_t d = 0; d < LOOP; d++) {
		if (d % 2) {
			list.emplace_front(d);
		} else {
			list.emplace_back(d);
		}
	}
	for (const auto &v : list) { }
}

g_logger().info("std::deque: {}ms", bm.duration());

g_logger().info("--- stdext::arraylist vs std::forward_list vs std::list vs std::deque --- (push_front)");

bm.reset();
bm.start();

for (size_t i = 0; i < 999; i++) {
	stdext::arraylist<uint32_t> list;
	list.reserve(LOOP);
	for (size_t d = 0; d < LOOP; d++) {
		list.emplace_front(d);
	}
	for (const auto &v : list) { }
}

g_logger().info("stdext::arraylist + reserve: {}ms", bm.duration());

bm.reset();
bm.start();

for (size_t i = 0; i < 999; i++) {
	stdext::arraylist<uint32_t> arraylist;
	for (size_t d = 0; d < LOOP; d++) {
		arraylist.emplace_front(d);
	}
	for (const auto &v : arraylist) { }
}

g_logger().info("stdext::arraylist: {}ms", bm.duration());

bm.reset();
bm.start();

for (size_t i = 0; i < 999; i++) {
	std::forward_list<uint32_t> list;
	for (size_t d = 0; d < LOOP; d++) {
		list.emplace_front(d);
	}
	for (const auto &v : list) { }
}

g_logger().info("std::forward_list: {}ms", bm.duration());

bm.reset();
bm.start();

for (size_t i = 0; i < 999; i++) {
	std::list<uint32_t> list;
	for (size_t d = 0; d < LOOP; d++) {
		list.emplace_front(d);
	}
	for (const auto &v : list) { }
}

g_logger().info("std::list: {}ms", bm.duration());

bm.reset();
bm.start();

for (size_t i = 0; i < 999; i++) {
	std::deque<uint32_t> list;
	for (size_t d = 0; d < LOOP; d++) {
		list.emplace_front(d);
	}
	for (const auto &v : list) { }
}

g_logger().info("std::deque: {}ms", bm.duration());
```
</details>

**Validating Content**
<details>

```c++
constexpr size_t LOOP = 999999;
stdext::arraylist<uint32_t> arraylist;
arraylist.reserve(LOOP);

for (size_t d = 0; d < LOOP; d++) {
	if (d % 2) {
		arraylist.emplace_front(d);
	} else {
		arraylist.emplace_back(d);
	}
}

std::deque<uint32_t> deque;
for (size_t d = 0; d < LOOP; d++) {
	if (d % 2) {
		deque.emplace_front(d);
	} else {
		deque.emplace_back(d);
	}
}

for (size_t i = 0; i < LOOP; i++) {
	if (arraylist[i] != deque[i]) {
		g_logger().info("NOT EQUAL");
	}
}

g_logger().info("FINISHED");
```
</details>
  • Loading branch information
mehah authored Oct 19, 2023
1 parent 51eb0b2 commit afcae2c
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/pch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "utils/definitions.hpp"
#include "utils/simd.hpp"
#include "utils/vectorset.hpp"
#include "utils/arraylist.hpp"
#include "utils/vectorsort.hpp"

// --------------------
Expand Down
158 changes: 158 additions & 0 deletions src/utils/arraylist.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* Canary - A free and open-source MMORPG server emulator
* Copyright (©) 2019-2022 OpenTibiaBR <[email protected]>
* Repository: https://github.com/opentibiabr/canary
* License: https://github.com/opentibiabr/canary/blob/main/LICENSE
* Contributors: https://github.com/opentibiabr/canary/graphs/contributors
* Website: https://docs.opentibiabr.com/
*/

#pragma once

#include <algorithm>
#include <vector>

// # Mehah
// Arraylist is a very fast container for adding to the front and back,
// it uses two vectors to do this juggling and doesn't allow you to remove the front, as it is slow,
// use std::list for this case.

namespace stdext {
template <typename T>
class arraylist {
public:
bool contains(const T &v) {
update();
return std::ranges::find(backContainer, v) != backContainer.end();
}

bool erase(const T &v) {
update();

const auto &it = std::ranges::find(backContainer, v);
if (it == backContainer.end()) {
return false;
}
backContainer.erase(it);

return true;
}

bool erase(const size_t begin, const size_t end) {
update();

if (begin > size() || end > size()) {
return false;
}

backContainer.erase(backContainer.begin() + begin, backContainer.begin() + end);
return true;
}

template <class F>
bool erase_if(F fnc) {
update();
return std::erase_if(backContainer, std::move(fnc)) > 0;
}

auto &front() {
update();
return backContainer.front();
}

void pop_back() {
update();
backContainer.pop_back();
}

auto &back() {
update();
return backContainer.back();
}

void push_front(const T &v) {
needUpdate = true;
frontContainer.push_back(v);
}

void push_front(T &&_Val) {
needUpdate = true;
frontContainer.push_back(std::move(_Val));
}

template <class... _Valty>
decltype(auto) emplace_front(_Valty &&... v) {
needUpdate = true;
return frontContainer.emplace_back(std::forward<_Valty>(v)...);
}

void push_back(const T &v) {
backContainer.push_back(v);
}

void push_back(T &&_Val) {
backContainer.push_back(std::move(_Val));
}

template <class... _Valty>
decltype(auto) emplace_back(_Valty &&... v) {
return backContainer.emplace_back(std::forward<_Valty>(v)...);
}

bool empty() const noexcept {
return backContainer.empty() && frontContainer.empty();
}

size_t size() const noexcept {
return backContainer.size() + frontContainer.size();
}

auto begin() noexcept {
update();
return backContainer.begin();
}

auto end() noexcept {
return backContainer.end();
}

void clear() noexcept {
frontContainer.clear();
return backContainer.clear();
}

void reserve(size_t newCap) noexcept {
backContainer.reserve(newCap);
frontContainer.reserve(newCap);
}

const auto &data() noexcept {
update();
return backContainer.data();
}

T &operator[](const size_t i) {
update();
return backContainer[i];
}

private:
inline void update() noexcept {
if (!needUpdate) {
return;
}

needUpdate = false;
std::ranges::reverse(frontContainer);
frontContainer.insert(frontContainer.end(), make_move_iterator(backContainer.begin()), make_move_iterator(backContainer.end()));
backContainer.clear();
backContainer.insert(backContainer.end(), make_move_iterator(frontContainer.begin()), make_move_iterator(frontContainer.end()));
frontContainer.clear();
}

std::vector<T> backContainer;
std::vector<T> frontContainer;

bool needUpdate = false;
};
}
1 change: 1 addition & 0 deletions vcproj/canary.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@
<ClInclude Include="..\src\server\server.hpp" />
<ClInclude Include="..\src\server\server_definitions.hpp" />
<ClInclude Include="..\src\server\signals.hpp" />
<ClInclude Include="..\src\utils\arraylist.hpp" />
<ClInclude Include="..\src\utils\benchmark.hpp" />
<ClInclude Include="..\src\utils\const.hpp" />
<ClInclude Include="..\src\utils\definitions.hpp" />
Expand Down

0 comments on commit afcae2c

Please sign in to comment.