__restrict on C++ references?

Started by
10 comments, last by Antheus 14 years ago
Both gcc and VC++ support __restrict in C++ on pointers, but only gcc supports it on references. Can someone explain why MS might have chosen to only support it on pointers? Since my code uses references whenever possible instead of pointers, I am a bit concerned about performance, and whether I should change performance critical functions from using & to *__restrict Trying __restrict & gives warning C4227: anachronism used : qualifiers on reference are ignored in VC++.
"But who prays for Satan? Who, in eighteen centuries, has had the common humanity to pray for the one sinner that needed it most?" --Mark Twain

~~~~~~~~~~~~~~~Looking for a high-performance, easy to use, and lightweight math library? http://www.cmldev.net/ (note: I'm not associated with that project; just a user)
Advertisement
Unfortunately I can't tell you why MS might have chosen to only support __restrict on pointers.

As for your performance concerns: If you have a function/method that has performance problems that concern your and those problems go away when you change the reference to a restricted pointer, then, by all means, go for it!

I bet those cases will be very very rare tough.
Because references cannot be reseated without undefined behavior, so compiler can statically determine aliasing.

While
Foo & x = *some_pointer_perhaps_Foo;
can be written, the fact that some_pointer could be illegal makes me think that this is not a relevant case.

Quote:whether I should change performance critical functions

Under certain circumstances, a certain small subset of certain types of data structures doing certain limited set of operations there might be a 10-20% improvement when aliasing can be ignored.

Those cases will almost always involve a manually allocated aligned POD array. So it's a non-argument.
Thanks, I'll write it off as a non-concern for my brain in that case.

