Мирового сговора не было. Так вот написалось… :) Тем более есть подозрение, что скоро название Intel 64 станет гораздо популярнее названия AMD64. Intel вкладывает усилия. чтобы везде использовать эти названия. И лучше примкнуть к этому направлению, чем продолжать использовать не модное название. :)
Систем с общей памятью сейчас большинство. Я имею в виду машины обыкновенных пользователей. И именно по этому технология OpenMP должна стать весьма популярной среди прикладных программистов. Для систем с распределенной памятью давно существуют другие технологии, такие как MPI. И программирование под такие системы освоено гораздо лучше. Так что думаю не стоит переживать, что OpenMP не подходит для таких систем. Технологию OpenMP занимает новую быстро растущую нишу многоядерных систем, где крайне удобна.
Подведем итоги. Были быстро найдены все ошибки. Даже неинтересно как-то. :)
1) sizeof(int) + sizeof(void *) не равно sizeof(MyStruct). Это связано с выравниванием полей в структурах. В результате на 64-битной системе выделяется меньше памяти, чем необходимо. Обратите внимание, что если исправить все остальные ошибки, программа успешно работает. То есть перетирается некоторая пока неиспользуемая память. Это может быть весьма неприятно, так как усложняет диагностику подобных ошибок.
2) Условие result != unsigned(-1) на 64-битной системе всегда истинно. Схожие ошибки можно нередко встретить в программах активно работающих со строками. Образуются они так. В начале пишется код:
size_t result = str.find(«5»);
if (result != -1)
Он успешно работает на 32-битной системе. И хотя это плохой стиль, он будет успешно работать и на 64-битной системе. Иногда компиляторы/анализаторы предупреждают пользователя о сравнении знаковых и беззнаковых значений, что чревато определенным видом ошибок. И помятуя об этом программист приписывает некорректное приведение типов к unsigned. Оно подавляет предупреждения, но вносит ошибку, которая потом проявится на 64-битной системе.
3) Конструкция throw p — A; генерирует исключение, используя тип ptrdiff_t. Это приводит к тому, что на 64-битной системе данное исключение не будет перехвачено с помощью catch (int position) {}. Причина в том, что ptrdiff_t и int совпадают в 32-битной системе и не совпадают в 64-битной.
А теперь та долгожданная реклама, о которой так много говорят. Если запустить анализатор Viva64 входящий в состав PVS-Studio то он выдаст на рассматриваемый код следующие предупреждения:
error V119: More than one sizeof() operators are used in one expression. r:\...\example_x64_bug_01.cpp 22
error V112: Dangerous magic number 4 used: p = A; p <= &A[4]; p++). r:\...\example_x64_bug_01.cpp 31
error V115: Memsize type used for throw. r:\...\example_x64_bug_01.cpp 34
error V104: Implicit type conversion to memsize type in an arithmetic expression. r:\...\example_x64_bug_01.cpp 40
Анализатор обнаружил все ошибки, а также предупредил о наличии опасной константы «4», что в прочем является для данного кода ложным срабатыванием.
Кстати, с 64-битными программами есть следующий интересный эффект. Пока у производителя ПО нет 64-битной версии, он утверждает, что она и не очень нужна. Например, когда не было 64-битного Adobe Photoshop, то были записи и обсуждения, в которых говорилось, что и не так это нужно. А как появился 64-битный Photoshop, то теперь везде его реклама и рассказывается о повысившейся производительности. :)
Сейчас у меня относительно свеже поставленная 64-битная Windows 7. Из программ запущена только Opera для просмотра этой страничке. В фоне работают такие приложения как Касперский, аська, лингво. Но это минимум, который действительно необходим. Ну и плюс конечно запущены разные сервисы и так далее. Что именно не знаю, что система посчитала нужны, то и запущено. Я ничего не включал и не отключал. Так вот, сейчас при запуске Task manager он пишет, что используется уже 1,47 ГБ памяти (всего в системе установлено 8 ГБ). Так что вот я еще ничего не запустил, а 1,5 ГБ уже израсходовалось. :)
Можно поменять int на unsigned и говорить об UINT_MAX. Или заменить size_t на ptrdiff_t. По поводу сравнения знакового и беззнакового — да ругнется. Немного неудачно типы в примере выбрал.
Разные интересные примеры можно посмотреть в нашем блоге. Вот выборка записей по 64-битности. Блог регулярно пополняется.
Правда не очень понятна актуальность адресации массивов больше INT_MAX.
Я работал с подобными задачами, где это удобно. Хотя конечно не скажу, что это повседневные задачи. Еще раз замечу, что 64-битные ошибки проявляются не обязательно на больших массивах. Разные ссылки я уже приводил выше.
Кстати, если есть желание, можно поиграть здесь в игру. Я буду приводить различные примеры кода, а участники будут пытаться найти в них ошибки силой мысли. Ошибки будут на тему 64-битности, параллельности и просто на внимательность.
Вам поможет проект PortSample входящий в состав дистрибутива PVS-Studio. Этот проект можно открыть в Visual Studio 2005/2008 и полюбоваться на разнообразные 64-битные ошибки, поиграть с ними. Многие из них приводят к падению. Заодно этот проект полноценно проверяется демонстрационной версией PVS-Studio. Скачать демонстрационную версию можно здесь. Подробнее познакомиться с PVS-Studio и PortSample можно здесь.
А причем здесь компилятор? Если код некорректен, то не спасет, если он собирается GCC или является скажем OpenSource. Все это никак не связано. Вот пример.
Я не хочу здесь сильно надоедать статьями по разработке 64-битных приложений, но обязан сделать эту запись. Без нее знакомство с потенциальными 64-битными проблемами неполно. Ошибки, возникающие при переносе программы на 64-битные системы коварны тем, что могут проявлять себя только при попытке обработать объем данных большего размера, чем было доступно для 32-битной систем. Или при незначительной модернизации кода.
Часто после перекомпиляции для 64-битной системы складывается впечатление, что программа успешно работает. Но при этом она может содержать массу временно скрытых дефектов. Чтобы предупредить разработчиков об этом, и сделана эта статья. В дополнении также хочу предложить еще одну запись в блоге на нашем сайте: "Оптимизация в мире 64-битных ошибок".
Не могу удержаться. Вы, например, сайт Abraxas Software и Gimpel Software не видели. Они тоже инструменты статического анализа делают. И живут успешнее многих. :) Так что дизайн конечно важен, но часто далеко не основное. :)
100% гарантию может дать только методология математического доказательства корректности программного кода. Этот вариант естественно не рассматриваем. Любой инструмент позволяет обнаружить часть программных ошибок.
Можно привести аналогию. Задача — выловить всю рыбу в пруду. Ручной метод — удочка. Инструментальный метод — большая сеть. Естественно сеть не даст гарантии, что выловлена вся рыба, но это явно лучше, чем удочка.
Инструменты статического анализа выявляют в программах те места, которые с большой вероятностью содержат ошибку. Пример кода, на который выдаст предупреждение статический анализатор, встроенный в Visual Studio Team System (ключ /analyze): «if (m_x < 10 && m_x < 10)» — слева и справа от && одинаковые выражения. Это пример диагностики, которая практически всегда выявляет ошибку. В другом случае, диагностика гораздо менее точна. Пример:
void Foo(char *array, size_t size) {
for (int i = 0; i < size; ++i)
array[i] = 'A';
}
Имеется здесь 64-битная ошибка или нет, сказать сложно даже человеку. Ему необходимо знать с какими (маленькими или большими) массивами работает функция. Однако анализатор Viva64 предупредит об потенциальный ошибке и человек решит, стоит исправить int на size_t, отключить это диагностическое сообщение или указать в настройках, что программа не обрабатывает массивы более 2 гигабайт (Viva64 не будет считать этот код опасным).
И еще инструменты статического анализа часто выдают ложные срабатывания, предлагая обратить внимание на совершенно безобидный код.
Статический анализ не исключает просмотр человеком исходного кода. Но позволяет существенно сократить объем, необходимый для просмотра. Если прочитать 200 МБайт исходного кода почти нереальная задача (внимание быстро рассеивается и эффективность чтения очень низкая), то используя статический анализ можно просмотреть почти все места, потенциально содержащие 64-битные ошибки за несколько дней. Да, не все места, но почти все.
Программа ВСЕГДА содержит ошибки. Задача свести какой-то класс ошибок к разумному минимуму. Будь то ошибки GUI, ошибки, приводящие к замедлению или не освобождения памяти. Нет смысла стараться вывести все 64-битные ошибки, оставив тысячи обыкновенных. :) Звучит несколько необычно, но думаю Вы согласитесь. :)
Вывод – 100% уверенности от использования Viva64 нет. Но очень близко. И еще мы плотно работаем с клиентами и быстро реализуем их пожелания по диагностике и интерфейсу инструмента.
Мы готовы и делаем совместные статьи! Если у Вас есть интересные идеи или просто интересные наблюдения, связанные с параллельностью или 64-битностью — пишите нам. Мы обсудим создание совместную статью. Мы живые, мы общаемся, мы прислушиваемся к людям! Мы не абстрактные разработчики и авторы, с которыми нет связи. Мы готовы к общению! Поддержите нас и не ругайте. :) Так сложно быть самостоятельным разработчиком программного обеспечения.
1) sizeof(int) + sizeof(void *) не равно sizeof(MyStruct). Это связано с выравниванием полей в структурах. В результате на 64-битной системе выделяется меньше памяти, чем необходимо. Обратите внимание, что если исправить все остальные ошибки, программа успешно работает. То есть перетирается некоторая пока неиспользуемая память. Это может быть весьма неприятно, так как усложняет диагностику подобных ошибок.
2) Условие result != unsigned(-1) на 64-битной системе всегда истинно. Схожие ошибки можно нередко встретить в программах активно работающих со строками. Образуются они так. В начале пишется код:
size_t result = str.find(«5»);
if (result != -1)
Он успешно работает на 32-битной системе. И хотя это плохой стиль, он будет успешно работать и на 64-битной системе. Иногда компиляторы/анализаторы предупреждают пользователя о сравнении знаковых и беззнаковых значений, что чревато определенным видом ошибок. И помятуя об этом программист приписывает некорректное приведение типов к unsigned. Оно подавляет предупреждения, но вносит ошибку, которая потом проявится на 64-битной системе.
3) Конструкция throw p — A; генерирует исключение, используя тип ptrdiff_t. Это приводит к тому, что на 64-битной системе данное исключение не будет перехвачено с помощью catch (int position) {}. Причина в том, что ptrdiff_t и int совпадают в 32-битной системе и не совпадают в 64-битной.
А теперь та долгожданная реклама, о которой так много говорят. Если запустить анализатор Viva64 входящий в состав PVS-Studio то он выдаст на рассматриваемый код следующие предупреждения:
error V119: More than one sizeof() operators are used in one expression. r:\...\example_x64_bug_01.cpp 22
error V112: Dangerous magic number 4 used: p = A; p <= &A[4]; p++). r:\...\example_x64_bug_01.cpp 31
error V115: Memsize type used for throw. r:\...\example_x64_bug_01.cpp 34
error V104: Implicit type conversion to memsize type in an arithmetic expression. r:\...\example_x64_bug_01.cpp 40
Анализатор обнаружил все ошибки, а также предупредил о наличии опасной константы «4», что в прочем является для данного кода ложным срабатыванием.
Продолжим или хватит спама? :)
Правда не очень понятна актуальность адресации массивов больше INT_MAX.
Я работал с подобными задачами, где это удобно. Хотя конечно не скажу, что это повседневные задачи. Еще раз замечу, что 64-битные ошибки проявляются не обязательно на больших массивах. Разные ссылки я уже приводил выше.
Кстати, если есть желание, можно поиграть здесь в игру. Я буду приводить различные примеры кода, а участники будут пытаться найти в них ошибки силой мысли. Ошибки будут на тему 64-битности, параллельности и просто на внимательность.
Дополнительно предлагаю вниманию вот эти записи:
Почему A + B != A-(-B)
Красивая 64-битная ошибка на языке Си
Магические константы и функция malloc()
Проблемы 64-битного кода в реальных программах: магические константы
Изменения выравнивания типов и последствия
Поиск ошибок явного приведения типа в 64-битных программах
Это примеры, которые работоспособны в 32-битном режиме, но приводят к ошибкам при компиляции для 64-битной системы.
Часто после перекомпиляции для 64-битной системы складывается впечатление, что программа успешно работает. Но при этом она может содержать массу временно скрытых дефектов. Чтобы предупредить разработчиков об этом, и сделана эта статья. В дополнении также хочу предложить еще одну запись в блоге на нашем сайте: "Оптимизация в мире 64-битных ошибок".
7 шагов по переносу программы на 64-битную систему
А затем приглашаю в раздел ресурсов на нашем сайте где имеются наши статьи по разработке 64-битных приложений, обзоры сторонних статей, блог по тематике 64-битного программирования и так далее. Примеры статей:
20 ловушек переноса Си++ — кода на 64-битную платформу
Как оценить процесс 64-битной миграции Си/Си++ приложений?
Архитектура AMD64 (EM64T)
64-битный конь, который умеет считать
Что такое size_t и ptrdiff_t
Оптимизация 64-битных программ
Можно привести аналогию. Задача — выловить всю рыбу в пруду. Ручной метод — удочка. Инструментальный метод — большая сеть. Естественно сеть не даст гарантии, что выловлена вся рыба, но это явно лучше, чем удочка.
Инструменты статического анализа выявляют в программах те места, которые с большой вероятностью содержат ошибку. Пример кода, на который выдаст предупреждение статический анализатор, встроенный в Visual Studio Team System (ключ /analyze): «if (m_x < 10 && m_x < 10)» — слева и справа от && одинаковые выражения. Это пример диагностики, которая практически всегда выявляет ошибку. В другом случае, диагностика гораздо менее точна. Пример:
Имеется здесь 64-битная ошибка или нет, сказать сложно даже человеку. Ему необходимо знать с какими (маленькими или большими) массивами работает функция. Однако анализатор Viva64 предупредит об потенциальный ошибке и человек решит, стоит исправить int на size_t, отключить это диагностическое сообщение или указать в настройках, что программа не обрабатывает массивы более 2 гигабайт (Viva64 не будет считать этот код опасным).
И еще инструменты статического анализа часто выдают ложные срабатывания, предлагая обратить внимание на совершенно безобидный код.
Статический анализ не исключает просмотр человеком исходного кода. Но позволяет существенно сократить объем, необходимый для просмотра. Если прочитать 200 МБайт исходного кода почти нереальная задача (внимание быстро рассеивается и эффективность чтения очень низкая), то используя статический анализ можно просмотреть почти все места, потенциально содержащие 64-битные ошибки за несколько дней. Да, не все места, но почти все.
Программа ВСЕГДА содержит ошибки. Задача свести какой-то класс ошибок к разумному минимуму. Будь то ошибки GUI, ошибки, приводящие к замедлению или не освобождения памяти. Нет смысла стараться вывести все 64-битные ошибки, оставив тысячи обыкновенных. :) Звучит несколько необычно, но думаю Вы согласитесь. :)
Вывод – 100% уверенности от использования Viva64 нет. Но очень близко. И еще мы плотно работаем с клиентами и быстро реализуем их пожелания по диагностике и интерфейсу инструмента.