- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Just curious,
renorm.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
When you start programming, private, and protect in classes are not needed. Why? Becuase you are the only one in your code, but if you start selling your code or have thousands of other people using it, private, protected and const get used ALOT in order to tell the users about the intended functionality to ensure they dont break the code.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Since there isnt a need to make it non-const, the designers of tbb, made it const so the users can understand: dont change the class! Does this limit you im some way? I am confused as to why you are even concerned with something like this.
Ok, so, you dont like the const, why? What is it that you are trying to accomplish that the const is not allowing?
I think your concern is misplaced due to the fact that you have not delved far enough into tbb yet.
Remeber, its only the this pointer that is const, you can change anything else you want. :)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Tully const methods are much less flexible. The const requiremnt placed on Body is incongrous with non-const execute() method of task class. Currently, one either has to use mutable keyword, const_cast on this pointer or move all mutable variables outside (make them global?). All these encourages bad coding practice.
Boost threads don't require const call method for a good reason. Usual practice is to copy mutable variables into each thread. const requirement is redundant.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Anyone using such a Body is asking for trouble and he deserves it. Copy/reuse with side effects is too bad even in single threaded world. But there are plenty of uses for non-const Body. Non-trivial method can't be made const, except in rare cases. So, what are the alternatives? Either use task scheduler explicitly or defeat const with mutable or const_cast. Explicit task creation is more complex and declaring non-re-entrant methods as const is a minefield.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
#3 "const methods can't use TLS members, because TLS's local() method is non-const."
That seems all right to me (unless there would be other issues elsewhere): if you want a TLS object, you should have a reference to a unique control instance, not a member variable per Body, because each of those controls a separate thread-to-instance map, which isn't very useful (and quite expensive!). The const doesn't apply to the referent, and you're free to call local().
#3 "Using mutable keyword on data members and casting away constness of this pointer is a minefield."
Technically you could probably use those because there is no shared access (I'd have to check to be certain), but you don't need to go there.
#4 "Well, in the paralell_for example you mentioned, if the function was not const and a user changed the internal state of the class, it could break and go crash boom!"
You don't have to invent reasons. :-)
#5 "The const requiremnt placed on Body is incongrous with non-const execute() method of task class."
See #1.
#5 "Currently, one either has to use mutable keyword, const_cast on this pointer or move all mutable variables outside (make them global?). All these encourages bad coding practice."
What is the purpose of changing the Body? In parallel_for, most Body instances are copies, so if you want to build incremental knowledge, you are better off with TLS, and otherwise you might as well make another copy. You could also use parallel_reduce instead.
#5 "Boost threads don't require const call method for a good reason. Usual practice is to copy mutable variables into each thread. const requirement is redundant."
There are orders of magnitude more tasks than threads, so the concerns are different.
#6 "Anyone using such a Body is asking for trouble and he deserves it. Copy/reuse with side effects is too bad even in single threaded world."
This is not about side effects, but about having many Body instances that relate unpredictably to each other. Maybe you should give an example for our consideration, so that we know what you want to do, specifically. Who knows, maybe const is just helpfully preventing you from making a design mistake?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
[cpp]class Body { public: Body(const Exemplar& exemplar) : tls(new enumerable_thread_specific>(exemplar)) {} // default copy constructor is OK // default assigment is OK void operator()(const blocked_range & range) { // reference to thread local copy ptr_to_local_copy = &tls->local(); // initialize tmp and rng from range [...] non_const_method(); } private: void non_const_method() { /*mutating method*/ }; shared_ptr > tls; Exemplar* ptr_to_local_copy; vector tmp; pseudorandom_number_generator_type rng; };[/cpp]
The call operator can't be const for the following reasons:
tmp stores intermediate results. Each copy of Body has its own tmp. tmp is not TLS, because it is initialized from blocked_range.
pseudorandom_number_generator_type must be lazily initialized.
ptr_to_local_copy can't be initialized by the constructor.
The call operator calls non_const_method().
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
An object that really costs a lot to initialise, like the random-number generator maybe, shouldn't be instantiated in every Body, unless it would be that only a small fraction of them uses one. Perhaps it belongs in TLS instead?
The issue seems to be that you want to communicate between operator() and non_const_method() using member variables, but isn't that why somebody invented function parameters? And if you call a separate computation object from a small loop kernel, perhaps a lambda, you wouldn't (or shouldn't) even think of it as a workaround: a Body is just too ephemeral to usefully hold any mutable state.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Because the body object might be copied, its operator() should not modify the body. Otherwise the modification might or might not become visible to the thread that invoked parallel_for, depending upon whether operator() is acting on the original or a copy. As a reminder of this nuance, parallel_for requires that the body object's operator() be declared const.
In other words, constness of operator(), the only public method ofBody, serves as the way to "document" the contract: one should not expect that the Body object passed to parallel_for can be used as an accumulator to hold the results of the loop calculations. And in fact, parallel_for's body is expected to be a lightweight, easy-to-copyclosure, not something that carries a lot of business logic.
As you mentioned, there is a plenty of ways to overcome this; and some are "inelegant" more than others. I think that clear separation of concepts is the best possible solution here: encapsulate the logic into a special class, and reference an instance of that class from the body object, or maybe even create it as an automatic variable inside the operator(). My second preference would be to use parameters to pass the information between internal methods (as suggested by Raf); and if for some variables it wouldseeminconvenient, I would not mind using mutable class members, especially if those were just a few. I would avoid const_cast becauseit would look like an ugly hack, and arguably it would *be* exactly that.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
If all copies of Body are made from the original exemplar, then the call operators doesn't need to be const. Given that someone might use Body with mutable class members (in fact, many C++ programmers do that often), all copies should be made from the original exemplar anyway. Making the call operator const doesn't protect users from possible mistakes, because const can be easily bypassed. Const forces users to use inelegant hacks (like mutable members) or keep mutable variables outside Body. More admin and more complex bookkeeping means more room for errors. The only thing Body needs is a copy constructor without side effects. In multithreaded world, const must mean the same thing as re-entrant. Re-entrant is too restrictive. Body doesn't need to be re-entrant. Yes, Body must be cheap to copy, but it a different issue.
Btw, STL has the same problem. In theory, STL algorithms can do the following with the passed function object:
1) Copy it undefined number of times.
2) Copy, use, then copy again, then use again.
In practice, no implementation does that, except maybe copying the object once at the very beginning.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
"Holding TLS by a pointers inside Body hides it from the user and makes copying cheap."
Reference-counted smart pointers have scalability issues, on some processors even more than on others, which is why I advised against their use as a first choice. But I'm unsure as to how much, and you say you are obliged to use them, so...
"In multithreaded world, const must mean the same thing as re-entrant."
How do you figure that?
"Btw, STL has the same problem."
I would say that STL has the same design. Sometimes less is more. :-)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
In multithreaded world declaring mutating methods as const is a minefield. When the users want to use non-const method they either synchronize the access to it or copy the object into each thread. But if the method is declared as const, anyone is free to assume that no synchronization is need. Re-entrant const methods can be very useful, but mutating const members are minefields.
For example it is very helpful to know that std::vector::size is re-entrant. But Body which uses rand() from CRT is not re-entrant, even if it has no data fields at all. Therefore, the users must ensure that the call operator doesn't use non-re-entrant shared resources.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Let's agree to disagree.
"TBB developers can't assume that Body's call operator is actually const, because people are free to use mutable members."
TBB does seem to happen to throw away each Body after use, but that's not the toolkit's concern, really. Whatever happened to "Anyone using such a Body is asking for trouble and he deserves it."?
"But if the method is declared as const, anyone is free to assume that no synchronization is need."
That is wishful thinking: nothing currently part of C++ can promise thread-safety (which is orthogonal to constness), only the documentation can.
"For example it is very helpful to know that std::vector::size is re-entrant."
I assume that you are using reentrant as a synonym for thread-safe (unless I missed something, a member function can be reentrant in the sense that it can concurrently be invoked on different objects, but still not thread-safe when concurrently invoked on the same object)? In this case, like for index evaluation, thread-safety seems like a safe assumption, but I think that the standard is negligent by not documenting any of that yet, last time I looked anyway.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
A member function is reentrant if multiple threads can invoke it on the same object. Invoking a member function on different copies is thread safe by default, unless the instances share some mutable data (certainly, "Anyone using such a Body is asking for trouble and he deserves it.")
TBB doesn't need reentrant Body, it needs thread safe copies of Body.
const is synonym to immutable. Successive calls on the same object should give the same result. TBB invokes the call operator on each copy of Body only once. const doesn't really apply here.
With truly non-mutating call operator each thread could reuse its copy of Body by letting it to process more then one section of blocked range. But there is no way to enforce the call operator as const.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
"Yeh, C++ standard knows nothing about threads, yet."
I meant including the new version (in progress), up until N3000 at least, unless I missed anything.
"A member function is reentrant if multiple threads can invoke it on the same object. Invoking a member function on different copies is thread safe by default, unless the instances share some mutable data (certainly, "Anyone using such a Body is asking for trouble and he deserves it.")"
Don't take my word for it, e.g., see http://doc.qt.nokia.com/4.6/threads-reentrancy.html.
"TBB doesn't need reentrant Body, it needs thread safe copies of Body."
Reentrant should be enough, as all threads operate on different instances.
"const is synonym to immutable. Successive calls on the same object should give the same result. TBB invokes the call operator on each copy of Body only once. const doesn't really apply here."
const is by no means synonymous with immutable. In C++ it's a contract that if you get a const reference you are not allowed to significantly modify the object (beyond what has been declared mutable), but other code holding a non-const reference can still modify it when your back is turned (concurrently or otherwise). Only the author can make an object immutable, by not providing any way of modifying it even to code that has a non-const reference.
"With truly non-mutating call operator each thread could reuse its copy of Body by letting it to process more then one section of blocked range. But there is no way to enforce the call operator as const."
C++ can be subverted in many ways, so to be productive you have to work with it, not against it: mutable should be used only for part of the state that doesn't significantly change the object, such as a cached value.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
With truly const call operator, e.a. successive calls from the same thread give the same result, each thread could reuse its copy of Body. Each thread gets exactly one copy and reuses it until all work is done. But TBB can't make that assumption, because that would be a minefield.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I think we've been here already. Like STL, TBB calls with const by design: less is more. If you want to modify the object anyway, explicitly use mutable, or separate concerns by using another object to pass parameters as member variables, or put those parameters together in a struct that's allocated as a local variable in operator() and then passed around by reference as a single argument, or any other means, but in general code quality benefits from a discipline that doesn't modify the function object.
"With truly const call operator, e.a. successive calls from the same thread give the same result, each thread could reuse its copy of Body. Each thread gets exactly one copy and reuses it until all work is done. But TBB can't make that assumption, because that would be a minefield."
Not significantly modifying Body in a const operation is your responsibility, and making those pseudo-parameters mutable would not pose a problem. Perhaps somebody else could comment on the relative efficiency of parallel_for vs. parallel_reduce, which does reuse Body instances (for functional reasons), or you could speculate that there is no difference and benchmark them just to prove a point (and potentially have an additional workaround). :-) Intuitively, and since parallel_for is not simply implemented on top of parallel_reduce, I doubt it somewhat, but it would be interesting to have some figures.
Unless there are new issues, let's leave it at that.
(Added and partially retracted) Oh yes, on poor old computers without parallelism, it would be better to avoid all manner of parallelism overhead, so it wouldn't do to make a throwaway copy each time. But that's just an additional argument, and not very strong.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page