Skip to content
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

Implement UniquePtr #1

Open
wants to merge 83 commits into
base: master
Choose a base branch
from
Open

Implement UniquePtr #1

wants to merge 83 commits into from

Conversation

certik
Copy link
Owner

@certik certik commented Apr 11, 2015

This is a work in progress. I will keep pushing more patches as I implement things, and at the very end I will rebase and create a few very nice patches.

TODO:

  • Implement a custom deallocator (follow similar design as std::unique_ptr, i.e. add a template parameter, but unlike std::unique_ptr specialize the template for the most common default case for efficiency -- std::unique_ptr uses a tuple to store both the pointer and the deallocator)
  • Use a tuple to store both the pointer and the deallocator
  • Test that the Release mode sizeof UniquePtr is the same as raw pointer, as long as the deallocator doesn't have any member variables
  • Test that proper deleter constructors are called
  • Add the rest of the methods from RCP (appropriately adapted to UniquePtr)
  • Make sure we have the same interface as std::unique_ptr, or as close as possible
  • Doxygen documentation: the class UniquePtr will need Doxygen documentation before the final version gets pushed to Trilinos
  • Separate out function implementation bodies from C++ class body
  • UniquePtr::operator=() should release the old object first, so std::swap is not the way to go (write a test first that fails, then fix it)
  • Test that UniquePtr::swap() swaps the deleter as well
  • Achieve 100% test coverage. Done --- each line is now tested. From now on, always add the test first, then implement it, to ensure that every line is tested.

@certik certik changed the title [WIP] Implementation of UniquePtr Implement UniquePtr Jun 4, 2015
@bartlettroscoe
Copy link
Contributor

My review of Teuchos::UniquePtr class ...

So I have finished my initial review of the Teuchos::UniquePtr class. I found a few little issues and made a few small commits that I pushed the uniqueptr branch in my forked github repo:

To git@github.com:bartlettroscoe/trilinos.git
   44e972b..8d8f81c  uniqueptr -> uniqueptr

Please review those commits and the commit messages and the commits in my detailed review below.

The only major remaining todos that I can see for this UniquePtr class are:

  1. Add doxygen documentation to the UniquePtr class in the Teuchos_UniquePtr.hpp file. Just use the Teuchos_PtrDecl.hpp and Teuchos_RCPDecl.hpp files as examples.

Otherwise, this looks very nice. It is interesting to see how some of the new C++11 traits classes are being used. That makes programming code like this so much easier.

I just really wish we did not have to write memory management code of our own like this anymore. I am going to try to look at writing an updated white paper for the current status of the Teuchos MM classes in relation to the C++11/14 standard libraries.


1) Get the code and inspect the changes

Ondrej has created a github clone of the Trilinos public git repo at:

https://github.com/certik/trilinos

The pull request containing the UniquePtr code is:

https://github.com/certik/trilinos/pull/1

I added the remote and got the branch:

$ git remote add ondrej git@github.com:certik/trilinos.git
$ git fetch ondrej
$ git checkout --track ondrej/uniqueptr 

Since this is the filtered public repo, it has not commits in common with the main Trilinos git repo :-( But you can still do this in the same git repo.

Looking at the changed files compared to ondrej/master:

$ git diff --name-status `git merge-base HEAD ondrej/master`
A       packages/teuchos/core/src/Teuchos_UniquePtr.hpp
M       packages/teuchos/core/src/Teuchos_toString.hpp
M       packages/teuchos/core/test/MemoryManagement/CMakeLists.txt
A       packages/teuchos/core/test/MemoryManagement/UniquePtr_UnitTests.cpp

The only change to the file Teuchos_toString.hpp is:

+#ifdef HAVE_TEUCHOSCORE_CXX11
+inline
+std::ostream& operator<<(std::ostream& out, const std::nullptr_t)
+{
+  return (out << "nullptr");
+}
+#endif

Very good. Just as you would expect. It would seem that nullptr is a special keyword in C++ and not like an enum really. But the type std::nullptr_t can be used to pass it. Compiler magic. Very interesting.

The only other change it to the file teuchos/core/test/MemoryManagement/CMakeLists.txt which is just to add the unit tests in UniquePtr_UnitTests.cpp. Very good. Everything else is new code.

It looks like all of the unit tests in UniquePtr_UnitTests.cpp are protected by:

#ifdef HAVE_TEUCHOSCORE_CXX11
...
#endif // HAVE_TEUCHOSCORE_CXX11

so they should be excluded when C++11 is not enabled.

2) Configure, build, and test:

$ time ./do-configure -DTrilinos_ASSERT_MISSING_PACKAGES=OFF \
  -DTrilinos_ENABLE_TeuchosCore=ON &> configure.out

