mirror of
https://github.com/luanti-org/luanti.git
synced 2025-07-02 16:38:41 +00:00
Optimize name-id-lookup for MapBlock serialization (#16000)
This commit is contained in:
parent
75862e33b6
commit
60c47c51e0
1 changed files with 59 additions and 27 deletions
|
@ -24,6 +24,55 @@
|
||||||
#include "util/serialize.h"
|
#include "util/serialize.h"
|
||||||
#include "util/basic_macros.h"
|
#include "util/basic_macros.h"
|
||||||
|
|
||||||
|
// Like a std::unordered_map<content_t, content_t>, but faster.
|
||||||
|
//
|
||||||
|
// Unassigned entries are marked with 0xFFFF.
|
||||||
|
//
|
||||||
|
// The static memory requires about 65535 * 2 bytes RAM in order to be
|
||||||
|
// sure we can handle all content ids.
|
||||||
|
class IdIdMapping
|
||||||
|
{
|
||||||
|
static_assert(sizeof(content_t) == 2, "content_t must be 16-bit");
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<content_t[]> m_mapping;
|
||||||
|
std::vector<content_t> m_dirty;
|
||||||
|
|
||||||
|
public:
|
||||||
|
IdIdMapping()
|
||||||
|
{
|
||||||
|
m_mapping = std::make_unique<content_t[]>(CONTENT_MAX + 1);
|
||||||
|
memset(m_mapping.get(), 0xFF, (CONTENT_MAX + 1) * sizeof(content_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
DISABLE_CLASS_COPY(IdIdMapping)
|
||||||
|
|
||||||
|
content_t get(content_t k) const
|
||||||
|
{
|
||||||
|
return m_mapping[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(content_t k, content_t v)
|
||||||
|
{
|
||||||
|
m_mapping[k] = v;
|
||||||
|
m_dirty.push_back(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
for (auto k : m_dirty)
|
||||||
|
m_mapping[k] = 0xFFFF;
|
||||||
|
m_dirty.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static IdIdMapping &giveClearedThreadLocalInstance()
|
||||||
|
{
|
||||||
|
static thread_local IdIdMapping tl_ididmapping;
|
||||||
|
tl_ididmapping.clear();
|
||||||
|
return tl_ididmapping;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static const char *modified_reason_strings[] = {
|
static const char *modified_reason_strings[] = {
|
||||||
"reallocate or initial",
|
"reallocate or initial",
|
||||||
"setIsUnderground",
|
"setIsUnderground",
|
||||||
|
@ -212,16 +261,7 @@ void MapBlock::expireIsAirCache()
|
||||||
static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
|
static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
|
||||||
const NodeDefManager *nodedef)
|
const NodeDefManager *nodedef)
|
||||||
{
|
{
|
||||||
// The static memory requires about 65535 * 2 bytes RAM in order to be
|
IdIdMapping &mapping = IdIdMapping::giveClearedThreadLocalInstance();
|
||||||
// sure we can handle all content ids. But it's absolutely worth it as it's
|
|
||||||
// a speedup of 4 for one of the major time consuming functions on storing
|
|
||||||
// mapblocks.
|
|
||||||
thread_local std::unique_ptr<content_t[]> mapping;
|
|
||||||
static_assert(sizeof(content_t) == 2, "content_t must be 16-bit");
|
|
||||||
if (!mapping)
|
|
||||||
mapping = std::make_unique<content_t[]>(CONTENT_MAX + 1);
|
|
||||||
|
|
||||||
memset(mapping.get(), 0xFF, (CONTENT_MAX + 1) * sizeof(content_t));
|
|
||||||
|
|
||||||
content_t id_counter = 0;
|
content_t id_counter = 0;
|
||||||
for (u32 i = 0; i < MapBlock::nodecount; i++) {
|
for (u32 i = 0; i < MapBlock::nodecount; i++) {
|
||||||
|
@ -229,12 +269,12 @@ static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
|
||||||
content_t id = CONTENT_IGNORE;
|
content_t id = CONTENT_IGNORE;
|
||||||
|
|
||||||
// Try to find an existing mapping
|
// Try to find an existing mapping
|
||||||
if (mapping[global_id] != 0xFFFF) {
|
if (auto found = mapping.get(global_id); found != 0xFFFF) {
|
||||||
id = mapping[global_id];
|
id = found;
|
||||||
} else {
|
} else {
|
||||||
// We have to assign a new mapping
|
// We have to assign a new mapping
|
||||||
id = id_counter++;
|
id = id_counter++;
|
||||||
mapping[global_id] = id;
|
mapping.set(global_id, id);
|
||||||
|
|
||||||
const auto &name = nodedef->get(global_id).name;
|
const auto &name = nodedef->get(global_id).name;
|
||||||
nimap->set(id, name);
|
nimap->set(id, name);
|
||||||
|
@ -259,25 +299,20 @@ static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
|
||||||
std::unordered_set<content_t> unnamed_contents;
|
std::unordered_set<content_t> unnamed_contents;
|
||||||
std::unordered_set<std::string> unallocatable_contents;
|
std::unordered_set<std::string> unallocatable_contents;
|
||||||
|
|
||||||
bool previous_exists = false;
|
// Used to cache local to global id lookup.
|
||||||
content_t previous_local_id = CONTENT_IGNORE;
|
IdIdMapping &mapping_cache = IdIdMapping::giveClearedThreadLocalInstance();
|
||||||
content_t previous_global_id = CONTENT_IGNORE;
|
|
||||||
|
|
||||||
for (u32 i = 0; i < MapBlock::nodecount; i++) {
|
for (u32 i = 0; i < MapBlock::nodecount; i++) {
|
||||||
content_t local_id = nodes[i].getContent();
|
content_t local_id = nodes[i].getContent();
|
||||||
// If previous node local_id was found and same than before, don't lookup maps
|
|
||||||
// apply directly previous resolved id
|
if (auto found = mapping_cache.get(local_id); found != 0xFFFF) {
|
||||||
// This permits to massively improve loading performance when nodes are similar
|
nodes[i].setContent(found);
|
||||||
// example: default:air, default:stone are massively present
|
|
||||||
if (previous_exists && local_id == previous_local_id) {
|
|
||||||
nodes[i].setContent(previous_global_id);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
if (!nimap->getName(local_id, name)) {
|
if (!nimap->getName(local_id, name)) {
|
||||||
unnamed_contents.insert(local_id);
|
unnamed_contents.insert(local_id);
|
||||||
previous_exists = false;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,16 +321,13 @@ static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
|
||||||
global_id = gamedef->allocateUnknownNodeId(name);
|
global_id = gamedef->allocateUnknownNodeId(name);
|
||||||
if (global_id == CONTENT_IGNORE) {
|
if (global_id == CONTENT_IGNORE) {
|
||||||
unallocatable_contents.insert(name);
|
unallocatable_contents.insert(name);
|
||||||
previous_exists = false;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nodes[i].setContent(global_id);
|
nodes[i].setContent(global_id);
|
||||||
|
|
||||||
// Save previous node local_id & global_id result
|
// Save previous node local_id & global_id result
|
||||||
previous_local_id = local_id;
|
mapping_cache.set(local_id, global_id);
|
||||||
previous_global_id = global_id;
|
|
||||||
previous_exists = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const content_t c: unnamed_contents) {
|
for (const content_t c: unnamed_contents) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue