Перенос приложений на 64-битные платформы, или: "Не говори гоп, пока не перепрыгнешь... ."




Аннотация

64-битные системы появились больше десяти лет назад, однако близко знакомиться с ними мы начали относительно недавно, когда они вышли на рынок массовых компьютеров. Все больше производителей программного обеспечения говорят о необходимости поддержки таких систем. Если раньше 64-битные процессоры были широко распространены, в основном, в сфере сложных и длительных вычислений - численного моделирования процессов гидро- и газодинамики, механики деформируемого твердого тела, экологии и молекулярной химии и т.д., а также обслуживания сверхбольших баз данных, то сегодня системы на их основе уже можно видеть в качестве типовых рабочих станций. Насколько же велика необходимость перевода своих приложений на 64-битную платформу? И если решение о переходе принято, то каким образом это можно сделать с наименьшими временными и материальными затратами? Давайте посмотрим.

Почему это нужно?

Перед определением потребности в поддержке 64-битных систем, очевидно, следует определить преимущества, которые таковая поддержка дает.

1. Значительно больший объем памяти для приложений

Краткая информация по объемам адресного пространства для 64- и 32-битной ОС Windows приведена в таблице:

Адресное пространство

64-bit Windows

32-bit Windows

Виртуальная память

16 Тб

4 Гб

Файл подкачки

512 Тб

16 Тб

Системный кэш

1 Тб

1 Гб

Некоторые операционные системы резервируют часть адресного пространства под свои нужды, существенно снижая общий его размер, доступный для пользовательских приложений. Например, динамические библиотеки Windows XP и пользовательские компоненты ОС оставляют доступными только от 2 до 3 Гб адресного пространства (в зависимости от установок), даже если компьютер имеет 4 Гб оперативной памяти, что еще больше сужает доступный для приложений объем памяти.

В 32-битных системах файл размером более 4 Гб не мог быть полностью отображен в адресное пространство, в результате чего в них было необходимо отображать только часть файла, что приводило к снижению эффективности работы с данными больших объемов. В то же время, наличие даже на рабочей станции файлов размером более 4 Гб стало скорее правилом, чем исключением (прежде всего, это относится к DVD-видео). Использование 64-битных систем позволяет более эффективно оперировать файлами такого размера, поскольку существенное увеличение объема памяти, доступного приложениям, позволяет отображать такие файлы в адресное пространство целиком, а, как известно, время доступа к данным в оперативной памяти во много раз меньше, чем к данным на жестком диске.

2. Повышение быстродействия

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

Конечно, это не означает, что Ваш текстовый редактор будет работать заметно быстрее. Но 64-битные системы имеют возможность существенно повысить производительность при работе с более требовательными приложениями, такими, как CAD-системы, комплексы численного моделирования, аудио- и видеокодирования, криптографические системы и, конечно, игры.

3. Большее число регистров. Вычисления с высокой точностью

В 64-битных системах имеется вдвое большее число целочисленных регистров общего назначения, в том числе и SIMD-регистров (поддерживающих концепцию "один поток команд - множество потоков данных"). Использование компилятором этих регистров позволяет заметно улучшить эффективность реализации многих алгоритмов. Для операций с плавающей точкой используется не стек, а регистры, что также очень заметно отражается на производительности приложений, в которых производятся сложные математические вычисления. И, наконец, использование 64 бит увеличивает точность производимых вычислений, снижает ошибки округления, что особенно важно для пакетов численного моделирования процессов и ряда других приложений.

4. Улучшенный параллелизм

Усовершенствования в процессах распараллеливания и архитектуре шин дают возможность 64-битным платформам поддерживать большее число процессоров (до 64) с обеспечением линейной масштабируемости на каждый дополнительный процессор.

Кому это нужно?

Для определенного числа пользовательских повседневных программ перенос их в данный момент на 64-битную платформу не даёт какого-либо качественного скачка в производительности. Однако существует ряд областей, где такой скачок будет весьма сильным: программы для работы с базами данных (причём, чем больше объём используемых данных, тем более заметен выигрыш), программы для CAD/CAE (автоматизированное проектирование, моделирование), а также программы для создания цифрового контента (обработка изображений, звука, видео), 3D моделирования (рендеринг, анимация), в том числе высокотехнологичные игры, пакеты научных и высокопроизводительных вычислений (газовая и гидродинамика, сейсмология, геологоразведка, молекулярная химия и биология, генетика, исследования в области нанотехнологий), криптографические программы и экспертные системы и т.д.

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

Как это можно сделать?

Ряд существующих средств разработки существенно снижает затраты на переход с 32-битной платформы на 64-битную путем простой перекомпиляции уже существующего кода. Полученные приложения, по утверждениям создателей средств разработки, практически готовы к адекватной работе в новой системе. Необходимо только внести ряд изменений (в дальнейшем, мы будем говорить только о языках C и С++, поскольку они являются одними из самых распространенных языков программирования, и, в то же время, в достаточной степени иллюстрируют возникающие при переходе проблемы).