real    0m28.820s
user    0m23.326s
sys 0m3.763s

$ time make -j16 &> make.out

real    1m12.926s
user    9m58.624s
sys 1m34.670s

time ctest -j16 &> ctest.out

real    0m0.473s
user    0m0.712s
sys 0m0.778s

This gave the test results:

100% tests passed, 0 tests failed out of 46

Label Time Summary:
Teuchos    =   1.74 sec

Running the unit tests for UniquePtr manually:

./TeuchosCore_MemoryManagement_UnitTests.exe --group=UniquePtr

Teuchos::GlobalMPISession::GlobalMPISession(): started processor with name u235.ornl.gov and rank 0!

***
*** Unit test suite ...
***


Sorting tests by group name then by the order they were added ... (time = 0.000429)

Running unit tests ...

336. UniquePtr_assignSelf_null_UnitTest ... [Passed] (2.29e-05 sec)
337. UniquePtr_assignSelf_nonnull_UnitTest ... [Passed] (3.5e-05 sec)
338. UniquePtr_assign1_UnitTest ... [Passed] (5.96e-06 sec)
339. UniquePtr_assign2_UnitTest ... [Passed] (0.000287 sec)
340. UniquePtr_assign3_UnitTest ... [Passed] (7.89e-05 sec)
341. UniquePtr_assign4_UnitTest ... [Passed] (4.7e-05 sec)
342. UniquePtr_getConst_UnitTest ... [Passed] (1.19e-05 sec)
343. UniquePtr_explicit_null_UnitTest ... [Passed] (4.05e-06 sec)
344. UniquePtr_reset_null_UnitTest ... [Passed] (7.15e-06 sec)
345. UniquePtr_reset_null2_UnitTest ... [Passed] (2.31e-05 sec)
346. UniquePtr_deallocate_UnitTest ... [Passed] (1.72e-05 sec)
347. UniquePtr_dereference_UnitTest ... [Passed] (1.41e-05 sec)
348. UniquePtr_danglingPtr1_UnitTest ... [Passed] (7.8e-05 sec)
349. UniquePtr_danglingPtr2_UnitTest ... [Passed] (5.51e-05 sec)
350. UniquePtr_danglingPtr2b_UnitTest ... [Passed] (5.32e-05 sec)
351. UniquePtr_danglingPtr3_UnitTest ... [Passed] (5.2e-05 sec)
352. UniquePtr_danglingPtr4_UnitTest ... [Passed] (8.32e-05 sec)
353. UniquePtr_std_unique_ptr_interface_UnitTest ... Deleting x1, value is : 5
Deleting FooDeleting x1, value is : 5
Deleting Foo[Passed] (0.000616 sec)

Total Time: 0.00328 sec

Summary: total = 361, run = 18, passed = 18, failed = 0

End Result: TEST PASSED

It looks like there is some problems with printing in a few of these tests. I will fix that. ... I was able to fix that by using [&] with the lambda functions and I did so in the commit:

44e972b "Print to correct unit test out (UniquePtr)"
Author: Roscoe A. Bartlett <bartlettra@ornl.gov>
Date:   Sat Jun 20 13:43:32 2015 -0400 (4 minutes ago)

M       packages/teuchos/core/test/MemoryManagement/UniquePtr_UnitTests.cpp

3) Run coverage build and submit to Trilinos dashboard:

Now to do a coverage build and submit to Trilinos CDash:

$ time ./do-configure -DTrilinos_ENABLE_COVERAGE_TESTING=ON \
  -DTrilinos_ASSERT_MISSING_PACKAGES=OFF -DTrilinos_ENABLE_TeuchosCore=ON
  &> configure.out

real    0m16.037s
user    0m15.730s
sys 0m0.270s

time make dashboard &> make.dashboard.out

real    8m29.969s
user    47m34.597s
sys 3m14.851s

The produced the coverage results at:

http://testing.sandia.gov/cdash/viewCoverage.php?buildid=2095398

It shows 100% coverage for UniquePtr.hpp:

http://testing.sandia.gov/cdash/viewCoverageFile.php?buildid=2095398&fileid=76763

However, the coverage output does not show executable lines for functions like:

  189      | template<class T, class Deleter=std::default_delete<T>>
  190      | inline
  191      | bool nonnull( const UniquePtr<T, Deleter> &p )
  192      | {
  193      |   return bool(p);
  194      | }

So is this function tested or not? To test for that, I added TEUCHOS_TEST_FOR_EXCEPT(true) and built and ran the unit tests ...

