5

I would like to optimize some C++ code. Let have a simplified example:

int DoSomeStuff(const char* data)
{

  while(data != NULL)
  {
    //compute something and use optimization as much as possible
  }
  return result;
}

I know that *data is not changed elsewhere. I mean it's not changed in any other thread, but the compiler cannot know. Is there some way to tell the compiler that data on the pointer are not changed for the entire lifetime of the scope?

UPDATE:

int DoSomeStuff(const volatile char* data)
{

  while(data != NULL)
  {
    //compiler should assume that the data are changed elsewhere
  }
  return result;
}
Tomas Kubes
  • 23,880
  • 18
  • 111
  • 148
  • 4
    You already do that by making it const. – Samuel Jun 13 '14 at 11:36
  • 4
    Unless `data` is a pointer to `(const) volatile char *`, the compiler may already assume that the pointed-to data isn't changed by another thread. –  Jun 13 '14 at 11:36
  • 5
    @Samuel No, `const` doesn't have that meaning. `const` just means read-only, but doesn't tell the compiler the pointed-to memory isn't modified in other ways. For example, `int f(const int *a, int *b) { int c = *a; ++*b; return c - *a; }` must take into account that `a` and `b` may point to the same object. (But modifications by another thread are special, the compiler may assume that that doesn't happen regardless of `const`.) –  Jun 13 '14 at 11:37
  • I don't think the compiler *protects* you from changes from any other thread at all. – Bartek Banachewicz Jun 13 '14 at 11:41
  • 1
    @hvd: If variable `a` in your example is pointing to a read-only memory section (e.g., RO-data), then your code will cause a memory-access violation during runtime. Therefore, in OP's example, I believe that for `const char* data`, the compiler has to assume that `data` is possibly pointing to a read-only memory section. Under this assumption, the compiler may then perform the relevant optimization. – barak manos Jun 13 '14 at 11:43
  • hvd: So the compiler already assumes, that the data are not changed by other thread (because of absense of the volatile keyword) and the optimization are already performed? Am I right. – Tomas Kubes Jun 13 '14 at 11:44
  • 1
    related : [Constants and compiler optimization in C++](http://stackoverflow.com/questions/212237/constants-and-compiler-optimization-in-c) – uchar Jun 13 '14 at 11:51
  • 1
    @qub1n: No ... at least probably no. There is probably no way (let global optimization out of the equation and some easy scope examples) that the compiler/optimizer can be sure that no other piece of code holds a non-const reference on your data. – OnWhenReady Jun 13 '14 at 11:52
  • @barakmanos The pointer *may* point to read-only memory. It may also point to writeable memory. The compiler has to take both possibilities into account. –  Jun 13 '14 at 11:54
  • @qub1n Yes, sort of. Dominik Selzer rightly pointed out that the compiler cannot tell in the general case that the data won't be changed *by functions called from the same thread*. It can and generally does assume that *if no functions are called*, the data won't be changed by another thread. –  Jun 13 '14 at 11:55
  • 3
    @qub1n Your update doesn't apply here: that quote applies to objects *defined* with `const`. It doesn't apply to the objects pointed-to by `const`-qualified pointers, because `const`-qualified pointers may point to objects defined with or without `const`. –  Jun 13 '14 at 12:02
  • I have deleted the incorrect quote. – Tomas Kubes Jun 13 '14 at 12:13

3 Answers3

3

1) Will the compiler optimize if the variable is const ?

According to the standard (7.1.6.1):

A pointer or reference to a cv-qualified type need not actually point or refer
to a cv-qualified object, but it is treated as if it does

So I understand it as: the compiler will optimize all access to a const qualified variable as if it really was a const data and could not be modified by a path to a non const variable.

2) Will the compiler not optimize to protect inter-thread access ?

According to the standard (1.10):

The execution of a program contains a data race if it contains two conflicting
actions in different threads, at least one of which is not atomic, and neither
happens before the other. Any such data race results in undefined behavior.

A conflicting action is defined as:

Two expression evaluations conflict if one of them modifies a memory location
(1.7) and the other one accesses or modifies the same memory location.

The happens-before relationship is complex but as I understand it, unless you explicitly used atomic operations (or mutexes) in your code, accessing the same variable from two threads (one writing, another one reading/writing) is undefined behavior. This case being undefined behavior I don't think the compiler will protect you, and it is free to optimize.

3) Volatile ?

Volatile is designed to prevent optimization (7.1.6.1):

volatile is a hint to the implementation to avoid aggressive optimization
involving the object because the value of the object might be changed by means
undetectable by an implementation

Think about a memory mapped I/O register (like a special input port). Even though you cannot write it (because your hardware implementation is read-only), you cannot optimiza your read accesses (because they will return a different byte each time). This special I/O port may be a temperature sensor for instance.

fjardon
  • 7,921
  • 22
  • 31
  • 1
    Nice answer. I still don't understand why MSVC has __restrict keyword if compiler threat const pointer as it is const. – Tomas Kubes Jun 13 '14 at 12:50
  • @qub1n In the example use of **__restrict**, the pointers are not pointing to const data: http://msdn.microsoft.com/en-us/library/5ft82fed.aspx. So I think this is yet another case :) – fjardon Jun 13 '14 at 12:56
  • In the example code the pointers are const - not pointing to const data, that would be char* const - maybe that would helper the compiler out more – paulm Jun 13 '14 at 13:13
  • @paulm I was speaking about the MSDN example, not yours. – fjardon Jun 13 '14 at 13:22
  • 1
    "So I understand it as: the compiler will optimize all access to a const qualified variable as if it really was a const data and could not be modified by a path to a non const variable." -- Absolutely not. I already gave an example of a perfectly valid function in my comments on the question that would break if your interpretation were correct. "it is treated as if it does" means for purposes of static typing etc. The text in the standard *immediately* following your quote already clears this up. –  Jun 13 '14 at 16:31
3

The compiler is already allowed to assume *data is not modified by another thread. Modifying data in one thread and accessing it in another thread without proper synchronisation is Undefined Behaviour. The compiler is totally free to do anything at all with a program that contains undefined behaviour.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
1

I think what you're saying is that its not aliased anywhere else.

In MSVC you can do:

int DoSomeStuff(const char* __restrict  data)
{

  while(data != NULL)
  {
    //compute something and use optimization as much as possible
  }
  return result;
}

See:

http://msdn.microsoft.com/en-us/library/5ft82fed.aspx

paulm
  • 5,629
  • 7
  • 47
  • 70
  • The MSDN says: `The __restrict keyword indicates that a symbol is not aliased in the **current scope**`. But you only have one argument to your function, how could it be aliased in the current scope ? – fjardon Jun 13 '14 at 13:26
  • const char* bar = data; – paulm Jun 13 '14 at 17:59