Massive performance improvement on correctBlockNodeIds
authorLoic Blot <loic.blot@unix-experience.fr>
Wed, 26 Jul 2017 20:54:55 +0000 (22:54 +0200)
committerLoïc Blot <nerzhul@users.noreply.github.com>
Thu, 27 Jul 2017 05:56:48 +0000 (07:56 +0200)
correctBlockNodeIds does 2 lookups for each loaded node, one to translate DB ID to name and a second to translate name to real ID. Name to real ID is very consumming if done on every node. As mapblocks are in most cases composed of many identical adjacent nodes, cache previous source and destination id and use them on the next node to prevent any lookup on those maps.

This reduce the function load from 15% of my CPU usage to ~0.7%, on the test, calls was reduced from 2.5M lookups to 42k lookups, it's a huge performance gain

src/mapblock.cpp

index 910e91316b294558c03ad2d74921c6e95f989d56..f52a5ee3fabdf488cd65e84f38a24e5bebf378a2 100644 (file)
@@ -494,22 +494,44 @@ static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
        // correct ids.
        std::unordered_set<content_t> unnamed_contents;
        std::unordered_set<std::string> unallocatable_contents;
+
+       bool previous_was_found = false;
+       content_t previous_local_id = CONTENT_IGNORE;
+       content_t previous_global_id = CONTENT_IGNORE;
+
        for (u32 i = 0; i < MapBlock::nodecount; i++) {
                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
+               // This permits to massively improve loading performance when nodes are similar
+               // example: default:air, default:stone are massively present
+               if (previous_was_found && local_id == previous_local_id) {
+                       nodes[i].setContent(previous_global_id);
+                       continue;
+               }
+
                std::string name;
                if (!nimap->getName(local_id, name)) {
                        unnamed_contents.insert(local_id);
+                       previous_was_found = false;
                        continue;
                }
+
                content_t global_id;
                if (!nodedef->getId(name, global_id)) {
                        global_id = gamedef->allocateUnknownNodeId(name);
-                       if(global_id == CONTENT_IGNORE){
+                       if (global_id == CONTENT_IGNORE) {
                                unallocatable_contents.insert(name);
+                               previous_was_found = false;
                                continue;
                        }
                }
                nodes[i].setContent(global_id);
+
+               // Save previous node local_id & global_id result
+               previous_local_id = local_id;
+               previous_global_id = global_id;
+               previous_was_found = true;
        }
 
        for (const content_t c: unnamed_contents) {