Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - [120]

Шрифт
Интервал

Такие оптимизации корректны, только если память ведет себя нормально. “Особая” память так себя не ведет. Наиболее распространенным видом особой памяти, вероятно, является память, используемая для отображенного на память ввода-вывода.. Вместо чтения и записи обычной памяти, местоположения в такой особой памяти в действительности сообщаются с периферийными устройствами, например внешними датчиками или мониторами, принтерами, сетевыми портами и т.п. Давайте с учетом этого снова рассмотрим код с, казалось бы, избыточными чтениями:

>auto y = x; // Чтение x

>y = x;      // Чтение x еще раз

Если >x соответствует, скажем, значению, которое передает датчик температуры, то второе чтение >x избыточным не является, поскольку температура между первым и вторым чтениями может измениться.

Похожа ситуация с записями, кажущимися излишними. Например, если в коде

>x = 10; // Запись x

>х = 20; // Запись x еще раз

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

Квалификатор >volatile представляет собой способ сообщить компиляторам, что мы имеем дело с такой особой памятью. Для компилятора это означает “не выполняй никаких оптимизаций над операциями с этой памятью”. Так что если переменная >x соответствует особой памяти, она должна быть объявлена как >volatile:

>volatile int x;

Рассмотрим влияние этого квалификатора на последовательность нашего исходного кода:

>auto y = x; // Чтение x

>y = x;      // Чтение x еще раз (не может быть устранено)

>x = 10;     // Запись x (не может быть устранена)

>x = 20;     // Запись x еще раз

Это именно то, чего мы хотим, когда >x представляет собой отображение в память (или отображается в ячейке памяти, совместно используемой разными процессами и т.п.).

Вопрос на засыпку: какой тип у в последнем фрагменте кода: >int или >volatile int[28]?

Тот факт, что кажущиеся избыточными загрузки и бессмысленные сохранения должны оставаться на месте при работе с особой памятью, объясняет, кстати, почему для такого рода работы не подходят объекты >std::atomic. Компиляторам разрешается устранять такие избыточные операции у >std::atomic. Код написан не в точности так же, как и для >volatile, но если мы на минуту отвлечемся от этого и сосредоточимся на том, что компиляторам разрешается делать, то можно сказать, что концептуально компиляторы могут, получив код

>std::atomic<int> x;

>auto y = x; // Концептуально читает x (см. ниже)

>y = x;      // Концептуально читает x еще раз (см. ниже)

>x = 10;     // Записывает x

>х = 20;     // Записывает x еще раз

оптимизировать его до

>auto y = x; // Концептуально читает x (см. ниже)

>x = 20;     // Записывает x

Очевидно, что это неприемлемое поведение при работе с особой памятью.

Но если >x имеет тип >std::atomic, ни одна из этих инструкций компилироваться не будет:

>auto y = x; // Ошибка!

>y = x;      // Ошибка!

Дело в том, что копирующие операции в >std::atomic удалены (см. раздел 3.5). И не зря. Рассмотрим, что произошло бы, если бы инициализация у значением x компилировалась. Поскольку >x имеет тип >std::atomic, тип у был бы также выведен как >std::atomic (см. раздел 1.2). Ранее я отмечал, что одна из лучших возможностей >std::atomic заключается в атомарности всех их операций, но чтобы копирующее конструирование >y из >x было атомарным, компиляторы должны генерировать код для чтения >x и записи >y как единую атомарную операцию. В общем случае аппаратное обеспечение не в состоянии это сделать, так что копирующее конструирование типами >std::atomic не поддерживается. Копирующее присваивание удалено по той же причине, а потому присваивание >x переменной >y также не компилируется. (Перемещающие операции не объявлены в >std::atomic явно, так что в соответствии с правилами генерации специальных функций, описанных в разделе 3.11, >std::atomic не предоставляет ни перемещающего конструирования, ни перемещающего присваивания.)

Можно получить значение >x в переменную >y, но это требует использования функций- членов >load и >store типа >std::atomic. Функция-член >load атомарно считывает значение >std::atomic, а функция-член >store атомарно его записывает. Для инициализации >y значением >x, после которой выполняется размещение значения >x в >y, код должен иметь следующий вид:

>std::atomic y(x.load()); // Чтение x

>y.store(x.load());            // Чтение x еще раз

Этот код компилируется, но тот факт, что чтение >x (с помощью >x.load()) является отдельным от инициализации или сохранения значения вызовом функции, делает очевидным то, что нет никаких оснований ожидать, что целая инструкция будет выполняться как единая атомарная операция.

Компиляторы могут “оптимизировать” приведенный код, сохраняя значение >x в регистре вместо двойного его чтения:

>register = x.load();          // Чтение x в регистр


>std::atomic y(register); // Инициализация y

>                              // значением регистра

>y.store(


Еще от автора Скотт Мейерс
Эффективное использование STL

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


Как функции, не являющиеся методами, улучшают инкапсуляцию

Когда приходится инкапсулировать, то иногда лучше меньше, чем большеЯ начну со следующего утверждения: Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда Вы думаете об использовании инкапсуляции, Вы должны думать том, чтобы не использовать методы.Удивлены? Читайте дальше.


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Геймдизайн. Рецепты успеха лучших компьютерных игр от Super Mario и Doom до Assassin’s Creed и дальше

Что такое ГЕЙМДИЗАЙН? Это не код, графика или звук. Это не создание персонажей или раскрашивание игрового поля. Геймдизайн – это симулятор мечты, набор правил, благодаря которым игра оживает. Как создать игру, которую полюбят, от которой не смогут оторваться? Знаменитый геймдизайнер Тайнан Сильвестр на примере кейсов из самых популярных игр рассказывает как объединить эмоции и впечатления, игровую механику и мотивацию игроков. Познакомитесь с принципами дизайна, которыми пользуются ведущие студии мира! Создайте игровую механику, вызывающую эмоции и обеспечивающую разнообразие.


Обработка событий в С++

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


MFC и OpenGL

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


Симуляция частичной специализации

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


Питон — модули, пакеты, классы, экземпляры

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