The following tests FAILED:
    337. UniquePtr_assignSelf_nonnull_UnitTest ... 
    339. UniquePtr_assign2_UnitTest ... 
    340. UniquePtr_assign3_UnitTest ... 

Total Time: 0.00412 sec

Summary: total = 361, run = 18, passed = 15, failed = 3

Okay, so it is being tested. That means that lcov/gcov on my machine is not working correctly. My guess is that this is because I am using a custom installed version of GCC 4.8.3 and it is using lcov/gcov for the old gcc 4.4.7 compiler on this machine.

So much for coverage results. I will just look at the code manually and match it with with the unit tests. I will then add throws or break the code to assert the unit tests are working correctly.

4) Look at the code itself:

Now I will look at the code in Teuchos::UniquePtr.hpp carefully, line-by-line. There is not much code, so this should not take very long.

The first thing that I note is that it is lacking any Doxygen documentation. It will need at least the most basic Doxygen comments or the class nor any of its members will show up in the Doxygen documenation at all.

I am noticing that there is a mixture of 2 spaces and 4 spaces for indenting
lines. To compensate for this, I have used the Thyra emacs indentation
mode
to indent all fo the
code to be consistent with the rest fo the Teuchos MM classes. Also, using
the new C++11 '>>' to end templates messes up the emacs C++ indentation logic
so I had to change these back to the old '> >' to get it to work correctly.

In C++, the convention is to name typedefs as _t. Because of this, I think it would be a good idea to change the typedef Teuchos::UniquePtr::pointer to Teuchos::UniquePtr::pointer_t.

Also, there are some C-style casts being used in the code. We don't want C-style casts as they allow very bad things to happen silently. Note that when is a built-in type, then () is a C-style cast equivalent to (). To avoid the pitfalls of C-style casts, in C++11 (thank goodness) we can use {}. Prior to C++11, we had to write our own safe implicit cast function like Teuchos::as() (see the Thyra coding guidelines document).

The implementation of the private data of UniquePtr uses an std::tuple<pointer_t, Deleter>. From our prior emails, this is used to save on memory of the Deleter object when it has no data. However, using raw integers to access these data-members (e.g. std::get<0>(t_) to mean the pointer data-member) does not make for very readable code. Therefore, to clean this up, I created private inline member helper functions. I have done this with the inline private members thisPtr(). It is shorter and more descriptive and should have zero overhead. The Deleter object is only accessed in two functions so I did not bother adding helpers for it. If the Deleter object was accessed in more functions, I would create a helper function for it too (but it is more complicated because it could be a reference type as well?).

I have not read every line of the unit tests but they look reasonable. The tests that are called by the template function test_unique_ptr_interface() don't provide very good error reporting. When I was playing around adding exceptions to make sure that certain functions were being called, it was hard to see where in that long function the exception was being thrown from. But I guess if a unit test failed, it would have printed the line number so it would not have been a bid deal to track down. It is just uncaught exceptions that are the problem debugging.

@certik
Copy link
Owner Author

certik commented Jun 22, 2015

Thanks @bartlettroscoe for the thorough review. I am going to squash my 83 commits into a few nice logical commits, and then rebase your commits (which I agree with) on top.

And then push in more commits fixing the things you raised, mainly documentation. I will keep you updated.

P.S. The >> vs > > issue seems like a bug in Emacs (I use Vim, so I didn't notice), not a bug in the code, but I have no problem with using > > until the bug in Emacs is fixed.

@wermos
Copy link

wermos commented Apr 11, 2022

Hi, sorry to resurrect such an old thread, but what happened to this PR? I saw its link on the SymEngine website, so I came here to see what happened to it.

I am planning to do a GSoC project with SymEngine and implement the polynomial classes. Since this seems mostly complete (with just the documentation remaining), I could write the documentation as a part of my GSoC project and we could try to get this feature into Trilinos.

@certik
Copy link
Owner Author

certik commented Apr 11, 2022

@wermos I didn't have time to finish it. I still think it would be nice to have, but given that nobody asked for it in the 7 years since I started it, it seems that maybe the need is not that high.

For performance, we want to use custom versions of these pointer classes as we do in SymEngine. And make them safe in Debug mode.

But I'll admit I now use regular raw pointers as well as references in C++, essentially I use the language as it was intended. It is not 100% safe, but in the past 10 years that I've been using C++ almost every day, I never had to debug any difficult memory issue, so to me it means it is not that big of a deal in practice. That being said, I think somebody should write a C++ compiler plugin that restricts the C++ features to a subset and checks all raw pointers and references at runtime to ensure they are not dangling. That way one can use the simple direct syntax and it would still be 100% safe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants