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

Break the Manifold interface for performance. #4723

Merged
merged 5 commits into from Sep 11, 2017

Conversation

drwells
Copy link
Member

@drwells drwells commented Aug 8, 2017

This is the branch which I have mentioned in passing a few times: we spend nearly a third of our time allocating and freeing memory inside of the Manifold functions called when creating a grid.

I am not sure if we want to proceed with this and there are still a few things that need to be fixed:

  • The minimum boost version needs to be increased to 1.58 1.59 (update: 1.58 is not compatible with deal.II)
  • The dimension-dependent functions for returning the number of default points in each direction are pretty ugly.
  • The Manifolds::get_default_points_and_weights function could also be improved.

In addition, I included a commit that adds a new constructor to ArrayView which creates an ArrayView from a std::vector: this allows all tests but one to pass in their current form.

In reference to #4704.

@tjhei
Copy link
Member

tjhei commented Aug 8, 2017

/no-longer-run-tests (I am trying out if the tester gcc-serial is running on demand now)

@bangerth
Copy link
Member

bangerth commented Aug 8, 2017

Please submit the ArrayView work with a testcase in a separate PR -- this could be merged independently.

What new functionality of BOOST do you need for the current work?

@drwells
Copy link
Member Author

drwells commented Aug 8, 2017

I only added the new ArrayView constructor so that it is possible to call the modified functions with their old arguments. I think it is worth discussing whether or not we should permit that (or make the break even bigger) before moving it to a separate PR.

Regardless of this patch: are people OK with adding a new conversion constructor like this one? I am a bit on the fence as to whether or not we should automatically convert std::vectors into ArrayViews.

The only new boost functionality that I require it boost::container::small_vector which is new in 1.58.

@bangerth
Copy link
Member

bangerth commented Aug 8, 2017

I think we found ArrayView useful, so making more uses possible seems like the right direction. I would advocate making conversion operators explicit to avoid the automatic conversion.

Updating boost requirements is a different issue, of course...

Copy link
Member

@kronbichler kronbichler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very much in favor of changing this because we really do way too many memory allocations in those places. I have a few comments regarding one of the functions and would suggest to choose larger sizes of around 200 for the small vectors.

By the way, it is not very difficult to work around the boost vector: We use a variable of size [200] on the stack unconditionally. If it fits, we put the array view on that data type. If not, we put it into an std::vector. That would maybe be 8x5 more lines of code. I don't know about the usage in manifold.h, though.

