Почему приложение x64 в два раза медленнее x86?

Я новичок в С. Сделал свой первый проект в VisualStudio - собрал релизы x64 и x86.

Задача простейшая: грузим словарь на 8 млн. слов, строим префиксное дерево(реализация, вы не поверите, из коммента на stackoverflow - первое что завелось и заработало)

Поиск сотни слов:
x86: 2-3 сек.
x64: 6-7 сек.

Ровно в два раза падает производительность x64 vs x86, такая же картинка при поиске 1000 слов - ровно 2 раза.

Хотелось бы понять в общих чертах откуда такая разница. Скорее всего, я что-то не знаю и не понимаю про x64.

Win 8.1, VS 2013 Express для Windows Desktop, настройки сборки/компиляции не трогал
cl.exe версия продукта 11.00.50727.1

Попробовал собрать на gcc с ключами -m32 и -m64 - x64 чуть тормознее, около 5-10%
Версия gcc - Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
  • Вопрос задан
  • 2433 просмотра
Пригласить эксперта
Ответы на вопрос 5
Deerenaros
@Deerenaros
Программист, математик, задрот и даже чуть инженер
Отвечать на этот вопрос без какой-либо дополнительной информации - это как гадать на кофейной гуще. Какой CPU - если это древний Pentium D с допотопным конвейером и глупыми регистрами - одно дело, а если это новейший Core i7 на Haswell - другое. Что до настроек - вот честно, "стандартные" вообще ни о чём не говорит. Я уже не говорю, что было бы не плохо указать количество опытов с максимальным и минимальным - вполне возможно глупые ОС с планировщиком как-то неудачно распределяют время. Любой ответ, который можно тут указать может быть техническим грамотным, но совершенно не соответствующий истине.

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

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

Теперь смотрим на код. Что там? Куча адресной арифметики, немного функций, да и аргументов почти нет. 8 миллионов слов? Не думаю что рекурсия вынудит вылезти стеку за пределы кэша, так что есть подозрение о паритете архитектур в данном случае. Однако большое количество адресной арифметики и увеличенный размер адреса в битах... во сколько раз? В два раза?

Ну да ладно, ясное дело, сложение реализовано за 1 такт. Скорее всего. Конечно, здесь вопрос процессора, но даже узнав модель будет сложно узнать наверняка, разве только синтетическим тестом (много раз обращаться по адресу - сумме двух случайных чисел). Да и Windows 8.1 никогда не был стандартом производительности (скорее с точностью наоборот), и VC++ никогда не был лучшим компилятором.

Попробуйте gcc (меня разве только интересует откуда на Windows взялся gcc) с флагом -O3. И посмотрите машинный код для 64 бита и 32 бита (можно пользоваться objdump из binutils или посмотреть машинный код в IDE Visual Studio - точно расположение кнопки не помню, но можно поискать в менюшках). Скорее всего причина не одна, их множество. Так, вызов функции сопровождается сохранением контекста, тогда как в x64 регистров больше, больше и контекст. Собираем такие моменты по крупицам... Вот и получаем.

P.S. Давным давно, разговаривал с преподавателем. Простая перекомпиляция под 64 бита ускорила код на 30%. Это был колхозный кодек, немного похожий на libx264 (от туда была сдёрнута часть кода). Естественно, проект собирался со всеми оптимизациями, со всем расширениями инструкций - со всем, чем можно. И сборка под платформу x86-64 (с SSE, MMX, FMA и прочие). Жутко наукоёмкий разношёрстный код (писали все - от зелёных аспирантов, до ровесников Страуструпа и профессоров университета) - туева хуча функций, структур, объединений и очень, очень много параметров, многие из которых передают в аргументы функций. Ну и целевая платформа - жутко порезанный и переделанный Windows Embedded - там просто не чего было планировать.
Ответ написан
Комментировать
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
Хотелось бы понять в общих чертах откуда такая разница.

Обычно ответ на этот вопрос проще всего получить с помощью профилирования, которое укажет вам где программа проводит время. Вам останется лишь понять, почему именно там.
При использовании gcc это делается добавлением опции -pg к командам компиляции и линковки, запуску приложения и запуску gprof потом. Для VS наверняка тоже есть профайлер.
Ответ написан
Комментировать
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
Скорее всего в x64 билде происходит какая-то лишняя работа с памятью. В любом случае проблема кроется где-то в работе с указателями и оптимизациями компилятора.
Ответ написан
Комментировать
@ichernob
а не может быть это связано со страницами памяти и адресацией?
Ответ написан
Комментировать
iLeonidze
@iLeonidze
xbooster.ru
Вам к RAM
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы