[Bf-blender-cvs] [a34761afc03] functions: Reduce wasted space in Function class

Jacques Lucke noreply at git.blender.org
Tue May 28 12:13:29 CEST 2019


Commit: a34761afc03c1866928695c403b6b725615b6788
Author: Jacques Lucke
Date:   Tue May 28 12:11:11 2019 +0200
Branches: functions
https://developer.blender.org/rBa34761afc03c1866928695c403b6b725615b6788

Reduce wasted space in Function class

This is achieved by storing strings in a single buffer instead
of using `std::string`, which does small object optimization
for every string individually. This optimization can be done
because all strings are known when the function is build and
don't change later on.

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

A	source/blender/blenlib/BLI_chained_strings.hpp
M	source/blender/blenlib/CMakeLists.txt
M	source/blender/functions/core/function.cpp
M	source/blender/functions/core/function.hpp
A	tests/gtests/blenlib/BLI_chained_strings_test.cc
M	tests/gtests/blenlib/CMakeLists.txt

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

diff --git a/source/blender/blenlib/BLI_chained_strings.hpp b/source/blender/blenlib/BLI_chained_strings.hpp
new file mode 100644
index 00000000000..01952ad8487
--- /dev/null
+++ b/source/blender/blenlib/BLI_chained_strings.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+/* These classes help storing multiple strings more compactly.
+ * This should only be used when:
+ *   - All strings are freed at the same time.
+ *   - The length of individual strings does not change.
+ *   - All string lengths are known in the beginning. */
+
+#include "BLI_string_ref.hpp"
+#include "BLI_small_vector.hpp"
+
+namespace BLI {
+
+class ChainedStringRef {
+  uint32_t m_start : 24;
+  uint32_t m_size : 8;
+
+ public:
+  ChainedStringRef(uint start, uint size) : m_start(start), m_size(size)
+  {
+    BLI_assert(size < (1 << 8));
+    BLI_assert(start < (1 << 24));
+  }
+
+  uint size() const
+  {
+    return m_size;
+  }
+
+  StringRefNull to_string_ref(const char *ptr) const
+  {
+    return StringRefNull(ptr + m_start, m_size);
+  }
+};
+
+class ChainedStringsBuilder {
+ public:
+  ChainedStringRef add(StringRef str)
+  {
+    ChainedStringRef ref(m_chars.size(), str.size());
+    m_chars.extend(str.data(), str.size());
+    m_chars.append(0);
+    return ref;
+  }
+
+  char *build()
+  {
+    char *ptr = (char *)MEM_mallocN(m_chars.size(), __func__);
+    memcpy(ptr, m_chars.begin(), m_chars.size());
+    return ptr;
+  }
+
+ private:
+  SmallVector<char, 64> m_chars;
+};
+
+};  // namespace BLI
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index b6b4a3e7647..5eef0fe93bd 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -236,6 +236,7 @@ set(SRC
 
   BLI_array_lookup.hpp
   BLI_array_ref.hpp
+  BLI_chained_strings.hpp
   BLI_composition.hpp
   BLI_lazy_init.hpp
   BLI_listbase_wrapper.hpp
diff --git a/source/blender/functions/core/function.cpp b/source/blender/functions/core/function.cpp
index 85c2543c4f8..e2d63c85619 100644
--- a/source/blender/functions/core/function.cpp
+++ b/source/blender/functions/core/function.cpp
@@ -2,6 +2,11 @@
 
 namespace FN {
 
+Function::~Function()
+{
+  MEM_freeN((void *)m_strings);
+}
+
 void Function::print()
 {
   std::cout << "Function: " << this->name() << std::endl;
@@ -26,20 +31,24 @@ FunctionBuilder::FunctionBuilder()
 
 void FunctionBuilder::add_input(StringRef name, SharedType &type)
 {
-  m_input_names.append(name.to_std_string());
+  auto ref = m_strings_builder.add(name);
+  m_input_names.append(ref);
   m_input_types.append(type);
 }
 
 void FunctionBuilder::add_output(StringRef name, SharedType &type)
 {
-  m_output_names.append(name.to_std_string());
+  auto ref = m_strings_builder.add(name);
+  m_output_names.append(ref);
   m_output_types.append(type);
 }
 
 SharedFunction FunctionBuilder::build(StringRef function_name)
 {
+  auto name_ref = m_strings_builder.add(function_name);
+  char *strings = m_strings_builder.build();
   return SharedFunction::New(
-      function_name, m_input_names, m_input_types, m_output_names, m_output_types);
+      name_ref, m_input_names, m_input_types, m_output_names, m_output_types, strings);
 }
 
 } /* namespace FN */
diff --git a/source/blender/functions/core/function.hpp b/source/blender/functions/core/function.hpp
index dd33846852b..d015dd29199 100644
--- a/source/blender/functions/core/function.hpp
+++ b/source/blender/functions/core/function.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "type.hpp"
+#include "BLI_chained_strings.hpp"
 
 namespace FN {
 
@@ -38,26 +39,28 @@ class Function final : public RefCountedBase {
  public:
   Function(Function &fn) = delete;
 
-  Function(StringRef name,
-           ArrayRef<std::string> input_names,
+  Function(ChainedStringRef name,
+           ArrayRef<ChainedStringRef> input_names,
            ArrayRef<SharedType> input_types,
-           ArrayRef<std::string> output_names,
-           ArrayRef<SharedType> output_types)
-      : m_name(name.to_std_string()),
+           ArrayRef<ChainedStringRef> output_names,
+           ArrayRef<SharedType> output_types,
+           const char *strings)
+      : m_name(name),
         m_input_names(input_names.to_small_vector()),
         m_input_types(input_types.to_small_vector()),
         m_output_names(output_names.to_small_vector()),
-        m_output_types(output_types.to_small_vector())
+        m_output_types(output_types.to_small_vector()),
+        m_strings(strings)
   {
     BLI_assert(m_input_names.size() == m_input_types.size());
     BLI_assert(m_output_names.size() == m_output_types.size());
   }
 
-  ~Function() = default;
+  ~Function();
 
   const StringRefNull name() const
   {
-    return m_name;
+    return m_name.to_string_ref(m_strings);
   }
 
   template<typename T> inline bool has_body() const
@@ -120,12 +123,12 @@ class Function final : public RefCountedBase {
 
   StringRefNull input_name(uint index)
   {
-    return m_input_names[index];
+    return m_input_names[index].to_string_ref(m_strings);
   }
 
   StringRefNull output_name(uint index)
   {
-    return m_output_names[index];
+    return m_output_names[index].to_string_ref(m_strings);
   }
 
   template<typename T> SmallVector<T *> input_extensions() const
@@ -161,14 +164,16 @@ class Function final : public RefCountedBase {
   }
 
  private:
-  const std::string m_name;
+  ChainedStringRef m_name;
   Composition m_bodies;
   mutable std::mutex m_body_mutex;
 
-  SmallVector<std::string> m_input_names;
+  SmallVector<ChainedStringRef> m_input_names;
   SmallVector<SharedType> m_input_types;
-  SmallVector<std::string> m_output_names;
+  SmallVector<ChainedStringRef> m_output_names;
   SmallVector<SharedType> m_output_types;
+
+  const char *m_strings;
 };
 
 using SharedFunction = AutoRefCount<Function>;
@@ -176,9 +181,10 @@ using FunctionPerType = SmallMap<SharedType, SharedFunction>;
 
 class FunctionBuilder {
  private:
-  SmallVector<std::string> m_input_names;
+  ChainedStringsBuilder m_strings_builder;
+  SmallVector<ChainedStringRef> m_input_names;
   SmallVector<SharedType> m_input_types;
-  SmallVector<std::string> m_output_names;
+  SmallVector<ChainedStringRef> m_output_names;
   SmallVector<SharedType> m_output_types;
 
  public:
diff --git a/tests/gtests/blenlib/BLI_chained_strings_test.cc b/tests/gtests/blenlib/BLI_chained_strings_test.cc
new file mode 100644
index 00000000000..ec0ef8691cb
--- /dev/null
+++ b/tests/gtests/blenlib/BLI_chained_strings_test.cc
@@ -0,0 +1,46 @@
+#include "testing/testing.h"
+#include "BLI_chained_strings.hpp"
+
+using BLI::ChainedStringRef;
+using BLI::ChainedStringsBuilder;
+
+TEST(chained_strings, BuildEmpty)
+{
+  ChainedStringsBuilder builder;
+  char *str = builder.build();
+  EXPECT_NE(str, nullptr);
+  MEM_freeN(str);
+}
+
+TEST(chained_strings, BuildSingleString)
+{
+  ChainedStringsBuilder builder;
+  auto ref = builder.add("Hello");
+
+  char *str = builder.build();
+
+  EXPECT_EQ(ref.size(), 5);
+  EXPECT_EQ(ref.to_string_ref(str), "Hello");
+
+  MEM_freeN(str);
+}
+
+TEST(chained_strings, BuildMultiple)
+{
+  ChainedStringsBuilder builder;
+  auto ref1 = builder.add("Why");
+  auto ref2 = builder.add("What");
+  auto ref3 = builder.add("Where");
+
+  char *str = builder.build();
+
+  EXPECT_EQ(ref1.size(), 3);
+  EXPECT_EQ(ref2.size(), 4);
+  EXPECT_EQ(ref3.size(), 5);
+
+  EXPECT_EQ(ref1.to_string_ref(str), "Why");
+  EXPECT_EQ(ref2.to_string_ref(str), "What");
+  EXPECT_EQ(ref3.to_string_ref(str), "Where");
+
+  MEM_freeN(str);
+}
diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt
index 477a99938ee..4e8fe21d0ff 100644
--- a/tests/gtests/blenlib/CMakeLists.txt
+++ b/tests/gtests/blenlib/CMakeLists.txt
@@ -42,6 +42,7 @@ BLENDER_TEST(BLI_array_lookup "bf_blenlib")
 BLENDER_TEST(BLI_array_store "bf_blenlib")
 BLENDER_TEST(BLI_array_utils "bf_blenlib")
 BLENDER_TEST(BLI_array_ref "bf_blenlib")
+BLENDER_TEST(BLI_chained_strings "bf_blenlib")
 BLENDER_TEST(BLI_expr_pylike_eval "bf_blenlib")
 BLENDER_TEST(BLI_edgehash "bf_blenlib")
 BLENDER_TEST(BLI_ghash "bf_blenlib")



More information about the Bf-blender-cvs mailing list