From 31384bdfeb7579c99c154b44fa81d172696b0978 Mon Sep 17 00:00:00 2001 From: Kyle Bayes Date: Tue, 5 Mar 2024 02:49:22 -0800 Subject: [PATCH] Speed up String2Vector. PiperOrigin-RevId: 612767185 Change-Id: I4a4de5e03bbd6653be7ccb3e730ad64a335e17b0 --- src/user/user_api.cc | 2 +- src/user/user_composite.cc | 8 +-- src/xml/xml_native_reader.cc | 62 ++++++++++------------- src/xml/xml_util.cc | 98 ++++++++++++++---------------------- src/xml/xml_util.h | 16 ++---- test/xml/xml_util_test.cc | 57 +++++++++++++++++++++ 6 files changed, 132 insertions(+), 111 deletions(-) create mode 100644 test/xml/xml_util_test.cc diff --git a/src/user/user_api.cc b/src/user/user_api.cc index 41a86adb..6a78ad3b 100644 --- a/src/user/user_api.cc +++ b/src/user/user_api.cc @@ -490,7 +490,7 @@ mjtByte mjm_setInStringVec(mjStringVec dest, int i, const char* text) { // split text and copy into string array void mjm_setStringVec(mjStringVec dest, const char* text) { std::vector* v = reinterpret_cast*>(dest); - mjXUtil::String2Vector(text, *v); + *v = mjXUtil::String2Vector(text); } diff --git a/src/user/user_composite.cc b/src/user/user_composite.cc index ad48db64..fd43b916 100644 --- a/src/user/user_composite.cc +++ b/src/user/user_composite.cc @@ -420,9 +420,9 @@ bool mjCComposite::MakeParticle(mjCModel* model, mjmBody* body, char* error, int } } mjXUtil::Vector2String(userface, face); - } else { + } else { dim = 2; // can only load a surface for now - mjXUtil::String2Vector(userface, face); + face = mjXUtil::String2Vector(userface); for (int i=0; i(userface); for (int j=0; j skinface; - mjXUtil::String2Vector(userface, skinface); + skinface = mjXUtil::String2Vector(userface); int nvert = uservert.size()/3; for (int j=0; j<2; j++) { diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index e87c95be..a2b8aea3 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -1304,18 +1304,15 @@ void mjXReader::OneFlex(XMLElement* elem, mjmFlex* pflex) { mjm_setStringVec(pflex->vertbody, text.c_str()); } if (ReadAttrTxt(elem, "vertex", text)) { - std::vector vert; - String2Vector(text, vert); + std::vector vert = String2Vector(text); mjm_setDouble(pflex->vert, vert.data(), vert.size()); } if (ReadAttrTxt(elem, "element", text, true)) { - std::vector elem; - String2Vector(text, elem); + std::vector elem = String2Vector(text); mjm_setInt(pflex->elem, elem.data(), elem.size()); } if (ReadAttrTxt(elem, "texcoord", text)) { - std::vector texcoord; - String2Vector(text, texcoord); + std::vector texcoord = String2Vector(text); mjm_setFloat(pflex->texcoord, texcoord.data(), texcoord.size()); } @@ -1447,22 +1444,19 @@ void mjXReader::OneSkin(XMLElement* elem, mjmSkin* pskin) { // read vertex data if (ReadAttrTxt(elem, "vertex", text)) { - std::vector vert; - String2Vector(text, vert); + std::vector vert = String2Vector(text); mjm_setFloat(pskin->vert, vert.data(), vert.size()); } // read texcoord data if (ReadAttrTxt(elem, "texcoord", text)) { - std::vector texcoord; - String2Vector(text, texcoord); + std::vector texcoord = String2Vector(text); mjm_setFloat(pskin->texcoord, texcoord.data(), texcoord.size()); } // read user face data if (ReadAttrTxt(elem, "face", text)) { - std::vector face; - String2Vector(text, face); + std::vector face = String2Vector(text); mjm_setInt(pskin->face, face.data(), face.size()); } @@ -1490,15 +1484,13 @@ void mjXReader::OneSkin(XMLElement* elem, mjmSkin* pskin) { bindquat.push_back(data[3]); // read vertid - vector tempid; ReadAttrTxt(bone, "vertid", text, true); - String2Vector(text, tempid); + vector tempid = String2Vector(text); mjm_appendIntVec(pskin->vertid, tempid.data(), tempid.size()); // read vertweight - vector tempweight; ReadAttrTxt(bone, "vertweight", text, true); - String2Vector(text, tempweight); + vector tempweight = String2Vector(text); mjm_appendFloatVec(pskin->vertweight, tempweight.data(), tempweight.size()); // advance to next bone @@ -2277,8 +2269,8 @@ void mjXReader::OneComposite(XMLElement* elem, mjmBody* pbody, mjmDefault* def) ReadAttrTxt(elem, "curve", curves); ReadAttrTxt(elem, "initial", comp.initial); ReadAttr(elem, "size", 3, comp.size, text, false, false); - if (ReadAttrTxt(elem, "vertex", text)){ - String2Vector(text, comp.uservert); + if (ReadAttrTxt(elem, "vertex", text)) { + comp.uservert = String2Vector(text); } // shell @@ -2521,13 +2513,13 @@ void mjXReader::OneFlexcomp(XMLElement* elem, mjmBody* pbody) { fcomp.rigid = (n==1); } if (ReadAttrTxt(elem, "point", text)){ - String2Vector(text, fcomp.point); + fcomp.point = String2Vector(text); } if (ReadAttrTxt(elem, "element", text)){ - String2Vector(text, fcomp.element); + fcomp.element = String2Vector(text); } if (ReadAttrTxt(elem, "texcoord", text)) { - String2Vector(text, fcomp.texcoord); + fcomp.texcoord = String2Vector(text); } // edge @@ -2566,22 +2558,22 @@ void mjXReader::OneFlexcomp(XMLElement* elem, mjmBody* pbody) { XMLElement* epin = FirstChildElement(elem, "pin"); while (epin) { // accumulate id, coord, range - vector temp; - if (ReadAttrTxt(epin, "id", text)){ - String2Vector(text, temp); - fcomp.pinid.insert(fcomp.pinid.end(), temp.begin(), temp.end()); + if (ReadAttrTxt(epin, "id", text)) { + vector v = String2Vector(text); + fcomp.pinid.insert(fcomp.pinid.end(), v.begin(), v.end()); } - if (ReadAttrTxt(epin, "range", text)){ - String2Vector(text, temp); - fcomp.pinrange.insert(fcomp.pinrange.end(), temp.begin(), temp.end()); + if (ReadAttrTxt(epin, "range", text)) { + vector v = String2Vector(text); + fcomp.pinrange.insert(fcomp.pinrange.end(), v.begin(), v.end()); } - if (ReadAttrTxt(epin, "grid", text)){ - String2Vector(text, temp); - fcomp.pingrid.insert(fcomp.pingrid.end(), temp.begin(), temp.end()); + if (ReadAttrTxt(epin, "grid", text)) { + + vector v = String2Vector(text); + fcomp.pingrid.insert(fcomp.pingrid.end(), v.begin(), v.end()); } - if (ReadAttrTxt(epin, "gridrange", text)){ - String2Vector(text, temp); - fcomp.pingridrange.insert(fcomp.pingridrange.end(), temp.begin(), temp.end()); + if (ReadAttrTxt(epin, "gridrange", text)) { + vector v = String2Vector(text); + fcomp.pingridrange.insert(fcomp.pingridrange.end(), v.begin(), v.end()); } // advance @@ -2884,7 +2876,7 @@ void mjXReader::Custom(XMLElement* section) { // read name and assign ReadAttrTxt(obj, "objname", text, true); - objname += text + " "; + objname += " " + text; // read parameter and assign double oprm = 0; diff --git a/src/xml/xml_util.cc b/src/xml/xml_util.cc index 2b230a73..fcda1791 100644 --- a/src/xml/xml_util.cc +++ b/src/xml/xml_util.cc @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include #include #include @@ -42,7 +43,6 @@ namespace { -using std::istringstream; using std::size_t; using std::string; using std::stringstream; @@ -735,74 +735,52 @@ bool mjXUtil::ReadAttrInt(XMLElement* elem, const char* attr, int* data, bool re -// read vector from string -void mjXUtil::String2Vector(const string& txt, vector& vec) { - stringstream strm(txt); - vec.clear(); - - while (!strm.eof()) { - string word; - strm >> word; - if (strm.fail()) { - break; - } else { - vec.push_back(word); - } - } +template<> int mjXUtil::StrToNum(char* str, char** c) { + return std::strtol(str, c, 10); } - - -// read vector from string -void mjXUtil::String2Vector(const string& txt, vector& vec) { - stringstream strm(txt); - vec.clear(); - - while (!strm.eof()) { - double num; - strm >> num; - if (strm.fail()) { - break; - } else { - vec.push_back(num); - } - } +template<> float mjXUtil::StrToNum(char* str, char** c) { + return std::strtof(str, c); } - - -// read vector from string -void mjXUtil::String2Vector(const string& txt, vector& vec) { - stringstream strm(txt); - vec.clear(); - - while (!strm.eof()) { - float num; - strm >> num; - if (strm.fail()) { - break; - } else { - vec.push_back(num); - } - } +template<> double mjXUtil::StrToNum(char* str, char** c) { + return std::strtod(str, c); } +template +std::vector mjXUtil::String2Vector(const std::string& s) { + errno = 0; + std::vector v; + char* cs = (char*) s.c_str(); + char* ch = cs; + // reserve worst case + v.reserve((s.size() >> 1) + 1); -// read vector from string -void mjXUtil::String2Vector(const string& txt, vector& vec) { - stringstream strm(txt); - vec.clear(); - - while (!strm.eof()) { - int num; - strm >> num; - if (strm.fail()) { - break; - } else { - vec.push_back(num); - } + for (;;) { + cs = ch; + T num = StrToNum(cs, &ch); + if (cs == ch) break; + if (errno) break; + v.push_back(num); } + + v.shrink_to_fit(); + return v; +} + +template std::vector mjXUtil::String2Vector(const std::string& s); +template std::vector mjXUtil::String2Vector(const std::string& s); +template std::vector mjXUtil::String2Vector(const std::string& s); +template<> +std::vector mjXUtil::String2Vector(const std::string& s) { + std::vector v; + std::stringstream ss(s); + std::string word; + while (ss >> word) { + v.push_back(word); + } + return v; } diff --git a/src/xml/xml_util.h b/src/xml/xml_util.h index 1d819041..89231075 100644 --- a/src/xml/xml_util.h +++ b/src/xml/xml_util.h @@ -163,17 +163,9 @@ class mjXUtil { static bool ReadAttrInt(tinyxml2::XMLElement* elem, const char* attr, int* data, bool required = false); - // read vector from string - static void String2Vector(const std::string& txt, std::vector& vec); - - // read vector from string - static void String2Vector(const std::string& txt, std::vector& vec); - - // read vector from string - static void String2Vector(const std::string& txt, std::vector& vec); - - // read vector from string - static void String2Vector(const std::string& txt, std::vector& vec); + // convert string to vector + template + static std::vector String2Vector(const std::string& s); // write vector to string static void Vector2String(std::string& txt, const std::vector& vec); @@ -220,6 +212,8 @@ class mjXUtil { template static bool ReadAttrValues(tinyxml2::XMLElement* elem, const char* attr, std::function push, int max = -1); + template static T StrToNum(char* str, char** c); + }; #endif // MUJOCO_SRC_XML_XML_UTIL_H_ diff --git a/test/xml/xml_util_test.cc b/test/xml/xml_util_test.cc new file mode 100644 index 00000000..a0ff0b6c --- /dev/null +++ b/test/xml/xml_util_test.cc @@ -0,0 +1,57 @@ +// Copyright 2024 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include +#include "src/xml/xml_util.h" +#include "test/fixture.h" + +namespace mujoco { +namespace { + +using XMLUtilTest = MujocoTest; + +using ::testing::ElementsAre; + +TEST_F(XMLUtilTest, String2VectorFloat) { + std::vector v = mjXUtil::String2Vector(" 1.2 3.2 5.3 6 "); + EXPECT_THAT(v, ElementsAre(1.2, 3.2, 5.3, 6)); +} + +TEST_F(XMLUtilTest, String2VectorEmpty) { + std::vector v = mjXUtil::String2Vector(""); + EXPECT_THAT(v, ElementsAre()); +} + +TEST_F(XMLUtilTest, String2VectorError) { + std::vector v = mjXUtil::String2Vector("ABCD. /123/122/113"); + EXPECT_THAT(v, ElementsAre()); +} + + +TEST_F(XMLUtilTest, String2VectorInt) { + std::vector v = mjXUtil::String2Vector(" -1 3 5 6"); + EXPECT_THAT(v, ElementsAre(-1, 3, 5, 6)); +} + +TEST_F(XMLUtilTest, String2VectorString) { + auto v = mjXUtil::String2Vector(" abc def "); + EXPECT_THAT(v, ElementsAre("abc", "def")); +} + +} // namespace +} // namespace mujoco