Commit: 9616e2ef78f04b9eb1974f490ba93ca13786fbfe
Author: Ankit Meel
Date:   Sun Aug 9 00:37:52 2020 +0530
Branches: soc-2020-io-performance
https://developer.blender.org/rB9616e2ef78f04b9eb1974f490ba93ca13786fbfe

Use std::string_view for string splitting.

While being slightly more error prone, this change
gives a speedup of 1.9 s vs 1.2 s on a 23.3 MB 8-level
subdivided cube OBJ file. [1] The biggest time sink earlier
was splitting strings into smaller ones. Now it is `std::stof`
about which nothing much can be done.

`StringRef` has been completely removed to avoid
casts between `std::string_view::size_type` & `int64_t`
that `StringRef::size()` gives. Also, `find_{first/last}_{not/}_of`
and `substr` have been used a lot, which are missing in `StrinRef`.

The change also removes `\r\n` carriage return line breaks
while cleaning up the string.

Use `blender::StringRefNull`  where null-terminated strings
are present.

`fprintf` has been replaced with `std::cerr` since
`std::string_view::data()` may give a pointer to a non
null terminated buffer. Also, error logging is not performance
-critical. [2]

[1] See week 7 reports for breakdown on the older timings.
https://wiki.blender.org/wiki/User:Ankitm/GSoC_2020_Daily_Reports#Week_7
[2] https://en.cppreference.com/w/cpp/string/basic_string_view/data

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

M       source/blender/io/wavefront_obj/intern/wavefront_obj_im_file_reader.cc
M       source/blender/io/wavefront_obj/intern/wavefront_obj_im_file_reader.hh
M       source/blender/io/wavefront_obj/intern/wavefront_obj_im_objects.hh

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