Эти изменения призваны исправить определенное число неправильно работающих блоков кода. Причем неправильно работающих именно под 64-битной системой, в то время как под 32-битной их функционирование абсолютно адекватно.

Прежде всего, такие блоки могут появиться из-за использования новой модели данных (в 64-битных ОС компании Microsoft - LLP64). В ней типы int и long остаются 32-битными целыми, а тип size_t становится 64-битным целым. Отсюда вытекает ряд возможных ошибок. Вот несколько примеров. Для упрощения изложения будем использовать понятие "memsize"-типа, то есть типа, способного хранить в себе указатель. К "memsize"-типам относятся непосредственно указатели и целые типы, размер которых соответствует размеру указателя.

1) Ошибка при неявном приведении аргумента функции, имеющего тип "memsize" к 32-битному типу:

float Foo(float *array, int arraySize) {...}
...
float *beginArray;
float *endArray;
...
float Value = Foo(beginArray, endArray - beginArray);

При выполнении арифметической операции вычитания над двумя указателями, согласно правилам языка С++, результат будет иметь тип ptrdiff_t. При вызове функции Foo этот результат будет приведен к типу int, что означает потерю старших битов и некорректное поведение функции, если приложение работает под 64-битной платформой, так как в этом случае ptrdiff_t является 64-битным целым, в отличие от 32-битного int.

2) Похожая ошибка возникает и при неявном приведении 32-битного аргумента функции к "memsize"-типу. На 64-битных платформах это может привести, например, к невозможности полного использования возможностей системы. Пример:

unsigned size = Size(); 
void *p = malloc(size);

Согласно определению в функции malloc() аргумент, определяющий размер выделяемой памяти имеет тип size_t. Приведенный блок кода не дает возможности выделить объем памяти более 4 Гб, так как этот объем ограничен максимальным размером переменной size, имеющей тип unsigned (32 бита).

3) Ошибка внутри арифметического выражения, связанная с неявным приведением к типу "memsize" и изменением допустимого интервала значений переменных, входящих в выражение. Одним из типичных примеров является возникновение бесконечного цикла в следующем блоке кода:

size_t n;
unsigned i;
...
for (i = 0; i != n; ++i) { ... }

При его переносе на 64-битную платформу значение n в соответствии с моделью данных LLP64 может превышать максимально возможное значение для типа unsigned, а, значит, в таком случае условие i != n никогда не выполнится!

Следует отметить, что ошибки, аналогичные примерам 1, 2 и 3, могут возникать и при явном приведении типов, например, путем использования static_cast.

4) Ошибка в адресной арифметике с указателями с переполнением при вычислении выражения:

short ind1, ind2, ind3;
char *pointerValue;
...
pointerValue += ind1* ind2* ind3;

В случае если значения переменных ind1, ind2, ind3 таковы, что их произведение превышает максимально допустимую для типа int величину (а именно к типу int в языке C++ будут приведены переменные ind1, ind2, ind3, и, следовательно, результат их умножения), то произойдет переполнение и переменная pointerValue получит неправильное значение. Указанный случай может произойти, когда программист, решивший воспользоваться возможностями 64-битной системы в работе с большими числами, разрешит переменным ind1, ind2, ind3 принимать большие, чем в 32-битной версии приложения, значения (хотя и в пределах, отмеряемых типом short). Например: 3000, 2000 и 1000 соответственно.

Аналогичная ошибка, связанная с неявным приведением типа и приводящая к переполнению, может встречаться и при выполнении оператора присваивания при некорректном вычислении значения выражения, стоящего справа от него.

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

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

Что для этого необходимо иметь?

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

В принципе, помочь должны существующие универсальные синтаксические верификаторы программного кода, однако они также обладают определенными недостатками. Так, один из лидеров в этой области - PC Lint - при всех его многочисленных достоинствах также не определяет значительную часть ошибок, возникающих при переносе приложения на 64-битную платформу, а, кроме того, чрезмерно сложен в использовании ввиду, зачастую, избыточной функциональности и большого количества ненужных (в данном случае) настроек.

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



Найдите ошибки в своем C, C++, C# и Java коде

Предлагаем попробовать проверить код вашего проекта с помощью анализатора кода PVS-Studio. Одна найденная в нём ошибка скажет вам о пользе методологии статического анализа кода больше, чем десяток статей.

goto PVS-Studio;


Найденные ошибки

Проверено проектов
364
Собрано ошибок
13 504

А ты совершаешь ошибки в коде?

Проверь с помощью
PVS-Studio

Статический анализ
кода для C, C++, C#
и Java

goto PVS-Studio;