Точки последовательности предотвращают переупорядочение кода через критические границы раздела?

Я работал на этой работе прежде. Я провел чуть более чем два года на звере прежней версии, который очень похож. Потребовалось два из нас более чем год только для стабилизации всего (это, все еще повредился, но это лучше).

Первая вещь - получает исключение, входящее в приложение, если это уже не существует. Мы использовали FogBugz, и нам потребовался приблизительно месяц для получения создания отчетов, интегрированного в наше приложение; это не было прекрасно сразу же, но это сообщало об ошибках автоматически. Обычно довольно безопасно реализовать блоки try-catch во всех Ваших событиях, и это покроет большинство Ваших ошибок.

Оттуда исправляют ошибки, которые входят сначала. Тогда ведите небольшие бои, особенно бои на основе ошибок. Если Вы исправляете ошибку, которая неожиданно влияет на что-то еще, осуществите рефакторинг тот блок так, чтобы он был разъединен от остальной части кода.

Это примет некоторые крайние меры для перезаписи большого, critical-to-company-success приложение, неважно, как плохо это. Даже Вы получаете разрешение сделать так, Вы будете проводить слишком много времени, поддерживая унаследованное приложение, чтобы сделать любые успехи на перезаписи так или иначе. Если Вы сделаете много маленьких рефакторингов, то в конечном счете или большие не будут настолько большими, или у Вас будут действительно хорошие фундаментальные классы для Вашей перезаписи.

Одна вещь отнять это - то, что это - большой опыт. Это будет печально, но Вы изучите много.

17
задан Peeter Joot 23 October 2009 в 19:59
поделиться

5 ответов

Короче говоря, компилятору разрешено переупорядочивать или преобразовывать программу по своему усмотрению, пока наблюдаемое поведение на виртуальной машине C ++ не изменится. В стандарте C ++ нет концепции потоков, поэтому эта фиктивная виртуальная машина выполняет только один поток. И на такой воображаемой машине нам не нужно беспокоиться о том, что видят другие потоки. Пока изменения не изменяют результат текущего потока, все преобразования кода действительны, включая переупорядочение доступа к памяти по точкам последовательности.

понимание того, почему изменчивость не является требованием для Данные, защищенные мьютексом, на самом деле являются частью этого вопроса

Volatile гарантирует одно, и только одно: операции чтения из изменчивой переменной будут считываться из памяти каждый раз - компилятор не предполагает, что значение можно кэшировать в регистре. Точно так же записи будут записываться в память. Компилятор не будет хранить его в регистре «какое-то время, прежде чем записать в память».

Но это все. Когда происходит запись, будет выполнена запись, а при чтении будет выполнено чтение. Но это не гарантирует ничего относительно , когда это чтение / запись будет иметь место. Компилятор может, как обычно, переупорядочивать операции по своему усмотрению (до тех пор, пока он не меняет наблюдаемое поведение в текущем потоке, о котором знает воображаемый ЦП C ++). Столь изменчивость на самом деле не решает проблему. С другой стороны, он дает гарантию того, что мы не т действительно нужно. Нам не нужно, чтобы каждая запись в переменную записывалась немедленно, мы просто хотим убедиться, что они записываются до пересечения этой границы. Ничего страшного, если они кэшируются до этого момента - и аналогично, как только мы пересекли границу критического раздела, последующие записи могут быть кэшированы снова, что нам нужно, - пока мы не пересечем границу в следующий раз. Таким образом, volatile предлагает слишком строгую гарантию, которая нам не нужна, но не предлагает ту, которая нам нужна (порядок чтения / записи не будет переупорядочен)

Итак, чтобы реализовать критические секции, нам нужно полагаться на магию компилятора. Мы должны сказать ему: «Хорошо, забудьте на мгновение о стандарте C ++, мне все равно, какие оптимизации он позволил бы, если бы вы строго следовали этому стандарту.

11
ответ дан 30 November 2019 в 14:00
поделиться

No, sequence points do not prevent rearranging of operations. The main, most broad rule that governs optimizations is the requirement imposed on so called observable behavior. Observable behavior, by definition, is read/write accesses to volatile variables and calls to library I/O functions. These events must occur in the same order and produce the same results as they would in the "canonically" executed program. Everything else can be rearranged and optimized absolutely freely by the compiler, in any way it sees fit, completely ignoring any orderings imposed by sequence points.

Of course, most compilers are trying not to do any excessively wild rearrangements. However, the issue you are mentioning has become a real practical issue with modern compilers in recent years. Many implementations offer additional implementation-specific mechanisms that allow user to ask the compiler not to cross certain boundaries when doing optimizing rearrangements.

Since, as you are saying, the protected data is not declared as volatile, formally speaking the access can be moved outside of the protected region. If you declare the data as volatile, it should prevent this from happening (assuming that mutex access is also volatile).

4
ответ дан 30 November 2019 в 14:00
поделиться

см. Кое-что в [linux-kernel] /Documentation/memory-barriers.txt

0
ответ дан 30 November 2019 в 14:00
поделиться

Точки последовательности C / C ++ встречаются, например, когда ';' встречается. В этот момент должны возникнуть все побочные эффекты всех операций, которые ему предшествовали. Однако я совершенно уверен, что под «побочным эффектом» подразумеваются операции, которые являются частью самого языка (например, z увеличивается в 'z ++'), а не эффекты на более низких / более высоких уровнях (например, что на самом деле делает ОС что касается управления памятью, управления потоками и т. д. после завершения операции)

Это вроде как ответ на ваш вопрос? Я действительно хочу сказать, что AFAIK концепция точек последовательности на самом деле не имеет ничего общего с побочными эффектами, о которых вы говорите.

hth

1
ответ дан 30 November 2019 в 14:00
поделиться

Давайте посмотрим на следующий пример:

my_pthread_mutex_lock( &m ) ;
someNonVolatileGlobalVar++ ;
my_pthread_mutex_unlock( &m ) ;

Функция my_pthread_mutex_lock () просто вызывает pthread_mutex_lock (). Используя my_pthread_mutex_lock (), я уверен, что компилятор не знает, что это функция синхронизации. Для компилятора это просто функция, и для меня это функция синхронизации, которую я могу легко переоснять. Потому что соменонволатилеглобальвар - глобальный, я ожидал, что компилятор не двигает соменонволатилеглобальвар ++ за пределами критического раздела. Фактически, благодаря наблюдаемым поведению , даже в ситуации с одной резьбой, компилятор не знает, если функция до того, и то, как после этой инструкции изменяют глобальные VAR. Итак, чтобы сохранить наблюдаемое поведение правильным, он должен держать заказ выполнения, как написано. Я надеюсь, что Pthread_mutex_lock () и pthread_mutex_unlock () также выполняют Барьеры памяти .

Я прав?

Если я пишу:

my_pthread_mutex_lock( &m ) ;
someNonVolatileGlobalVar1++ ;
someNonVolatileGlobalVar2++ ;
my_pthread_mutex_unlock( &m ) ;

Я не могу знать, какой из двух переменных увеличивается первым, но это нормально не проблема.

Теперь, если я пишу:

someGlobalPointer = &someNonVolatileLocalVar;
my_pthread_mutex_lock( &m ) ;
someNonVolatileLocalVar++ ;
my_pthread_mutex_unlock( &m ) ;

или

someLocalPointer = &someNonVolatileGlobalVar;
my_pthread_mutex_lock( &m ) ;
(*someLocalPointer)++ ;
my_pthread_mutex_unlock( &m ) ;

- это компилятор, делающий то, что ожидает, что неподвижный разработчик?

2
ответ дан 30 November 2019 в 14:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: