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