I have another question: does __restrict imply only non-mutual aliasing between __restrict pointers in a block (and thus doesn't make sense being used on a single pointer in a block), or does it indicate that there is no aliasing whatsoever, including by pointers in external scopes?

For example, if I'm ping-ponging between two stringstreams (as a timer thread may be flushing one of them to a log file periodically, while writing continues to the other), should I make the pointer that points to either buffer alternatively __restrict in the scope where it is the only access to that area?
Say at the end of a flush _buffer = _buffers + (i = (++i % 2));
then when using buffer for several stream insertions in a log function, would it make sense to stringstream *const _restrict buffer(_buffer); and write to that?

Additionally, does it ever make sense if a class has a single pointer member, that member to be qualified with __restrict? Would any usage of a member thus declared in a member function be assumed non-aliased?

That is, if class A { int *_restrict p; void B(void) { /* work with p */ }; does that mean that in B, p would be accessed as if it were a variable declared as int *_restrict p(this->p); ?

[Edited by - Prune on April 22, 2010 3:20:22 PM]
"But who prays for Satan? Who, in eighteen centuries, has had the common humanity to pray for the one sinner that needed it most?" --Mark Twain

~~~~~~~~~~~~~~~Looking for a high-performance, easy to use, and lightweight math library? http://www.cmldev.net/ (note: I'm not associated with that project; just a user)
C++ features are somewhat incompatible with this type of optimization, so classes should be out of the picture for anything that is so sensitive to optimization.

struct Foo {  int x;  int *y;   void foo() {    for (int i = 0; i < x; i++) y = i;   }  void foo_fixed() {    int n = x;    for (int i = 0; i < n; i++) y = i;   }};
The above might be needed in case of y = &x?

So eventually one ends up with C anyway:
void foo(int x, int * __restrict y) {  // yay, no aliasing  for (int i = 0; i < x; i++) y = i;}
In above case, restrict is redundant, since x gets passed by value anyway, so aliasing cannot occur. In former case, even though only one pointer was used, aliasing could still occur.

Quote:Additionally, does it ever make sense if a class has a single pointer member, that member to be qualified with __restrict? Would any usage of a member thus declared in a member function be assumed non-aliased?
See above.

Quote:ping-ponging between two stringstreams (as a timer thread may be flushing one of them to a log file periodically, while writing continues to the other), should I make the pointer that points to either buffer alternatively __restrict in the scope where it is the only access to that area?

Honestly, I have no clue what this is supposed to do, or how it's supposed to work, or why, but since it contains threads, restrict and pointer arithmetic in same sentence makes me want to kill it with fire.

Quote:stringstream *const _restrict buffer(_buffer); and write to that?

I suppose. I don't know what it would accomplish, and isn't likely to break anything, but when it will, it will be hell to track down.

In practice, if performance really matters that much (in logging system it does not), then use C. Free functions, noalias or restrict if multiple parameters are passed, fully local scope, states passed by value.

Logging system is IO bound. Always. Aliasing optimizations simply don't matter. They make a difference when doing complex math or physics, preferably using SIMD, and operations are not memory bandwidth bound (quite likely with SIMD).

Restrict is a very obscure optimization applicable to a handful of corner cases to squeeze out extra 5-20% of inner computation loop. But increasingly, systems today are IO bound, either disk, network or memory, especially on out-of-order CPUs.

And considering the miniscule or zero gains, the chance of code producing invalid results without breaking is hardly worth it.
The logging system is not really IO bound in my case. Because I did not want to have a flush to disk stall the main loop, I handle the flushes in a separate thread. I'm simply using two stringstream objects for plain double-buffering. The actual calls to write to the log by other threads are memory bound--inserting stuff in a stringstream (only would block if the buffer overflows before the other one is flushed, or multiple threads try to write to the buffer simultaneously).

When you wrote "The above might be needed in case of y = &x?" is declaring y as restrict in the struct declaration giving the same guarantee as if a restrict pointer to it was created inside y::foo()? I'm not completely clear on the semantics.

By the way, consider that any pointer argument of a member function could alias the this pointer, so it seems to me that any pointer argument of a member functions should be __restricted.

[Edited by - Prune on April 22, 2010 5:50:31 PM]
"But who prays for Satan? Who, in eighteen centuries, has had the common humanity to pray for the one sinner that needed it most?" --Mark Twain

~~~~~~~~~~~~~~~Looking for a high-performance, easy to use, and lightweight math library? http://www.cmldev.net/ (note: I'm not associated with that project; just a user)
Quote:Original post by Prune
The logging system is not really IO bound in my case. Because I did not want to have a flush to disk stall the main loop, I handle the flushes in a separate thread. I'm simply using two stringstream objects for plain double-buffering.


It is IO bound - this why separate thread, IO is blocking the application. Consider an application that generates logs in infinite loop - which will stall first, CPU or storage?

Out-of-thread logging is actually a very bad idea. When an application process crashes, it takes all threads with it. So any logs pending will be lost - and those will be the most valuable ones. Logging system must, for that reason, call fsync() and wait until that call returns, or fault/exit immediately.

C++ in itself is not required, and very likely does not call fsync(), which is different from flush or endl.

Quote:When you wrote "The above might be needed in case of y = &x?" is declaring y as restrict in the struct declaration giving the same guarantee as if a restrict pointer to it was created inside y::foo()? I'm not completely clear on the semantics.


Yay for big can of worms... And stuff I had hoped I would be able to forget long ago...

General rule is that pointers of different types cannot alias. In above case, both are typed as ints, which means they can.

If types are different and incompatible, then compiler will assume that they cannot alias.

There is also a rule that any piece of memory may only ever be occupied by one single type. In practice this means that restrict on members applies to union, where different types violate this rule. But unions are covered by too many exceptions anyway, from alignment to padding to access rules...

restrict also doesn't make sense for fstream or stringstream, since those countain pointer to buffer, and restrict applies only to memory occupied by first dereference of an instance. So in case of a struct T it merely guarantees that bytes from t..t+sizeof(T) do not alias anything, not that any pointer contained in there does not. In case of STL classes, this makes restrict irrelevant, except in case of 16-byte inplace buffer of std::string MVC uses.

Trying to apply restrict to stringstream * does nothing, it merely claims that instance of stringstream isn't aliased, which in case of 'new' allocation would be the case anyway. Anything auto-allocated cannot alias either. A direct dereference of a pointer might, even without array semantics, but there type analysis determines they cannot.

I also believe that restrict is a C99 qualifier which doesn't exist in C++, code containing restrict might not even be valid C++, hence the __restrict extension.


Short version: restrict isn't worth the bother, except where it really matters.
Quote:Original post by Prune
Additionally, does it ever make sense if a class has a single pointer member, that member to be qualified with __restrict? Would any usage of a member thus declared in a member function be assumed non-aliased?

That is, if class A { int *_restrict p; void B(void) { /* work with p */ }; does that mean that in B, p would be accessed as if it were a variable declared as int *_restrict p(this->p); ?


In such a case, you would restrict the member-function itself by applying the restrict keyword after the function signature in the declaration/definition. Restrict on Member Functions
restrict is useful to avoid load-hit-stores on the 360 and PS3. Mike Acton has a nice article which details that: Cellperformance

and yes, restrict comes from C99.

HTH,
-tiv
Quote:Original post by Antheus
Out-of-thread logging is actually a very bad idea. When an application process crashes, it takes all threads with it. So any logs pending will be lost - and those will be the most valuable ones. Logging system must, for that reason, call fsync() and wait until that call returns, or fault/exit immediately.

I force a flush in the thread that writes when the error level exceeds some threshold; only warnings and notifications are buffered up (and I have a lot more of those than errors and fatal errors). Another issue for me is that I also write the logs to gzstream, not fstream, so closing the file has some extra work (finishing the compression).
Calling fsync() on an error sounds like a good idea, but I see no way to get the file descriptor from an fstream (from which gzstream is derived), and definitely no portable way (I need to be able to build with MSVC, ICC, GCC, and ICL).
"But who prays for Satan? Who, in eighteen centuries, has had the common humanity to pray for the one sinner that needed it most?" --Mark Twain

~~~~~~~~~~~~~~~Looking for a high-performance, easy to use, and lightweight math library? http://www.cmldev.net/ (note: I'm not associated with that project; just a user)
Quote:Original post by Prune
I force a flush in the thread that writes when the error level exceeds some threshold; only warnings and notifications are buffered up (and I have a lot more of those than errors and fatal errors). Another issue for me is that I also write the logs to gzstream, not fstream, so closing the file has some extra work (finishing the compression).
Calling fsync() on an error sounds like a good idea, but I see no way to get the file descriptor from an fstream (from which gzstream is derived), and definitely no portable way


As said, this type of logging is highly undesirable. If application crashes, the stream will be corrupt. Same goes for XML-formatted logs. Unless application closes the log cleanly, the file will not be valid XML.

Then there's corner cases - what happens if partition runs out of space. What if it's a compressed partition. What if it's a network shared drive.

Generally, reliable logging would be done via C (which is almost all OS APIs are written in), using raw file descriptors or equivalent, using whatever OS mechanisms are available to force a file system flush. To conserve space, logs are rotated, and if they need to be stored, some form of ETL is used, perhaps hooked to file system change notification which automatically compresses and stores old logs.

Datetimes should also be in UTC, ordered: YEAR-MONTH-DAY-HOUR-MINUTE-SECOND, with each entry zero padded to maximum number of digits. This ensures they are naturally sorted.

The above is very friendly to 'more' and 'less' and similar tools.

Quote:(I need to be able to build with MSVC, ICC, GCC, and ICL).
That doesn't matter, the target OS does, since that will have the platform-specific flush syscall.

This topic is closed to new replies.

Advertisement