constexpr int n_default_points_per_cell<3>()
{
return GeometryInfo<3>::vertices_per_cell + GeometryInfo<3>::lines_per_cell
+ GeometryInfo<3>::faces_per_cell;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not simply returning 3^dim-1?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I.e., use Utilities::fixed_int_power<3,dim>::value-1

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to write it out this way so that it is clear what the number signifies.

const std::size_t n_points = surrounding_points.size();
// TODO find a better dimension-dependent estimate for the size of this
// vector
boost::container::small_vector<double, 20> local_weights(n_points);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we know that. It really depends on the degree of interpolation (or whatever comes in here). In the end, the allocation is a one-time cost that must be paid off by enough operations. I would have chosen a much larger value here, maybe 200 or 500.

// The older version used surrounding_points
new_points[row] = project_to_manifold(make_array_view(surrounding_points.begin(),
surrounding_points.end()),
new_point);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use surrounding_points_start but it might be that project_to_manifold does the necessary adjustments internally. Do we have tests that trigger on one or the other?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure; it has been long enough since I wrote this that I don't even really remember the issue. I will look at this with @luca-heltai at some point during the next few days.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that the answer is 'yes' and our current implementation is wrong. At the same time, we do not use the surrounding points in FlatManifold::project_to_manifold (which is another bug in itself; this function should not be the identity function for structdim != spacedim) so this is really a separate problem.

@drwells
Copy link
Member Author

drwells commented Aug 11, 2017

@kronbichler I have made some of the preliminary changes (and I adopted the little optimization you added in a commit to #2418). I will address things more fully once #2418 is merged.

@bangerth
Copy link
Member

Just following up -- what's the status of this patch right now?

@drwells
Copy link
Member Author

drwells commented Aug 28, 2017

@bangerth What we have now works but there are still some clean-ups I would like to do before merging (see the TODO list at the top of the PR). These are closely related to @kronbichler's criticisms.

I haven't spent much time of this recently; I'd like to finish #4798 before returning to this.

@drwells drwells mentioned this pull request Sep 4, 2017
@drwells
Copy link
Member Author

drwells commented Sep 5, 2017

The first part of this patch has been moved to #5024.

@drwells
Copy link
Member Author

drwells commented Sep 6, 2017

I would prefer to merge #4798 before we get to this but I will write more patches for this PR this week.

@drwells
Copy link
Member Author

drwells commented Sep 7, 2017

I addressed most of the review comments and made a few other cosmetic changes. I believe that this is ready to be reviewed again. Here are the main points:

  1. I kept, for now, the implicit conversion constructor from a std::vector to an ArrayView. I added this purely for compatibility: users can still call the old functions with std::vectors and things will work. We can make the break even bigger, if we want, by removing this (I would prefer to do that; I don't like implicit conversion constructors).
  2. I changed the interface to TransfiniteInterpolationManifold::compute_chart_points to take a mutable ArrayView as an argument and return a cell iterator. This is not as pretty as the old version (which returns return values instead of populating a provided array) but this approach works better with ArrayView. This change aligns with my philosophy 'small_vector is a memory management detail and should never be in an API'.
  3. I think that the implementation of get_default_points_and_weights is improved but I would like to hear feedback on it. Perhaps this should be an internal function? How to do this correctly came up in the last review.

After reviewing this and #4798 I don't think it matters which is merged first; this one is probably easier to understand.

@drwells drwells removed the WIP label Sep 7, 2017
@drwells drwells mentioned this pull request Sep 7, 2017
Copy link
Member

@kronbichler kronbichler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a look through the patch and it looks great. I only have a minor comment. I'm looking forward to using this.

const std::vector<double> &weights) const;
get_new_point (const ArrayView<const Point<spacedim>> &surrounding_points,
const ArrayView<const double> &weights) const;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove one empty line (we use one line distance between function declarations).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

@kronbichler
Copy link
Member

I think the get_default_points_and_weights function makes sense. It is a bit longish to read but the names are really descriptive so I like it.

@kronbichler
Copy link
Member

/run-tests

@kronbichler
Copy link
Member

Are there any further comments? If nobody objects, I will merge this PR in a day or two.

@drwells
Copy link
Member Author

drwells commented Sep 8, 2017

If no one feels strongly about preserving the compatibility shim then I will remove the implicit conversion constructor from std::vector to ArrayView. In addition to deleting 511f57e I will also have to go back and fix a few dozen tests (which expect the old interface).

@drwells drwells force-pushed the manifold-arrayview-compat branch 4 times, most recently from 954ff14 to d82a525 Compare September 8, 2017 21:32
@drwells
Copy link
Member Author

drwells commented Sep 11, 2017

I have made the requested changes (including the changelog entry).

Copy link
Member

@bangerth bangerth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just two minor comments about the changelog. please fix and merge yourself!

Nice work!

<li>Manifold::project_to_manifold</li>
</ol>
have had their declarations changed in an incompatible manner: these three
methods now take arguments that are ArrayView​s instead of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, it's actually only two methods :-)

If you write it as "ArrayView objects", then doxygen will link to the class correctly.

eliminates this allocation cost.

The method <code>Manifold::add_new_points</code> has been removed in favor of
<code>Manifold::get_new_points</code>, which also uses ArrayView​s instead of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

you don't need to use <code> for `Manifold::get_new_points -- doxygen will automatically print it with tt font because it knows about it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... when using Manifold::get_new_points() (opening and closing parenthesis).

@tamiko
Copy link
Member

tamiko commented Sep 11, 2017

@drwells Please merge after updating the changelog.

Looks good!

@drwells
Copy link
Member Author

drwells commented Sep 11, 2017

Clang is not happy with the internal function: perhaps it does not like the = delete syntax.

@bangerth
Copy link
Member

Ah, what a shame.

I think you can generally define the function as

  template <int dim>
  uint ()
  {
  return
     GeometryInfo<dim>::vertices_per_cell + 
     GeometryInfo<dim>::lines_per_cell +
     GeometryInfo<dim>::quads_per_cell  +
     GeometryInfo<dim>::hexes_per_cell 
     - 1;   // don't count the cell itself, just the bounding objects

Things like quads_per_cell should be zero in dim == 1.

@drwells
Copy link
Member Author

drwells commented Sep 11, 2017

I cannot locally reproduce the clang problem; I suspect that they have since fixed this bug. I'll upload the fixed version in a moment.

@drwells
Copy link
Member Author

drwells commented Sep 11, 2017

I have made the requested changes.

@bangerth's proposal is a lot nicer than the list of internal functions, so I am glad we went back and changed this.

@drwells
Copy link
Member Author

drwells commented Sep 11, 2017

Blah, I screwed something up when rebasing on different machines: hold on.

This commit changes the interfaces of

Manifolds::get_default_points_and_weights
Manifold::project_to_manifold
Manifold::get_new_point
Manifold::add_new_points

to use ArrayView instead of std::vector. In addition, the interface of
add_new_points has been changed to populate the ArrayView argument
instead of appending to the end of the array.

This breaks the public interface of Manifold for the sake of improving
performance by about 30%: profiling indicates that, when creating a grid
with a Manifold, we spend about 30% of our time purely calling new and
delete since we must create and destroy so many std::vectors.
This function no longer appends new points to a vector so a name change is in
order.
@drwells
Copy link
Member Author

drwells commented Sep 11, 2017

All right; this should be good to go now.

@bangerth
Copy link
Member

OK to merge once the tester is happy.

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

Successfully merging this pull request may close these issues.

None yet

5 participants