Preciso declarar uma variável de temporizador de atraso como volátil mesmo se eu acessá-la de outro módulo

5

Esta é uma questão mais sobre o uso de volátil para evitar a otimização do que sobre o armazenamento em cache de gravação / leitura de uma variável. Particularmente, as variáveis de atraso do temporizador, uma vez que eu não quero declarar tudo volátil e otimização de resíduos ao fazê-lo.

Primeiramente, sei que o snippet a seguir não funcionará se a otimização estiver ativada, a menos que eu declare i volatile:

int i;
for(i=0; i<LARGE_NUM; i++); //Delay for a bit

E se o loop invocar uma chamada de função e aguardar em vez disso, o compilador examinará os arquivos de origem e otimizará a chamada? Estou perguntando isso porque alguns compiladores oferecem compilação de vários arquivos.

//A.c
#include "B.h"    //B.h has declaration for dec(int)

...
int i;
for(i=LARGE_NUM; i>0; i=dec(i));
...

//B.c

int dec(int a){
  return a-1;
}

Que tal algo um pouco mais complexo e envolve interrupção de hardware?

//A.c
#include "B.h"

...
timer_start(LARGE_NUM);
while(timer_busy());
...

void hardware_timer_isr(void){    //Hardware timer interrupt
    timer_tick();
}

//B.c

static int time=0;

void timer_start(int t){
    time = t;
}

int timer_busy(void){
    return time>0;
}

void timer_tick(void){
    if(time > 0)
        time--;
}

Eu precisarei declarar as variáveis como voláteis em qualquer um dos casos? Ou há alguma coisa em particular que eu também deva procurar?

    
por Pyxzure 23.06.2016 / 10:15
fonte

1 resposta

3

O compilador opera sob a regra as-if , que permite todas e quaisquer transformações de código que don ' t mudar o comportamento observável do programa.

[C ++ 14: 1.5 / 8]

The least requirements on a conforming implementation are:

  • Access to volatile objects are evaluated strictly according to the rules of the abstract machine.
  • At program termination, all data written into files shall be identical to one of the possible results that execution of the program according to the abstract semantics would have produced.
  • The input and output dynamics of interactive devices shall take place in such a fashion that prompting output is actually delivered before a program waits for input. What constitutes an interactive device is implementation-defined.

These collectively are referred to as the observable behavior of the program.

[C11 5.1.2.3.6 Execução do programa] tem uma redação semelhante:

The least requirements on a conforming implementation are:

  • Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine.
  • At program termination, all data written into files shall be identical to the result that execution of the program according to the abstract semantics would have produced.
  • The input and output dynamics of interactive devices shall take place as specified in 7.21.3. The intent of these requirements is that unbuffered or line-buffered output appear as soon as possible, to ensure that prompting messages actually appear prior to a program waiting for input.

This is the observable behavior of the program.

Um atraso não é considerado um comportamento observável e o primeiro exemplo pode ser "otimizado" para um programa vazio.

Note que, para permitir transformações do compilador, como a remoção de loops vazios (mesmo quando a terminação não pode ser provada), os padrões C ++ 14 dizem:

[C ++ 14: 1.10 / 24]

The implementation may assume that any thread will eventually do one of the following:

  • terminate,
  • make a call to a library I/O function,
  • access or modify a volatile object, or
  • perform a synchronization operation or an atomic operation.

[ Note: This is intended to allow compiler transformations such as removal of empty loops, even when termination cannot be proven. —end note ]

(veja também O C / C ++ oferece alguma garantia sobre o tempo mínimo de execução? )

O segundo exemplo é mais difícil para o compilador porque geralmente é incapaz de analisar o código de uma biblioteca externa para determinar se ele executa / não executa E / S ou acesso volátil. No entanto, o código de biblioteca de terceiros vinculado estaticamente pode estar sujeito a otimização de tempo de link . uma organização de vários arquivos não é uma barreira intransponível.

O terceiro exemplo não introduz nada de novo:

  1. a entrada do vetor de interrupção é inicializada na inicialização do programa
  2. que envolve pegar o endereço da função do manipulador e é suficiente para proteger hardware_timer_isr de ser otimizado para fora
  3. mas a variável time não é designada manualmente como uma variável que pode ser alterada por manipuladores de interrupção, de forma que as instruções:

     timer_start(LARGE_NUM);
     while(timer_busy());
    

    não tem um comportamento observável e pode ser otimizado (consulte Por que o compilador não otimiza o código de interrupção? para mais detalhes).

Se você precisar de um atraso, use std::sleep_for ou std::sleep_until .

OBSERVAÇÕES ADICIONAIS

What if I deliberately put a while(1); or a similarly obfuscated infinite loop to intentionally halt the program? According to C++14 1.10/24 above, even though the termination can not be proven, the loop itself does not change the observable behavior of the program and thus can be legally removed, right?

O motivo da nota ( "destina-se a permitir transformações do compilador, como a remoção de loops vazios, mesmo quando a terminação não pode ser comprovada" ) não há como detectar laços infinitos universalmente e a incapacidade de provar a terminação dificulta compiladores que poderiam, de outra forma, fazer transformações úteis (existe uma bom exemplo em N1528: Por que comportamento indefinido para loops infinitos? ).

Para a linguagem C ++, a situação é descrita em:

Para a linguagem C C11 fornece uma exceção para controlar expressões que são expressões constantes .

    
por 23.06.2016 / 12:38
fonte