|
|
|||||||||||||||||||||||||||||
|
Об одной ошибке оптимизации времени выполненияИсточник: habrahabrru
Изначально пост планировалось посвятить ошибке 64х-битового компилятора xlc которую я безуспешно отлавливал многие часы и которая имеет место быть на серверах фирмы IBM архитектуры AIX. Но так уж получилось, что подобная ошибка затрагивает многие компиляторы, не стал исключением и Visual Studio 2010 с установленным пакетом обновления SP1. Что в итоге кажется забавным, так как наводит на мысли, что специалисты Microsoft сотрудничают с разработчиками из IBM в деле создания оптимизирующих компиляторов. Немного предыстории. Есть один научный проект, который был написан на С++ достаточно давно и сейчас успешно переносится на многие платформы, среди которых можно отметить мейнфреймы HP-UX, IBM AIX, Oracle Solaris. Перенос по большому счету состоит в том, что исправляются ошибки времени компиляции, запускается группа тестов и если все тесты проходят, то делается вывод о работоспособности кода. Так как скорость выполнения математических процедур очень даже важна, компиляция проходит с включенным ключом оптимизации по скорости -O2. Но на архитектуре IBM AIX компилятор xlc почему-то не может создать работоспособный код, удовлетворяющий набору тестов. В то же время без ключа -O2 все работает нормально. Я бы, конечно, мог попробовать отловить эту ошибку непосредственно на мейнфрейме IBM AIX, будь у меня в запасе достаточно времени, но за отсутствием отладчика (в debug mode ошибка не проявлялась) ловить приходилось по-старинке, методом вставки printf в участки кода. Удаленный доступ к IBM AIX мне так и не дали, приходилось работать непосредственно в дата-центре и за те несколько часов, проведенных за терминалом, ничего внятного понять не удалось, кроме того, что ошибка имеет место быть и достаточно устойчивая. В итоге, ошибка так и сидела в коде на протяжении долгого времени. Так продолжалось до тех пор, пока я не попробовал перенести код на Visual Studio 2010 SP1. И о чудо! Ошибка проявила себя в том же первозданном виде, а именно в 32х-битовом режиме все работает нормально и при включении флага -O2 и без этого, а в x64 при включенном -O2 один из тестов "ругается" в точности так же, как это было на IBM AIX! Это победа, потому что теперь я мог, не ограничивая себя временными рамками, вдумчиво копать непаханное поле кода, экспериментируя и последовательно сравнивая результаты printf при правильном и неправильном прохождении тестов. Результат не заставил себя долго ждать. Ниже будет приведена выжимка из полного кода, это наиболее сокращенный в размерах код. Данный код не работает и в 32х-битовом режиме тоже, так как параметр N равен 4. Если же установить #define N 8, то мы получим изначальный код, работающий на 32х битах, но неработающий на x64. Для простоты (не у всех есть x64, а многие, наверное, захотят попробовать) привожу исходный код, неработающий на любой архитектуре. Итак, попробуем откомпилировать вот этот код с ключом -O2 и без него: #include <stdio.h>
Код программы запишем в файл test32.c Для компиляции воспользуемся Visual Studio 2010 SP1 и будем делать код для 32х разрядной операционной системы. Сборку и запуск проведем при помощи такого командного файла: call "C:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" После запуска получим результаты: Setting environment fоr using Microsoft Visual Studio 2010 x86 tools. Видно, что после оптимизации получается 00 01 02 02 вместо 00 01 02 03. Почему так происходит? Рассмотрим ассемблерный файл with_opt.asm полученный при включенной оптимизации. Ассемблерный файл no_opt.asm полученный при выключенной оптимизации нам не очень интересен, так как там все работает нормально. Желающие могут найти его у себя в рабочей директории. Оптимизация включена: _TEXT SEGMENT
Легко заметить, что вызов функции f() реально не происходит, компилятор сразу же рассчитывает значения переменной x и заполняет массив а. Причем при оптимизации заполнение происходит неправильно, элементы массива _a+2 и _a+3 заполняются одними и теми же значениями из регистра al. Это же верно при компиляции 64х-разрядного исполняемого файла. Для работы с 64х-битным кодом заменим первую строку в командном файле: call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" amd64
Получим такой же неправильный результат, но только при sizeof(void*)=8, что подтверждает 64х-битность полученного кода: Setting environment fоr using Microsoft Visual Studio 2010 x64 tools. Ассемблерный x64 код выглядит так: main PROC ; COMDAT
Легко увидеть, что здесь также не происходит вызов функции f(), а компилятор сразу рассчитывает значения переменной x и заполняет массив а. При этом элементы массива _a+2 и _a+3 заполняются одними и теми же значениями из регистра cl, что неправильно. В итоге исходный код функции f() был исправлен таким образом: void f(int k)
И тут же все прекрасно заработало как на Visual Studio x86/x64 так и на xlc для IBM AIX. Скорость выполнения тестов с ключом -O2 в итоге увеличилась примерно в 2,5 - 3 раза. Ссылки по теме
|
|