diff --git 
a/source/blender/io/wavefront_obj/intern/wavefront_obj_im_file_reader.cc 
b/source/blender/io/wavefront_obj/intern/wavefront_obj_im_file_reader.cc
index 6f81cdb5ad5..3a7e2e49978 100644
--- a/source/blender/io/wavefront_obj/intern/wavefront_obj_im_file_reader.cc
+++ b/source/blender/io/wavefront_obj/intern/wavefront_obj_im_file_reader.cc
@@ -36,41 +36,76 @@
 namespace blender::io::obj {
 
 using std::string;
+using std::string_view;
 
 /**
- * Split the given string by the delimiter and fill the given vector.
- * If an intermediate string is empty, or space or null character, it is not 
appended to the
- * vector.
+ * Split a line string into the first word (key) and the rest of the line.
+ * Also remove leading & trailing space as well as `\r` carriage return
+ * character if present.
  */
-static void split_by_char(const string &in_string, char delimiter, 
Vector<string> &r_out_list)
+static void split_line_key_rest(string_view line,
+                                string_view &r_line_key,
+                                string_view &r_rest_line)
 {
-  std::stringstream stream(in_string);
-  string word{};
-  while (std::getline(stream, word, delimiter)) {
-    if (word.empty() || word[0] == ' ' || word[0] == '\0') {
-      continue;
-    }
-    r_out_list.append(word);
+  if (line.empty()) {
+    return;
+  }
+
+  const string_view::size_type pos{line.find_first_of(' ')};
+  if (pos == string_view::npos) {
+    /* Use the first character if no space is found in the line. It's usually 
a comment like:
+     * #This is a comment. */
+    r_line_key = line.substr(0, 1);
+  }
+  else {
+    r_line_key = line.substr(0, pos);
+  }
+  r_rest_line = line.substr(pos + 1, string_view::npos);
+  if (r_rest_line.empty()) {
+    return;
+  }
+  /* Remove any leading spaces, trailing spaces & \r character, if any. */
+  const string_view::size_type leading_space{r_rest_line.find_first_not_of(' 
')};
+  if (leading_space != string_view::npos) {
+    r_rest_line = r_rest_line.substr(leading_space, string_view::npos);
+  }
+
+  const string_view::size_type 
carriage_return{r_rest_line.find_first_of('\r')};
+  if (carriage_return != string_view::npos) {
+    r_rest_line = r_rest_line.substr(0, carriage_return);
+  }
+
+  const string_view::size_type trailing_space{r_rest_line.find_last_not_of(' 
')};
+  if (trailing_space != string_view::npos) {
+    /* The position is of a character that is not ' ', so count of characters 
is pos + 1. */
+    r_rest_line = r_rest_line.substr(0, trailing_space + 1);
   }
 }
 
 /**
- * Split a line string into the first word (key) and the rest of the line with 
the
- * first space in the latter removed.
+ * Split the given string by the delimiter and fill the given vector.
+ * If an intermediate string is empty, or space or null character, it is not 
appended to the
+ * vector.
+ * Ensure that the given string has no leading spaces.
  */
-static void split_line_key_rest(std::string_view line, string &r_line_key, 
string &r_rest_line)
+static void split_by_char(string_view in_string, char delimiter, 
Vector<string_view> &r_out_list)
 {
-  if (line.empty()) {
-    return;
-  }
-  size_t pos = line.find_first_of(' ');
-  r_line_key = pos == string::npos ? line.substr(0, 1) : line.substr(0, pos);
-  r_rest_line = line.substr(r_line_key.size(), string::npos);
-  if (r_rest_line.empty()) {
-    return;
+  r_out_list.clear();
+  while (!in_string.empty()) {
+    const string_view::size_type pos_delim{in_string.find_first_of(delimiter)};
+    const string_view::size_type word_len = pos_delim == string_view::npos ? 
in_string.size() :
+                                                                             
pos_delim;
+
+    string_view word{in_string.data(), word_len};
+    if (!word.empty() && !(word == " " && !(word[0] == '\0'))) {
+      r_out_list.append(word);
+    }
+    if (pos_delim == string_view::npos) {
+      return;
+    }
+    /* Add one in position of delimiter to skip it. */
+    in_string = in_string.substr(pos_delim + 1, string_view::npos);
   }
-  /* Remove the space between line key and the data after it. */
-  r_rest_line.erase(0, 1);
 }
 
 /**
@@ -80,13 +115,13 @@ static void split_line_key_rest(std::string_view line, 
string &r_line_key, strin
  * is set to the given fallback value in that case.
  */
 
-void copy_string_to_float(StringRef src, const float fallback_value, float 
&r_dst)
+void copy_string_to_float(string_view src, const float fallback_value, float 
&r_dst)
 {
   try {
-    r_dst = std::stof(src.data());
+    r_dst = std::stof(string(src));
   }
   catch (const std::invalid_argument &inv_arg) {
-    fprintf(stderr, "Bad conversion to float:%s:%s\n", inv_arg.what(), 
src.data());
+    std::cerr << "Bad conversion to float:'" << inv_arg.what() << "':'" << src 
<< "'" << std::endl;
     r_dst = fallback_value;
   }
 }
@@ -98,8 +133,7 @@ void copy_string_to_float(StringRef src, const float 
fallback_value, float &r_ds
  * Catches exception if any string cannot be converted to a float. The 
destination
  * float is set to the given fallback value in that case.
  */
-
-BLI_INLINE void copy_string_to_float(Span<string> src,
+BLI_INLINE void copy_string_to_float(Span<string_view> src,
                                      const float fallback_value,
                                      MutableSpan<float> r_dst)
 {
@@ -115,13 +149,13 @@ BLI_INLINE void copy_string_to_float(Span<string> src,
  * Catches exception if the string cannot be converted to an integer. The 
destination
  * int is set to the given fallback value in that case.
  */
-BLI_INLINE void copy_string_to_int(StringRef src, const int fallback_value, 
int &r_dst)
+BLI_INLINE void copy_string_to_int(string_view src, const int fallback_value, 
int &r_dst)
 {
   try {
-    r_dst = std::stoi(src.data());
+    r_dst = std::stoi(string(src));
   }
   catch (const std::invalid_argument &inv_arg) {
-    fprintf(stderr, "Bad conversion to int:%s:%s\n", inv_arg.what(), 
src.data());
+    std::cerr << "Bad conversion to int:'" << inv_arg.what() << "':'" << src 
<< "'" << std::endl;
     r_dst = fallback_value;
   }
 }
@@ -132,7 +166,7 @@ BLI_INLINE void copy_string_to_int(StringRef src, const int 
fallback_value, int
  * Catches exception if any string cannot be converted to an integer. The 
destination
  * int is set to the given fallback value in that case.
  */
-BLI_INLINE void copy_string_to_int(Span<string> src,
+BLI_INLINE void copy_string_to_int(Span<string_view> src,
                                    const int fallback_value,
                                    MutableSpan<int> r_dst)
 {
@@ -212,14 +246,14 @@ void 
OBJParser::parse_and_store(Vector<std::unique_ptr<Geometry>> &all_geometrie
   string object_group{};
 
   while (std::getline(obj_file_, line)) {
-    string line_key{}, rest_line{};
+    string_view line_key, rest_line;
     split_line_key_rest(line, line_key, rest_line);
     if (line.empty() || rest_line.empty()) {
       continue;
     }
 
     if (line_key == "mtllib") {
-      mtl_libraries_.append(rest_line);
+      mtl_libraries_.append(string(rest_line));
     }
     else if (line_key == "o") {
       /* Update index offsets to keep track of objects which have claimed 
their vertices. */
@@ -231,7 +265,7 @@ void 
OBJParser::parse_and_store(Vector<std::unique_ptr<Geometry>> &all_geometrie
     }
     else if (line_key == "v") {
       float3 curr_vert{};
-      Vector<string> str_vert_split;
+      Vector<string_view> str_vert_split;
       split_by_char(rest_line, ' ', str_vert_split);
       copy_string_to_float(str_vert_split, FLT_MAX, {curr_vert, 3});
       global_vertices.vertices.append(curr_vert);
@@ -245,7 +279,7 @@ void 
OBJParser::parse_and_store(Vector<std::unique_ptr<Geometry>> &all_geometrie
     }
     else if (line_key == "vt") {
       float2 curr_uv_vert{};
-      Vector<string> str_uv_vert_split;
+      Vector<string_view> str_uv_vert_split;
       split_by_char(rest_line, ' ', str_uv_vert_split);
       copy_string_to_float(str_uv_vert_split, FLT_MAX, {curr_uv_vert, 2});
       global_vertices.uv_vertices.append(curr_uv_vert);
@@ -256,7 +290,7 @@ void 
OBJParser::parse_and_store(Vector<std::unique_ptr<Geometry>> &all_geometrie
     else if (line_key == "l") {
       BLI_assert(current_geometry);
       int edge_v1 = -1, edge_v2 = -1;
-      Vector<string> str_edge_split;
+      Vector<string_view> str_edge_split;
       split_by_char(rest_line, ' ', str_edge_split);
       copy_string_to_int(str_edge_split[0], -1, edge_v1);
       copy_string_to_int(str_edge_split[1], -1, edge_v2);
@@ -285,14 +319,12 @@ void 
OBJParser::parse_and_store(Vector<std::unique_ptr<Geometry>> &all_geometrie
           rest_line.find("null") == string::npos) {
         /* TODO ankitm make a string to bool function if need arises. */
         try {
-          std::stoi(rest_line);
+          std::stoi(string(rest_line));
           shaded_smooth = true;
         }
         catch (const std::invalid_argument &inv_arg) {
-          fprintf(stderr,
-                  "Bad argument for smooth shading: %s:%s\n",
-                  inv_arg.what(),
-                  rest_line.c_str());
+          std::cerr << "Bad argument for smooth shading:'" << inv_arg.what() 
<< "':'" << rest_line
+                    << "'" << std::endl;
           shaded_smooth = false;
         }
       }
@@ -311,9 +343,9 @@ void 
OBJParser::parse_and_store(Vector<std::unique_ptr<Geometry>> &all_geometrie
         current_geometry->use_vertex_groups_ = true;
       }
 
-      Vector<string> str_corners_split;
+      Vector<string_view> str_corners_split;
       split_by_char(rest_line, ' ', str_corners_split);
-      for (const string &str_corner : str_corners_split) {
+      for (string_view str_corner : str_corners_split) {
         FaceCorner corner;
         size_t n_slash = std::count(str_corner.begin(), str_corner.end(), '/');
         if (n_slash == 0) {
@@ -322,7 +354,7 @@ void 
OBJParser::parse_and_store(Vector<std::unique_ptr<Geometry>> &all_geometrie
         }
         else if (n_slash == 1) {
           /* Case: f v1/vt1 v2/vt2 v3/vt3 . */
-          Vector<string> vert_uv_split;
+          Vector<string_view> vert_uv_split;
           split_by_char(str_corner, '/', ver

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to