Intel® oneAPI Threading Building Blocks
Ask questions and share information about adding parallelism to your applications when using this threading library.
2464 Discussions

Modification variable in operator() method

nuntawat
Beginner
621 Views
I would like to modify the value of the object inside operator method look like this:

[cpp]class Case {
  int sampleVar;
public:
  void operator() (const blocked_range& range) const {
    for(int i = range.begin(), i != range.end(), i++) {
      // do some tasks
      sampleVar++;
    }
  }

  Case(int sampleVar): sampleVar(sampleVar) {}
};[/cpp]

but because the operator method must be declared const keyword, so I cannot modify the value. What should I do?
0 Kudos
12 Replies
RafSchietekat
Valued Contributor III
621 Views
mutable int sampleVar;
0 Kudos
Dmitry_Vyukov
Valued Contributor I
621 Views
Quoting - Raf Schietekat
mutable int sampleVar;

AFAIR, functor can be executing and concurrently coping. If so, mutable is not the way. If only single variable of primitive type is copied, then mutable tbb::atomic can be used. Otherwise, mutex must be used in operator() and copy ctor.

0 Kudos
RafSchietekat
Valued Contributor III
621 Views
I don't know, I didn't feel like speculating about the context at this time, but I don't think I would try copying atomics myself, concurrently or otherwise.

Meanwhile, there's this pipeline thing that I don't care to look at again for at least a month, so maybe you could shine your light on that, too?
0 Kudos
Dmitry_Vyukov
Valued Contributor I
621 Views
Quoting - Raf Schietekat
I don't know, I didn't feel like speculating about the context at this time, but I don't think I would try copying atomics myself, concurrently or otherwise.


What do you mean?

0 Kudos
Dmitry_Vyukov
Valued Contributor I
621 Views
Quoting - Raf Schietekat
Meanwhile, there's this pipeline thing that I don't care to look at again for at least a month, so maybe you could shine your light on that, too?

There is so many unconnected questions/answers/discussions, what exactly moment do you mean?

0 Kudos
RafSchietekat
Valued Contributor III
621 Views
"What do you mean?" Maybe I should have another look at it (and explain the fifth line), but I wrote these comments in my "Additions to atomic" proposal:

// About copy constructors and copy assignment operators:
// Normally these must be defined together for consistency.
// Atomics cannot have copy constructors because that would also require the definition of a default constructor,
// which would interfere with the expectation that atomics are ready for use right after static initialization;
// any use of a(n implicitly defined) copy constructor should be avoided, however.
// They must have copy assignment operators to invoke store(), because, unlike the situation with constructors,
// a copy assignment operator will be implicitly defined even if another user-defined assignment operator exists,
// and selectively restricting assignment would only invite accidental use of a (non-atomic) implicit definition.
// TODO: double check the last statement

On a related topic, I also wrote this (reformatted):

// default-initialization does not translate to zero-initialization because atomic is non-POD
// because it has a copy assignment operator, which means that putting my_atomic()
// in an initializer list or doing "tbb::atomic a; a = tbb::atomic();"
// is incorrect (it doesn't do what it seems to be expected to do).

What do you think?

"There is so many unconnected questions/answers/discussions, what exactly moment do you mean?"

See "Pipeline" thread: I published a code snapshot, but it doesn't work yet, and I need to take a break from it myself to regain a fresh perspective.
0 Kudos
Dmitry_Vyukov
Valued Contributor I
621 Views
Quoting - Raf Schietekat
"What do you mean?" Maybe I should have another look at it (and explain the fifth line), but I wrote these comments in my "Additions to atomic" proposal:

// About copy constructors and copy assignment operators:
// Normally these must be defined together for consistency.
// Atomics cannot have copy constructors because that would also require the definition of a default constructor,
// which would interfere with the expectation that atomics are ready for use right after static initialization;
// any use of a(n implicitly defined) copy constructor should be avoided, however.
// They must have copy assignment operators to invoke store(), because, unlike the situation with constructors,
// a copy assignment operator will be implicitly defined even if another user-defined assignment operator exists,
// and selectively restricting assignment would only invite accidental use of a (non-atomic) implicit definition.
// TODO: double check the last statement

On a related topic, I also wrote this (reformatted):

// default-initialization does not translate to zero-initialization because atomic is non-POD
// because it has a copy assignment operator, which means that putting my_atomic()
// in an initializer list or doing "tbb::atomic a; a = tbb::atomic();"
// is incorrect (it doesn't do what it seems to be expected to do).

What do you think?



I think that functor's copy ctor must be defined explicitly:


struct functor
{
tbb::atomic x;
...
functor(functor const& f)
{
x.store(f.x.load(relaxed), relaxed);
}
};


0 Kudos
Dmitry_Vyukov
Valued Contributor I
621 Views
Quoting - Raf Schietekat
"There is so many unconnected questions/answers/discussions, what exactly moment do you mean?"

See "Pipeline" thread: I published a code snapshot, but it doesn't work yet, and I need to take a break from it myself to regain a fresh perspective.

I see the attachment in the last post, and I have skimmed over the thread, but I don't get what problem the attachment tries to solve, or what it tries to improve. What it's all about?

0 Kudos
RafSchietekat
Valued Contributor III
621 Views

"I think that functor's copy ctor must be defined explicitly" Maybe, but I just didn't want to speculate on what's going on here (I did think about suggesting "tbb::atomic *sampleVar;", but I decided instead to wait and see).

0 Kudos
RafSchietekat
Valued Contributor III
621 Views
Quoting - Dmitriy Vyukov

I see the attachment in the last post, and I have skimmed over the thread, but I don't get what problem the attachment tries to solve, or what it tries to improve. What it's all about?


Over to "Pipeline"...
0 Kudos
ARCH_R_Intel
Employee
621 Views
Yes, the functor body might be copied by the implementation of parallel_for. The simplest solution is pass sampleVar by reference and make sampleVar atomic, as shown below:

[cpp]class Case {   
  atomic& sampleVar;   
public:   
  void operator() (const blocked_range& range) const {   
    for(int i = range.begin(), i != range.end(), i++) {   
      // do some tasks   
      sampleVar++;   
    }   
  }   
  
  Case(atomic& sampleVar_): sampleVar(sampleVar_) {}   
}; [/cpp]
[cpp][/cpp]

Another option, which works if sampleVar is not being read until the parallel loop completes, is to use parallel_reduce. Using paralle_reduce this way may improve performance, because it allows you to accumulate in local copies of sampleVar and then sum them. Look at file examples/parallel_reduce/primes/primes.cpp in the TBB ditribution. Inside there is a class Sieve that tallies a counter variable "count". You can tally sampleVar in a similar way. Be sure to inspect how method Sieve(Sieve&&,split) initializes a local count and method Sieve::join sums two local counts.
0 Kudos
Alexey-Kukanov
Employee
621 Views
Another option, which works if sampleVar is not being read until the parallel loop completes, is to use parallel_reduce.

A side note: this reminded me of a suggestion I have heard earlier, which was for TBB algorithms, parallel_reduce in particular,to return a copy of the body object. This object can then be assigned to some variable, and it might have a conversion operator so that the following could be written:
int my_sum = parallel_reduce(blocked_range(0,N), MySummationBody(my_array), auto_partitioner());
0 Kudos
Reply