{"action":"created","issue":{"url":"https://api.github.com/repos/idaholab/moose/issues/7062","repository_url":"https://api.github.com/repos/idaholab/moose","labels_url":"https://api.github.com/repos/idaholab/moose/issues/7062/labels{/name}","comments_url":"https://api.github.com/repos/idaholab/moose/issues/7062/comments","events_url":"https://api.github.com/repos/idaholab/moose/issues/7062/events","html_url":"https://github.com/idaholab/moose/issues/7062","id":156527375,"number":7062,"title":"Smart Pointer Usage in MOOSE (Discussion)","user":{"login":"permcody","id":3036960,"avatar_url":"https://avatars.githubusercontent.com/u/3036960?v=3","gravatar_id":"","url":"https://api.github.com/users/permcody","html_url":"https://github.com/permcody","followers_url":"https://api.github.com/users/permcody/followers","following_url":"https://api.github.com/users/permcody/following{/other_user}","gists_url":"https://api.github.com/users/permcody/gists{/gist_id}","starred_url":"https://api.github.com/users/permcody/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/permcody/subscriptions","organizations_url":"https://api.github.com/users/permcody/orgs","repos_url":"https://api.github.com/users/permcody/repos","events_url":"https://api.github.com/users/permcody/events{/privacy}","received_events_url":"https://api.github.com/users/permcody/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/idaholab/moose/labels/C:%20MOOSE","name":"C: MOOSE","color":"0052cc"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":30,"created_at":"2016-05-24T14:47:27Z","updated_at":"2016-05-25T15:59:44Z","closed_at":null,"body":"We need to chat about what kinds of smart pointers we should be using them and how we should be using them in the framework. According to the new Scott Meyer's book. He recommends `std::unique_ptr()` as the go to pointer to replace raw pointers in a lot of contexts. It's very fast since there is simply a pointer to the destructor and there's no other complications to deal. Shared pointers on the other hand actually require two words (128 bytes) to store and there's a \"control block\" for each object stored by a shared pointer. As those pointers are copied around there are multiple hits (at least one increment and decrement) to update the information in the control block which is protected with atomics which can severely impact performance especially in threaded regions.\r\n\r\nI think the fear of using unique_ptrs is that as you copy them, the owner's pointer becomes invalid and that has left a nasty taste in some people's mouths. This is fine for some things and not fine for others but it goes hand in hand with using move semantics in C++11 of which we can start taking advantage of throughout. Scott points out that Factory patterns should _always_ use unique pointers to construct objects. The Factory doesn't need a pointer to the object after it's constructed so it's really fast an efficient to build a \"unique_ptr\" object and pass it out of the factory. In contexts where those objects might need to be shared you can simply make them shared on demand. He points out that if you understand the lifetimes of your objects you can simply create raw pointers from your unique pointers for general access. We absolutely do know who owns all objects in MOOSE, and we know when they are destructed so again, it seems like a smart idea.\r\n\r\nI realize that some of this is moot because hopefully we don't need to worry about the costs of copying around factory constructed objects. That happens once per simulation. However, we've already seen people passing smart pointers by reference for no reason other than C++98 best practices of passing non-POD objects by reference. This needs to go away with C++11. It's no longer the best or only way to get the best performance (as half of Scott's book talks about).\r\n\r\nThe only huge downside to using unique ptrs right now, is that \"make_unique\" is MISSING in C++11! This however can be rectified with a simple wrapper that we can add to MOOSE. I'd like to start this discussion with @idaholab/moose-developers and see what people think? Should we be using a few more unique_ptrs instead of turning to shared_ptrs throughout the framework?"},"comment":{"url":"https://api.github.com/repos/idaholab/moose/issues/comments/221622596","html_url":"https://github.com/idaholab/moose/issues/7062#issuecomment-221622596","issue_url":"https://api.github.com/repos/idaholab/moose/issues/7062","id":221622596,"user":{"login":"permcody","id":3036960,"avatar_url":"https://avatars.githubusercontent.com/u/3036960?v=3","gravatar_id":"","url":"https://api.github.com/users/permcody","html_url":"https://github.com/permcody","followers_url":"https://api.github.com/users/permcody/followers","following_url":"https://api.github.com/users/permcody/following{/other_user}","gists_url":"https://api.github.com/users/permcody/gists{/gist_id}","starred_url":"https://api.github.com/users/permcody/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/permcody/subscriptions","organizations_url":"https://api.github.com/users/permcody/orgs","repos_url":"https://api.github.com/users/permcody/repos","events_url":"https://api.github.com/users/permcody/events{/privacy}","received_events_url":"https://api.github.com/users/permcody/received_events","type":"User","site_admin":false},"created_at":"2016-05-25T15:59:44Z","updated_at":"2016-05-25T15:59:44Z","body":"I have a difficult time arguing my position since I know that \"premature optimization is bad\" but there will come a time when it's worth looking at. I just wrote up this quick example to illustrate the cost of shared pointer copy (and thus, atomics) on my Mac (note that there is no threading or anything else to make this worse). Also note that our Intel processors kick ass at this kind of thing. It would be interesting to run this benchmark on BlueGene or some other system where atomics might be different.\r\n\r\n```\r\n#include <iostream>\r\n#include <chrono>\r\n#include <thread>\r\n\r\nint f(std::shared_ptr<int> foo)\r\n{\r\n  return *foo;\r\n}\r\n\r\nint g(int * foo)\r\n{\r\n  return *foo;\r\n}\r\n\r\nint main()\r\n{\r\n  {\r\n    // Shared Pointer case\r\n    auto sp = std::make_shared<int>(1);\r\n    auto t1 = std::chrono::high_resolution_clock::now();\r\n\r\n    auto foo = 0u;\r\n    for (auto i = 0u; i < 10000000; ++i)\r\n      foo += f(sp);\r\n    std::cout << foo << '\\n';\r\n\r\n    auto t2 = std::chrono::high_resolution_clock::now();\r\n    std::cout << \"f() took \"\r\n              << std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count()\r\n              << \" milliseconds\\n\";\r\n  }\r\n\r\n  {\r\n    // Unique Pointer with raw case\r\n    auto up = std::unique_ptr<int>(new int(1));\r\n    auto t1 = std::chrono::high_resolution_clock::now();\r\n\r\n    auto foo = 0u;\r\n    for (auto i = 0u; i < 10000000; ++i)\r\n      foo += g(up.get());\r\n    std::cout << foo << '\\n';\r\n\r\n    auto t2 = std::chrono::high_resolution_clock::now();\r\n    std::cout << \"f() took \"\r\n              << std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count()\r\n              << \" milliseconds\\n\";\r\n  }\r\n}\r\n```\r\n\r\nResults:\r\n```\r\n10000000\r\nshared_pointer_copy() took 231 milliseconds\r\n10000000\r\nraw_pointer_copy() took 44 milliseconds\r\n```\r\n\r\nThis is a consistent benchmark on my system. It varies by one or two ms each time I run it."}}
{"actor":{"display_login":"permcody"}}