diff --git a/README.md b/README.md
index 66e394fc1..4875a4d02 100644
--- a/README.md
+++ b/README.md
@@ -94,14 +94,34 @@ drop-in replacement for it with a minimal effort.
### Performance
As it stands right now, `EnTT` is just fast enough for my requirements if compared to my first choice (that was already
-amazingly fast):
+amazingly fast).
+These are the results of the twos when compiled with GCC 6.3:
-| Benchmark | EntityX (master) | EntityX (experimental/compile_time) | EnTT |
+| Benchmark | | EntityX (experimental/compile_time) | EnTT |
|-----------|-------------|-------------|-------------|
-| Creating 10M entities | 0.281481s | 0.213988s | **0.00542235s** |
-| Destroying 10M entities | 0.166156s | 0.0673857s | **0.0582367s** |
-| Iterating over 10M entities, unpacking one component | 0.047039s | 0.0297941s | **9.3e-08s** |
-| Iterating over 10M entities, unpacking two components | 0.0701693s | 0.0412988s | **0.0206747s** |
+| Creating 10M entities | 0.187042s | **0.0928331s** |
+| Destroying 10M entities | 0.0735151s | **0.060166s** |
+| Iterating over 10M entities, unpacking one component | 0.00784801s | **1.02e-07s** |
+| Iterating over 10M entities, unpacking two components | 0.00865273s | **0.00326714s** |
+| Iterating over 10M entities, unpacking five components | 0.0122006s | **0.00323354s** |
+| Iterating over 10M entities, unpacking ten components | 0.0100089s | **0.00323615s** |
+| Iterating over 50M entities, unpacking one component | 0.0394404s | **1.14e-07s** |
+| Iterating over 50M entities, unpacking two components | 0.0400407s | **0.0179783s** |
+
+These are the results of the twos when compiled with Clang 3.8.1:
+
+| Benchmark | | EntityX (experimental/compile_time) | EnTT |
+|-----------|-------------|-------------|-------------|
+| Creating 10M entities | 0.268049s | **0.0899998s** |
+| Destroying 10M entities | **0.0713912s** | 0.078663s |
+| Iterating over 10M entities, unpacking one component | 0.00863192s | **3.05e-07s** |
+| Iterating over 10M entities, unpacking two components | 0.00780158s | **2.5434e-05s** |
+| Iterating over 10M entities, unpacking five components | 0.00829669s | **2.5497e-05s** |
+| Iterating over 10M entities, unpacking ten components | 0.00789789s | **2.5563e-05s** |
+| Iterating over 50M entities, unpacking one component | 0.0423244s | **1.94e-07s** |
+| Iterating over 50M entities, unpacking two components | 0.0435464s | **0.00012661s** |
+
+I don't know what Clang does to squeeze out of `EnTT` the performance above, but I'd say that it does it incredibly well.
See [benchmark.cpp](https://github.com/skypjack/entt/blob/master/test/benchmark.cpp) for further details.
Of course, I'll try to get out of it more features and better performance anyway in the future, mainly for fun.
@@ -172,6 +192,7 @@ Once you have created a registry, the followings are the exposed member function
* `size`: returns the number of entities still alive.
* `capacity`: returns the maximum number of entities created till now.
+* `valid`: returns true if the entity is still in use, false otherwise.
* `empty`: returns `true` if at least an instance of `Component` exists, `false` otherwise.
* `empty`: returns `true` if all the entities have been destroyed, `false` otherwise.
* `create`: creates a new entity and assigns it the given components, then returns the entity.
diff --git a/src/ident.hpp b/src/ident.hpp
new file mode 100644
index 000000000..7ce660f9c
--- /dev/null
+++ b/src/ident.hpp
@@ -0,0 +1,43 @@
+#ifndef ENTT_IDENT_HPP
+#define ENTT_IDENT_HPP
+
+
+#include
+#include
+#include
+
+
+namespace entt {
+
+
+namespace details {
+
+
+template
+struct Wrapper {
+ using type = Type;
+ constexpr Wrapper(std::size_t index): index{index} {}
+ const std::size_t index;
+};
+
+template
+struct Identifier final: Wrapper... {
+ template
+ constexpr Identifier(std::index_sequence): Wrapper{Indexes}... {}
+
+ template
+ constexpr std::size_t get() const { return Wrapper>::index; }
+};
+
+
+}
+
+
+template
+constexpr auto ident = details::Identifier...>{std::make_index_sequence{}};
+
+
+}
+
+
+#endif // ENTT_IDENT_HPP
diff --git a/src/registry.hpp b/src/registry.hpp
index b4e712f90..cf50fe8d4 100644
--- a/src/registry.hpp
+++ b/src/registry.hpp
@@ -4,10 +4,13 @@
#include
#include
+#include
#include
#include
#include
+#include
#include "component_pool.hpp"
+#include "ident.hpp"
namespace entt {
@@ -21,14 +24,16 @@ template class Pool, typename Entity, typename... Componen
class View, std::tuple, std::tuple> final {
using pool_type = Pool;
using entity_type = typename pool_type::entity_type;
+ using mask_type = std::bitset;
class ViewIterator {
bool valid() const noexcept {
using accumulator_type = bool[];
- bool check = pool.template has(entities[pos-1]);
- accumulator_type types = { true, (check = check && pool.template has(entities[pos-1]))... };
- accumulator_type filters = { true, (check = check && not pool.template has(entities[pos-1]))... };
- return void(types), void(filters), check;
+ auto &bitmask = mask[entities[pos-1]];
+ bool all = bitmask.test(ident.template get());
+ accumulator_type types = { true, (all = all && bitmask.test(ident.template get()))... };
+ accumulator_type filters = { true, (all = all && !bitmask.test(ident.template get()))... };
+ return void(types), void(filters), all;
}
public:
@@ -38,8 +43,8 @@ class View, std::tuple, std::tuplepos) { while(!valid() && --this->pos); }
}
@@ -55,7 +60,7 @@ class View, std::tuple, std::tuple, std::tuple, std::tuple
@@ -89,10 +95,11 @@ public:
template
using view_type = View, std::tuple>;
- explicit View(pool_type &pool) noexcept
+ explicit View(pool_type &pool, mask_type *mask) noexcept
: entities{pool.template entities()},
size{pool.template size()},
- pool{pool}
+ pool{pool},
+ mask{mask}
{
using accumulator_type = int[];
accumulator_type accumulator = { 0, (prefer(), 0)... };
@@ -101,15 +108,15 @@ public:
template
view_type exclude() noexcept {
- return view_type{pool};
+ return view_type{pool, mask};
}
iterator_type begin() const noexcept {
- return ViewIterator{pool, entities, size};
+ return ViewIterator{pool, entities, size, mask};
}
iterator_type end() const noexcept {
- return ViewIterator{pool, entities, 0};
+ return ViewIterator{pool, entities, 0, mask};
}
void reset() noexcept {
@@ -124,6 +131,7 @@ private:
const entity_type *entities;
size_type size;
pool_type &pool;
+ mask_type *mask;
};
@@ -131,6 +139,7 @@ template class Pool, typename Entity, typename... Componen
class View, std::tuple, std::tuple<>> final {
using pool_type = Pool;
using entity_type = typename pool_type::entity_type;
+ using mask_type = std::bitset;
struct ViewIterator {
using value_type = entity_type;
@@ -177,11 +186,13 @@ public:
template
using view_type = View, std::tuple>;
- explicit View(pool_type &pool) noexcept: pool{pool} {}
+ explicit View(pool_type &pool, mask_type *mask) noexcept
+ : pool{pool}, mask{mask}
+ {}
template
view_type exclude() noexcept {
- return view_type{pool};
+ return view_type{pool, mask};
}
iterator_type begin() const noexcept {
@@ -198,6 +209,7 @@ public:
private:
pool_type &pool;
+ mask_type *mask;
};
@@ -207,33 +219,29 @@ class Registry;
template class Pool, typename Entity, typename... Components>
class Registry> {
- using pool_type = Pool;
-
-public:
static_assert(sizeof...(Components) > 1, "!");
+ using pool_type = Pool;
+ using mask_type = std::bitset;
+
+ static constexpr auto validity_bit = sizeof...(Components);
+
+public:
using entity_type = typename pool_type::entity_type;
- using size_type = typename std::vector::size_type;
+ using size_type = typename std::vector::size_type;
private:
- template
- void destroy(entity_type entity) {
- if(pool.template has(entity)) {
- pool.template destroy(entity);
- }
- }
-
template
void clone(entity_type to, entity_type from) {
- if(pool.template has(from)) {
- pool.template construct(to, pool.template get(from));
+ if(entities[from].test(ident.template get())) {
+ assign(to, pool.template get(from));
}
}
template
void sync(entity_type to, entity_type from) {
- bool src = pool.template has(from);
- bool dst = pool.template has(to);
+ bool src = entities[from].test(ident.template get());
+ bool dst = entities[to].test(ident.template get());
if(src && dst) {
copy(to, from);
@@ -250,7 +258,7 @@ public:
template
Registry(Args&&... args)
- : count{0}, pool{std::forward(args)...}
+ : pool{std::forward(args)...}
{}
Registry(const Registry &) = delete;
@@ -260,11 +268,11 @@ public:
Registry & operator=(Registry &&) = delete;
size_type size() const noexcept {
- return count - available.size();
+ return entities.size() - available.size();
}
size_type capacity() const noexcept {
- return count;
+ return entities.size();
}
template
@@ -273,7 +281,11 @@ public:
}
bool empty() const noexcept {
- return available.size() == count;
+ return entities.empty();
+ }
+
+ bool valid(entity_type entity) const noexcept {
+ return (entity < entities.size() && entities[entity].test(validity_bit));
}
template
@@ -289,37 +301,48 @@ public:
entity_type entity;
if(available.empty()) {
- entity = count++;
+ entity = entity_type(entities.size());
+ entities.emplace_back();
} else {
entity = available.back();
available.pop_back();
}
+ entities[entity].set(validity_bit);
+
return entity;
}
void destroy(entity_type entity) {
+ assert(valid(entity));
using accumulator_type = int[];
- accumulator_type accumulator = { 0, (destroy(entity), 0)... };
- (void)accumulator;
+ accumulator_type accumulator = { 0, (reset(entity), 0)... };
available.push_back(entity);
+ entities[entity].reset();
+ (void)accumulator;
}
template
Comp & assign(entity_type entity, Args... args) {
+ assert(valid(entity));
+ entities[entity].set(ident.template get());
return pool.template construct(entity, args...);
}
template
void remove(entity_type entity) {
+ assert(valid(entity));
+ entities[entity].reset(ident.template get());
pool.template destroy(entity);
}
template
bool has(entity_type entity) const noexcept {
+ assert(valid(entity));
using accumulator_type = bool[];
bool all = true;
- accumulator_type accumulator = { true, (all = all && pool.template has(entity))... };
+ auto &mask = entities[entity];
+ accumulator_type accumulator = { true, (all = all && mask.test(ident.template get()))... };
(void)accumulator;
return all;
}
@@ -341,14 +364,17 @@ public:
template
Comp & accomodate(entity_type entity, Args... args) {
- return (pool.template has(entity)
+ assert(valid(entity));
+
+ return (entities[entity].test(ident.template get())
? this->template replace(entity, std::forward(args)...)
: this->template assign(entity, std::forward(args)...));
}
entity_type clone(entity_type from) {
- auto to = create();
+ assert(valid(from));
using accumulator_type = int[];
+ auto to = create();
accumulator_type accumulator = { 0, (clone(to, from), 0)... };
(void)accumulator;
return to;
@@ -367,30 +393,36 @@ public:
template
void reset(entity_type entity) {
- if(pool.template has(entity)) {
- pool.template destroy(entity);
+ assert(valid(entity));
+
+ if(entities[entity].test(ident.template get())) {
+ remove(entity);
}
}
template
void reset() {
- pool.template reset();
+ for(entity_type entity = 0, last = entity_type(entities.size()); entity < last; ++entity) {
+ if(entities[entity].test(ident.template get())) {
+ remove(entity);
+ }
+ }
}
void reset() {
+ entities.clear();
available.clear();
- count = 0;
pool.reset();
}
template
view_type view() noexcept {
- return view_type{pool};
+ return view_type{pool, entities.data()};
}
private:
+ std::vector entities;
std::vector available;
- entity_type count;
pool_type pool;
};
diff --git a/test/registry.cpp b/test/registry.cpp
index 2d488b79d..aa69708fe 100644
--- a/test/registry.cpp
+++ b/test/registry.cpp
@@ -105,6 +105,7 @@ TEST(DefaultRegistry, Functionalities) {
ASSERT_TRUE(registry.empty());
e1 = registry.create();
+ e2 = registry.create();
ASSERT_NO_THROW(registry.reset(e1));
ASSERT_NO_THROW(registry.reset(e2));