I hope you don't mind, but I'm going to Cc: this to libmesh-devel.
These are good questions, and my opinions do not match those of all
the other developers and it might be unfair not to let them chime in.

On Wed, 2 May 2018, Tuzun, Robert wrote:

Greetings.  I'm still in the middle of my hiatus, but there was one
question I've been meaning to ask.  I notice, in several places, the
use of vector<bool> in the code.  Way back in 2001, Scott Meyers
(Effective STL, Item 18) recommended against the use of vector<bool>
and recommended using deque<bool> or bitset instead.

"As an STL container, there are really only two things wrong with
vector<bool>.  First, it's not an STL container.  Second, it doesn't
hold bools.  Other than that, there's not much to object to." - Scott
Meyers.  I seriously love his writing.  It takes something special to
make programming language minutia so entertaining.

This is indeed often annoying, because the inability to take real
references to vector entries or to get a pointer to a underlying array
means we often can't handle vector<bool> with the same generic code as
we use to handle vector<literally_anything_else>.  I assume you first
encountered vector<bool> in our parallel_implementation.h overloads,
written to handle just that problem?

However, the recommended alternatives are even worse in many cases.
If a code is always fine using deque<bool>, if using EIGHT TIMES as
much memory never fazes the author, then Meyers' hundreds of C++
recommendations should be ignored in favor of "Item 1: Use Java
Instead" (or maybe Python or Go these days?).  Conversely, if a code
is always happy with bitset, and never needs an array that can be
resized, then what are you writing, autotranslated Fortran 77?

Personally, I'm mostly with Howard Hinnant:
https://isocpp.org/blog/2012/11/on-vectorbool

What's abominable about vector<bool> is the name, because that's
basically a lie about supporting the whole std::vector interface, but
the data structure itself is worthwhile and often useful.  And I'm
even sympathetic to the name!  Once you create a few well-chosen
overloads (like the parallel_implementation.h stuff) then the rest of
your generic code can *mostly* support vector<bool> as if it were a
real std::vector.  And when you miss a needed specialization, the
resulting breakage is a compile-time failure, so it's a hassle rather
than a disaster.


I don't know if this advice still holds, and my search on the Web
yields conflicting answers (just do a Google search on vector<bool>
and you'll see what I mean).  Is there reason for concern?

Well, despite everything I said above: yes, there is.


First, to Meyers' objections I'd also have added "Third, it requires
bit-fiddling for every access", which means if you have a vector small
enough to *not* worry about memory savings, maybe you ought to be
using vector<unsigned char> instead.  Grepping through the code for
short logically-boolean vectors, I see that we're doing the
unsigned-char-really-holding-bool trick in dof_map.C, and
mesh_communication_global_indices.C... but we're using small
vector<bool> in about a dozen places?

I'm not going to change those without benchmarking first, because
"unsigned char is faster due to less bit fiddling" vs "bool is faster
due to fitting more stuff in cache" is the sort of system-dependent
question I can't answer a priori, and in the absence of data I think
vector<bool> is more self-documenting for something that's logically
boolean.  If anyone wants to try to prove me wrong in the libMesh
context, I'd say try System::boundary_project_solution for the
benchmark.  That has a few vector<bool> inside loops over elements,
so it looks like the easiest place to expose performance differences.


Second and more seriously, in libMesh whenever find ourselves in a
situation where tactically vector<bool> is definitely the right
choice, it may mean that strategically we've made a huge mistake!

Our classic use case for vector<bool> are vectors indexed over nodes
or over elements, where we need to support millions or billions of
entries and so using 8 times as much space is clearly a bad idea.

*However*, some of that code was a reasonable design only because it
dates back to ReplicatedMesh-only days, where we couldn't get away
from having data stored for every element on every processor.  These
days, a DistributedMesh user should be able to avoid ever requiring
O(N_elements) memory on a single MPI rank, which means that
applications should never allocate such a long vector!  So the library
should never be using one, at least on a non-debug-mode
non-ReplicatedMesh-only code path.

So let's see...

Some of these uses are OK.  E.g. the MeshTools assertion code is all
debug-mode-only, where readability is much more important than
efficiency.

Some are more dubious.  E.g.  UnstructuredMesh::all_first_order() only
gets used in practice for writing serialized viz output of an error
estimate, so making it perfectly scalable would require improving
multiple other parts of that code path first, meaning it's suboptimal
but not high priority.

Some of these uses are probably outweighed by other distributed mesh
incompatibility issues.  At first glance I'd guess
MeshTools::Modification::Smooth may not even work on a distributed
mesh, but we've just never tried to use it that way.  Looks like
LaplaceMeshSmoother is distributed-mesh-aware, so maybe everyone who
needs smoothing post-distribution has just been using that?  I don't
think we have test coverage of it though so I wouldn't trust it.

But a couple of these uses are just way past due to be fixed!  We're
still relying on big serialized vectors (not just of bool; ErrorVector
is a straight-up vector<float> under the hood!) in our error
estimation and our mesh refinement flagging code.


A separate question: in looking at parallel_implementation.h, I note
several references to Communicator::get() (e.g., line 298).  Can you
tell me where this is defined?  I could not find it anywhere.  For
example, the command grep Communicator::get -r * from the main
directory did not yield the answer.

And it probably doesn't help that our online Doxygen isn't generated
from master/HEAD!

http://libmesh.github.io/doxygen/classlibMesh_1_1Parallel_1_1Communicator.html#a4b6bd36c38eb7de350fcd56510306ced

is still useful, though, even though refactoring has moved that method
to include/parallel/communicator.h, it's still the same shim accessor
method.  We often leave any definition that short in the class
declaration itself, where it doesn't need the ClassName:: prefix.
---
Roy

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Libmesh-devel mailing list
Libmesh-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libmesh-devel

Reply via email to