Как-будто правило и снятие выделения

" Правило " как если "" дает компилятору право оптимизировать или переупорядочивать выражения, которые не повлияют на вывод и правильность программы при определенных правилах, таких как;

§1.9.5

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

Ссылка выше, указанная в cppreference, специально упоминает специальные правила для значений изменчивых объектов, а также для "новых выражений" в C++14:

У выражения new есть еще одно исключение из правила "как если": компилятор может удалять вызовы заменяемых функций выделения, даже если предусмотрена пользовательская замена и имеет наблюдаемые побочные эффекты.

Я предполагаю, что "заменяемый" вот то, о чем говорится, например, в

§18.6.1.1.2

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

Это правильно, что mem ниже может быть удалено или переупорядочено по правилу "как будто"?

  {
  ... some conformant code // upper block of code

  auto mem = std::make_unique<std::array<double, 5000000>>();

  ... more conformant code, not using mem // lower block of code
  }

Есть ли способ убедиться, что он не удален и остается между верхним и нижним блоками кода? На ум приходит хорошо размещенный volatile (или / или volatile std::array, или left of auto), но поскольку нет чтения mem Я думаю, что даже это не поможет в соответствии с правилом " как будто".

Примечание; Я не смог заставить визуальную студию 2015 оптимизировать mem и распределение на всех.

Пояснение: способ наблюдать это может состоять в том, что вызов распределения для ОС происходит между любыми входами / выходами из двух блоков. Смысл этого для тестовых случаев и / или попыток получить объекты для размещения в новых местах.

1 ответ

Решение

Да; Нет, не в C++.

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

В абстрактной машине auto mem = std::make_unique<std::array<double, 5000000>>(); создает переменную mem, Это, если используется, дает вам доступ к большому количеству doubles упакован в массив. Абстрактная машина может выдать исключение или предоставить вам такое большое количество doubles; либо в порядке.

Обратите внимание, что это законный компилятор C++, чтобы заменить все выделения через new с безусловным throw ошибки выделения (или возврата nullptr для версий без бросков), но это было бы плохим качеством реализации.

В случае, когда он выделен, стандарт C++ не говорит, откуда он. Например, компилятор может использовать статический массив и сделать delete позвоните неоператору (обратите внимание, что, возможно, придется доказать, что он ловит все способы вызова delete в буфере).

Далее, если у вас есть статический массив, если никто не читает и не пишет в него (а конструкцию нельзя наблюдать), компилятор может его устранить.


Тем не менее, многое из вышеперечисленного зависит от того, что компилятор знает, что происходит.

Таким образом, подход состоит в том, чтобы компилятор не мог знать об этом. Ваш код загружает DLL, а затем передает указатель на unique_ptr в эту DLL в тех местах, где вы хотите, чтобы ее состояние было известно.

Поскольку компилятор не может оптимизировать вызовы DLL во время выполнения, состояние переменной должно в основном соответствовать ожидаемому.

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

Упомянутая DLL может быть отдельно написана, чтобы быть noop; или, даже, вы можете проверить некоторое внешнее состояние, и условно загрузить и передать данные в DLL на основе внешнего состояния. Пока компилятор не может доказать, что упомянутое внешнее состояние будет иметь место, он не сможет оптимизировать работу вокруг вызовов, которые не были сделаны. Тогда никогда не устанавливайте это внешнее состояние.

Объявите переменную в верхней части блока. Передайте указатель на него в fake-external-DLL, пока он не инициализирован. Повторите только перед инициализацией, затем после. Затем, наконец, сделайте это в конце блока, прежде чем уничтожить его, .reset() это, затем сделай это снова.

Другие вопросы по тегам