-
Notifications
You must be signed in to change notification settings - Fork 935
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix issue #2809 (broken test with clang) #2820
Conversation
which was disabled in moveit#2792
…cope error This optimization was originally introduced in moveit#2698 to avoid string copying.
Looks like std::make_pair is the culprit here. Avoiding it and initializing the pair(s) directly, resolves the issue too.
I found a trick to retain the original optimization using string references. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approve because it gets rid of the building with -O0
. Generally though isn't copying a short string going to be one of the most optimized features of any modern compiler and implementation of the STL? I as a rule generally avoid ever doing std::anything<Anything &>::anything
because by using references with STL types you can create pessimizations by avoiding elision optimizations the compiler could otherwise do.
I copy-pasted the example code into godbolt and I'm not sure what doesn't work about it: https://godbolt.org/z/5azE4Exc5 |
I likewise am confused. There is a single scope in the pasted example. |
Here, it's not only copying the strings but also allocating new memory for them. That's one of the most costly operations one can do. |
It isn't always expensive to copy a string: https://tc-imba.github.io/posts/cpp-sso |
@@ -495,9 +495,9 @@ bool distanceCallback(fcl::CollisionObjectd* o1, fcl::CollisionObjectd* o2, void | |||
|
|||
double dist_threshold = cdata->req->distance_threshold; | |||
|
|||
const std::pair<const std::string&, const std::string&> pc = cd1->getID() < cd2->getID() ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a matter of style, but I believe you could have equivalently removed the references and preserved the ternary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keeping the (const) references ensure optimization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup yup. Was more of a comment before the std::cref
became the choice thing to do.
Here is another blog post that explains SSO it in a helpful way: https://akrzemi1.wordpress.com/2014/04/14/common-optimizations/ |
@rhaschke looking at the code/PR, my guess is asan isn't happy/able to peer through the multiple levels of pointer indirection. and here, |
Codecov Report
@@ Coverage Diff @@
## master #2820 +/- ##
==========================================
+ Coverage 60.82% 60.82% +0.01%
==========================================
Files 366 366
Lines 31713 31713
==========================================
+ Hits 19285 19286 +1
+ Misses 12428 12427 -1
Continue to review full report at Codecov.
|
I also get |
@rhaschke you know what's fun?
https://godbolt.org/z/cE5PbvsfM does not produce any error with address sanitizer. |
Also, this code is fine
ie, removing the |
Forgot to mention this still generates the error
|
Also
is fine. Seems to be something with |
is good too |
@griswaldbrooks If I understand it correctly, it's because of std::make_pair always returning the decayed type see, which call the copy-constructor for |
I think we should go with using
|
@tylerjw wrote
Very true, but I don't want to rely on implicit optimizations and end up in situations where there's a significant runtime difference depending on whether my collision object is called |
@JafarAbdi, thanks for pointing out
I will push an update to this PR using #include <map>
#include <string>
#include <iostream>
#include <functional>
#include <chrono>
int main(int argc, char const* argv[])
{
using KeyT = std::pair<std::string, std::string>;
std::map<KeyT, int> map;
map.insert(std::make_pair(std::make_pair("foo", "bar"), 1));
map.insert(std::make_pair(std::make_pair("bar", "foo"), 2));
const std::string foo = "foo";
const std::string bar = "bar";
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < 10000; ++i)
{
const std::pair<const std::string&, const std::string&> key1(foo, bar);
const std::pair<const std::string&, const std::string&> key2(bar, foo);
const std::pair<const std::string&, const std::string&>& key = foo < bar ? key1 : key2;
}
auto elapsed = std::chrono::steady_clock::now() - start;
std::cerr << "ref !find: " << elapsed.count() << std::endl;
start = std::chrono::steady_clock::now();
for (int i = 0; i < 10000; ++i)
{
const std::pair<const std::string&, const std::string&> key1(foo, bar);
const std::pair<const std::string&, const std::string&> key2(bar, foo);
const std::pair<const std::string&, const std::string&>& key =
foo < bar ? std::make_pair(std::cref(foo), std::cref(bar)) : std::make_pair(std::cref(bar), std::cref(foo));
auto it = map.find(key);
}
elapsed = std::chrono::steady_clock::now() - start;
std::cerr << "with cref: " << elapsed.count() << std::endl;
start = std::chrono::steady_clock::now();
for (int i = 0; i < 10000; ++i)
{
const std::pair<const std::string, const std::string> key1(foo, bar);
const std::pair<const std::string, const std::string> key2(bar, foo);
const std::pair<const std::string, const std::string>& key = foo < bar ? key1 : key2;
auto it = map.find(key);
}
elapsed = std::chrono::steady_clock::now() - start;
std::cerr << "with copy: " << elapsed.count() << std::endl;
return 0;
} |
@rhaschke very cool. Throwing it into quickbench shows a 2.5x speedup https://quick-bench.com/q/CtWmHGp4nHOglaietXVNfi8r7cY @JafarAbdi do you have another reference about |
The culprit of #2792 was essentially an optimization I proposed for #2698 to avoid copying strings to generate a map key.
Looks like this optimization leads to a stack-use-after-scope issue (found with asan), both in clang and gcc.
Reverting it, fixes the issue.
However, I'm not sure why this code doesn't work - the string references should exist: