[Bf-blender-cvs] [6374644fd11] master: DNA: optimize struct reconstruction by doing some preprocessing

Jacques Lucke noreply at git.blender.org
Tue Sep 29 12:31:23 CEST 2020


Commit: 6374644fd11797806ab2e92341e5e7d32e94067b
Author: Jacques Lucke
Date:   Tue Sep 29 12:29:01 2020 +0200
Branches: master
https://developer.blender.org/rB6374644fd11797806ab2e92341e5e7d32e94067b

DNA: optimize struct reconstruction by doing some preprocessing

When loading large files that are more than a couple weeks old
(such that DNA has changed in that time), a significant amount of
time is spent in `DNA_struct_reconstruct`. This function takes a struct
in the old layout and creates a struct in the new layout from it.

This was slow because it was computing the diff between the struct
layouts every time a struct is updated. Now the steps for the struct
reconstruction is computed only once per struct. This information is
then used to actually reconstruct all structs that changed.

I measured about 10-20% speedup when loading Spring files.
E.g. `10.6s -> 8.7s` for `06_055_A.anim.blend` in BKE_blendfile_read`.

This percentage varies a lot based on the number of blocks that have
to be reconstructed and how much DNA has changed since they have
been written. In none of my tests was the new code slower than the
old code.

Reviewers: campbellbarton

Differential Revision: https://developer.blender.org/D8959

===================================================================

M	source/blender/blenloader/intern/readfile.c
M	source/blender/blenloader/intern/readfile.h
M	source/blender/makesdna/DNA_genfile.h
M	source/blender/makesdna/DNA_sdna_types.h
M	source/blender/makesdna/intern/dna_genfile.c

===================================================================

diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 19fab73b259..bd944ac32ac 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -779,7 +779,7 @@ static void bh4_from_bh8(BHead *bhead, BHead8 *bhead8, int do_endian_swap)
      * 0x0000000000000000000012345678 would become 0x12345678000000000000000000000000
      */
     if (do_endian_swap) {
-      BLI_endian_switch_int64(&bhead8->old);
+      BLI_endian_switch_uint64(&bhead8->old);
     }
 
     /* this patch is to avoid a long long being read from not-eight aligned positions
@@ -1114,6 +1114,8 @@ static bool read_file_dna(FileData *fd, const char **r_error_message)
       if (fd->filesdna) {
         blo_do_versions_dna(fd->filesdna, fd->fileversion, subversion);
         fd->compflags = DNA_struct_get_compareflags(fd->filesdna, fd->memsdna);
+        fd->reconstruct_info = DNA_reconstruct_info_create(
+            fd->filesdna, fd->memsdna, fd->compflags);
         /* used to retrieve ID names from (bhead+1) */
         fd->id_name_offs = DNA_elem_offset(fd->filesdna, "ID", "char", "name[]");
 
@@ -1610,6 +1612,9 @@ void blo_filedata_free(FileData *fd)
     if (fd->compflags) {
       MEM_freeN((void *)fd->compflags);
     }
+    if (fd->reconstruct_info) {
+      DNA_reconstruct_info_free(fd->reconstruct_info);
+    }
 
     if (fd->datamap) {
       oldnewmap_free(fd->datamap);
@@ -2177,8 +2182,7 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname)
           }
         }
 #endif
-        temp = DNA_struct_reconstruct(
-            fd->memsdna, fd->filesdna, fd->compflags, bh->SDNAnr, bh->nr, (bh + 1));
+        temp = DNA_struct_reconstruct(fd->reconstruct_info, bh->SDNAnr, bh->nr, (bh + 1));
       }
       else {
         /* SDNA_CMP_EQUAL */
@@ -9135,7 +9139,7 @@ static void convert_pointer_array_32_to_64(BlendDataReader *UNUSED(reader),
                                            const uint32_t *src,
                                            uint64_t *dst)
 {
-  /* Match pointer conversion rules from bh8_from_bh4 and cast_pointer. */
+  /* Match pointer conversion rules from bh8_from_bh4 and cast_pointer_32_to_64. */
   for (int i = 0; i < array_size; i++) {
     dst[i] = src[i];
   }
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index d43f7ded50e..2ddf96a2d47 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -106,6 +106,7 @@ typedef struct FileData {
   const struct SDNA *memsdna;
   /** Array of #eSDNA_StructCompare. */
   const char *compflags;
+  struct DNA_ReconstructInfo *reconstruct_info;
 
   int fileversion;
   /** Used to retrieve ID names from (bhead+1). */
diff --git a/source/blender/makesdna/DNA_genfile.h b/source/blender/makesdna/DNA_genfile.h
index 1cdaba81ffa..b09b6dcd8e4 100644
--- a/source/blender/makesdna/DNA_genfile.h
+++ b/source/blender/makesdna/DNA_genfile.h
@@ -78,6 +78,8 @@ enum eSDNA_StructCompare {
   /* Struct is different in some way
    * (needs to be copied/converted field by field) */
   SDNA_CMP_NOT_EQUAL = 2,
+  /* This is only used temporarily by #DNA_struct_get_compareflags. */
+  SDNA_CMP_UNKNOWN = 3,
 };
 
 struct SDNA *DNA_sdna_from_data(const void *data,
@@ -93,13 +95,17 @@ void DNA_sdna_current_init(void);
 const struct SDNA *DNA_sdna_current_get(void);
 void DNA_sdna_current_free(void);
 
+struct DNA_ReconstructInfo;
+struct DNA_ReconstructInfo *DNA_reconstruct_info_create(const struct SDNA *oldsdna,
+                                                        const struct SDNA *newsdna,
+                                                        const char *compare_flags);
+void DNA_reconstruct_info_free(struct DNA_ReconstructInfo *reconstruct_info);
+
 int DNA_struct_find_nr_ex(const struct SDNA *sdna, const char *str, unsigned int *index_last);
 int DNA_struct_find_nr(const struct SDNA *sdna, const char *str);
 void DNA_struct_switch_endian(const struct SDNA *oldsdna, int oldSDNAnr, char *data);
 const char *DNA_struct_get_compareflags(const struct SDNA *sdna, const struct SDNA *newsdna);
-void *DNA_struct_reconstruct(const struct SDNA *newsdna,
-                             const struct SDNA *oldsdna,
-                             const char *compflags,
+void *DNA_struct_reconstruct(const struct DNA_ReconstructInfo *reconstruct_info,
                              int oldSDNAnr,
                              int blocks,
                              const void *data);
diff --git a/source/blender/makesdna/DNA_sdna_types.h b/source/blender/makesdna/DNA_sdna_types.h
index 3af12ae791f..05f86e32961 100644
--- a/source/blender/makesdna/DNA_sdna_types.h
+++ b/source/blender/makesdna/DNA_sdna_types.h
@@ -108,13 +108,13 @@ typedef struct BHead {
 #
 typedef struct BHead4 {
   int code, len;
-  int old;
+  uint old;
   int SDNAnr, nr;
 } BHead4;
 #
 #
 typedef struct BHead8 {
   int code, len;
-  int64_t old;
+  uint64_t old;
   int SDNAnr, nr;
 } BHead8;
diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c
index b140a847188..74f587fe032 100644
--- a/source/blender/makesdna/intern/dna_genfile.c
+++ b/source/blender/makesdna/intern/dna_genfile.c
@@ -611,34 +611,89 @@ void DNA_sdna_current_free(void)
 /* ******************* HANDLE DNA ***************** */
 
 /**
- * Used by #DNA_struct_get_compareflags (below) to recursively mark all structs
- * containing a field of type structnr as changed between old and current SDNAs.
+ * This function changes compare_flags[old_struct_index] from SDNA_CMP_UNKNOWN to something else.
+ * It might call itself recursively.
  */
-static void recurs_test_compflags(const SDNA *sdna, char *compflags, int structnr)
+static void set_compare_flags_for_struct(const SDNA *oldsdna,
+                                         const SDNA *newsdna,
+                                         char *compare_flags,
+                                         const int old_struct_index)
 {
-  /* check all structs, test if it's inside another struct */
-  const int typenr = sdna->structs[structnr]->type;
-
-  for (int a = 0; a < sdna->structs_len; a++) {
-    if (a != structnr && compflags[a] == SDNA_CMP_EQUAL) {
-      SDNA_Struct *struct_info = sdna->structs[a];
-      for (int b = 0; b < struct_info->members_len; b++) {
-        SDNA_StructMember *member = &struct_info->members[b];
-        if (member->type == typenr) {
-          const char *member_name = sdna->names[member->name];
-          if (!ispointer(member_name)) {
-            compflags[a] = SDNA_CMP_NOT_EQUAL;
-            recurs_test_compflags(sdna, compflags, a);
-          }
+  if (compare_flags[old_struct_index] != SDNA_CMP_UNKNOWN) {
+    /* This flag has been initialized already. */
+    return;
+  }
+
+  SDNA_Struct *old_struct = oldsdna->structs[old_struct_index];
+  const char *struct_name = oldsdna->types[old_struct->type];
+
+  const int new_struct_index = DNA_struct_find_nr(newsdna, struct_name);
+  if (new_struct_index == -1) {
+    /* Didn't find a matching new struct, so it has been removed. */
+    compare_flags[old_struct_index] = SDNA_CMP_REMOVED;
+    return;
+  }
+
+  SDNA_Struct *new_struct = newsdna->structs[new_struct_index];
+  if (old_struct->members_len != new_struct->members_len) {
+    /* Structs with a different amount of members are not equal. */
+    compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+    return;
+  }
+  if (oldsdna->types_size[old_struct->type] != newsdna->types_size[new_struct->type]) {
+    /* Structs that don't have the same size are not equal. */
+    compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+    return;
+  }
+
+  /* Compare each member individually. */
+  for (int member_index = 0; member_index < old_struct->members_len; member_index++) {
+    SDNA_StructMember *old_member = &old_struct->members[member_index];
+    SDNA_StructMember *new_member = &new_struct->members[member_index];
+
+    const char *old_type_name = oldsdna->types[old_member->type];
+    const char *new_type_name = newsdna->types[new_member->type];
+    if (!STREQ(old_type_name, new_type_name)) {
+      /* If two members have a different type in the same place, the structs are not equal. */
+      compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+      return;
+    }
+
+    const char *old_member_name = oldsdna->names[old_member->name];
+    const char *new_member_name = newsdna->names[new_member->name];
+    if (!STREQ(old_member_name, new_member_name)) {
+      /* If two members have a different name in the same place, the structs are not equal. */
+      compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+      return;
+    }
+
+    if (ispointer(old_member_name)) {
+      if (oldsdna->pointer_size != newsdna->pointer_size) {
+        /* When the struct contains a pointer, and the pointer sizes differ, the structs are not
+         * equal. */
+        compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+        return;
+      }
+    }
+    else {
+      const int old_member_struct_index = DNA_struct_find_nr(oldsdna, old_type_name);
+      if (old_member_struct_index >= 0) {
+        set_compare_flags_for_struct(oldsdna, newsdna, compare_flags, old_member_struct_index);
+        if (compare_flags[old_member_struct_index] != SDNA_CMP_EQUAL) {
+          /* If an embedded struct is not equal, the parent struct cannot be equal either. */
+          compare_flags[old_struct_index] = SDNA_CMP_NOT_EQUAL;
+          return;
         }
       }
     }
   }
+
+  compare_flags[old_struct_index] = SDNA_CMP_EQUAL;
 }
 
 /**
  * Constructs and returns an array of byte flags with one element for each struct in oldsdna,
- * indicating how it compares to newsdna:
+ * indicating how it compares to newsdna.
  */
 const char *DNA_struct_get_compareflags(const SDNA *oldsdna, const SDNA *newsdna)
 {
@@ -647,130 +702,31 @@ const char *DNA_struct_get_compareflags(const SDNA *oldsdna, const SDNA *newsdna
     return NULL;
   }
 
-  char *compflags = MEM_callocN(oldsdna->structs_len, "compflags");
-
-  /* we check all structs in 'oldsdna' and compare them wit

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list