Я реализую очередь с одним производителем и одним потребителем, в которой один поток ждет, пока глобальная очередь будет заполнена другим потоком, например:

while (queue.head == queue.tail);

Когда я скомпилировал программу gcc -O0, она работала хорошо. Но когда он был скомпилирован с помощью gcc -O1, возникла тупиковая ситуация. Затем я просмотрел код сборки и обнаружил, что последняя версия проверяла (queue.head == queue.tail) только один раз, если это не так, то перешел в мертвый цикл и больше никогда не проверял.

Я также пытался объявить очередь как изменчивую, но это не сработало. Как сделать так, чтобы gcc знал, что очередь распределяется между потоками, и прекратить подобную оптимизацию? Большое спасибо.

P.S.

1 В однопоточной программе такая оптимизация - это нормально. Но в моей программе queue.tail может быть изменен другим потоком.

2 Моя очередь была объявлена ​​так:

typedef struct {
    struct my_data data[MAX_QUEUE_LEN];
    int head;
    int tail;
} my_queue_t;

volatile my_queue_t queue;

3 Я также пытался объявить голову и хвост (но не всю структуру) как изменчивые, это не сработало. Но после того, как я объявляю queue, head, tail все как volatile, он работает. Итак, следует ли объявить volatile для всех подобных переменных?

1
ZelluX 13 Июн 2009 в 22:19
1
Что именно вы имеете в виду под "тупиковой ситуацией"? В любом случае, почему должен это проверить еще раз? Ваш код говорит, что нужно продолжать цикл, пока они равны. Если это не так, он завершит цикл, потому что это то, что вы просили его сделать.
 – 
jalf
13 Июн 2009 в 22:22
2
Не могли бы вы показать определение очереди ... включая то, как вы пытались объявить их изменчивыми.
 – 
Chris Arguin
13 Июн 2009 в 22:23
Я не знаю, понимаю ли я смысл ... но предполагается ли, что этот код работает только в многопроцессорной системе, где вы будете ждать в цикле, пока в другом процессоре выполняется инструкция, которая заставляет этот цикл прерваться ... что-то как счет спина ?? по этой причине вы пытаетесь сделать его нестабильным?
 – 
Naveen
13 Июн 2009 в 22:27
Пожалуйста, не пытайтесь использовать HTML-теги для форматирования вашего кода - используйте кнопку 1010
 – 
anon
13 Июн 2009 в 22:31

2 ответа

Лучший ответ

Я скомпилировал следующий код:

struct my_data {
    int x;
};

typedef struct {
    struct my_data data[5];
    int head;
    int tail;
} my_queue_t;

volatile my_queue_t queue;

int main() {
    while (queue.head == queue.tail);
}

С участием :

g++ -S -c -O1  th.cpp

Который (для цикла while) дал следующий результат:

       movl    $_queue+20, %edx
       movl    $_queue+24, %eax
L2:
       movl    (%edx), %ebx
       movl    (%eax), %ecx
       cmpl    %ecx, %ebx
       je      L2

Где голова и хвост загружаются и тестируются внутри цикла. Не могли бы вы опубликовать, какой ассемблер вы получаете?

Изменить: изменение заголовка и хвоста изменчивыми в объявлении структуры вместо объявления экземпляра структуры изменчивым привело к созданию идентичного кода.

4
13 Июн 2009 в 23:07
Мне очень жаль, что я ошибся, посчитав объявление volatile бесполезным. Я изменил это удаление в заголовочном файле, который не был включен в качестве зависимости в make-файл, поэтому изменение кода просто не применялось.
 – 
ZelluX
13 Июн 2009 в 23:24
Если вы удалите объявления volatile, будет ли цикл оптимизирован?
 – 
Laurynas Biveinis
14 Июн 2009 в 02:37
Нет, цикл остается, но сравнение выполняется только один раз.
 – 
anon
14 Июн 2009 в 03:12

Вы пробовали объявить голову / хвост изменчивыми?

3
Assaf Lavie 13 Июн 2009 в 22:32