Подтвердить что ты не робот

Компилировать и оптимизировать для разных целевых архитектур

Резюме: Я хочу воспользоваться преимуществами оптимизаторов компилятора и наборов инструкций процессора, но все еще имею портативное приложение (работающее на разных процессорах). Обычно я мог бы скомпилировать 5 раз и позволить пользователю выбрать правильный для запуска.

Мой вопрос: как я могу автоматизировать это, чтобы процессор обнаруживался во время выполнения, а правый исполняемый файл выполнялся без необходимости его выбора?


У меня есть приложение с множеством математических вычислений низкого уровня. Эти вычисления обычно выполняются в течение длительного времени.

Я хотел бы использовать максимально возможную оптимизацию, желательно, также и наборов инструкций (не всегда поддерживаемых). С другой стороны, я хотел бы, чтобы мое приложение было портативным и простым в использовании (поэтому я бы не хотел компилировать 5 разных версий и позволить пользователю выбирать).

Есть ли возможность скомпилировать 5 разных версий моего кода и динамически запускать наиболее оптимизированную версию, которая возможна во время выполнения? С 5 различными версиями я имею в виду разные наборы инструкций и различные оптимизации для процессоров.

Мне не важно размер приложения.

В данный момент я использую gcc в Linux (мой код находится на С++), но я также заинтересован в этом для компилятора Intel и для MinGW для компиляции в Windows.

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

Изменить: Пожалуйста, дайте четкие указания, как это сделать, желательно с небольшими примерами кода или ссылками на объяснения. С моей точки зрения мне нужно супер общее решение, которое применимо к любому случайному проекту С++, который у меня есть позже.

Изменить. Я назначил награду ShuggyCoUk, у него было большое количество указателей, которые нужно было искать. Мне хотелось бы разделить его между несколькими ответами, но это невозможно. Я еще не реализовал это, поэтому вопрос все еще "открыт"! Пожалуйста, по-прежнему добавляйте и/или улучшайте ответы, даже несмотря на то, что больше нет ни одной награды.

Спасибо всем!

4b9b3361

Ответ 1

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

Вы можете достичь этого, имея отдельный исполняемый файл с соответствующим заголовком PE64. Просто использование CreateProcess запустит это как соответствующую битту (если только исполняемый файл не запущен в каком-то перенаправленном местоположении, нет необходимости беспокоиться о перенаправление папок WoW64

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

Это также означает, что ваш "основной" исполняемый файл может быть полностью разделен в зависимости от целевой операционной системы (поскольку обнаружение возможностей процессора/ОС по своей природе относится к конкретной ОС), а затем большую часть остальной части вашего код как общие объекты /dll. Кроме того, вы можете "делиться" одними и теми же файлами для двух разных архитектур, если вы в настоящее время не чувствуете, что есть какая-либо точка, использующая разные возможности.

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

Другие возможности данной модели:

  • Статическая привязка к различным версиям стандартных сред выполнения (для тех, у кого есть/без безопасности потоков) и их использование соответственно, если вы работаете без каких-либо возможностей SMP/SMT.
  • Обнаружение присутствия нескольких ядер и их реальной или гиперпоточности (также зависит от того, знает ли ОС, как эффективно расписание в этих случаях)
  • проверка производительности таких вещей, как таймер системы/высокопроизводительных таймеров, и использование кода, оптимизированного для этого поведения, скажем, если вы делаете что-либо, где вы ищете определенное количество времени, чтобы истечь, и, следовательно, можете знать, насколько это возможно. /li >
  • Если вы хотите оптимизировать выбор кода на основе размера кеша/другой загрузки на коробке. Если вы используете развернутые циклы, тогда более агрессивные варианты разворачивания могут зависеть от наличия кеша определенного уровня 1/2.
  • Компиляция условно для использования double/floats в зависимости от архитектуры. Менее важно для аппаратного обеспечения Intel, но если вы нацеливаете определенный процессор ARM, у некоторых есть реальная поддержка аппаратного обеспечения с плавающей запятой, а другие требуют эмуляции. Оптимальный код будет сильно изменяться, даже если вы используете условную компиляцию вместо использования оптимизирующего компилятора (1).
  • Использование аппаратного обеспечения сопроцессора, такого как CUDA-совместимые графические карты.
  • обнаружение виртуализации и изменение поведения (возможно, попытка избежать записи в файловой системе)

Что касается выполнения этой проверки, у вас есть несколько вариантов, наиболее полезным для Intel является cpuid.

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

Довольно много отдельных документов, чтобы выяснить, как обнаружить вещи:

Большая часть того, что вы платите в библиотеке CPU-Z, - это кто-то, кто делает все это (и неприятные небольшие проблемы) для вас.


  • Будьте осторожны с этим - трудно побить достойных оптимизирующих компиляторов на этом

Ответ 2

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

Ответ 3

Можете ли вы использовать script?

Вы можете обнаружить CPU с помощью script и динамически загружать исполняемый файл, который наиболее оптимизирован для архитектуры. Он также может выбирать версии с 32/64 бит.

Если вы используете Linux, вы можете запросить процессор с помощью

cat /proc/cpuinfo

Возможно, вы могли бы сделать это с помощью bash/perl/python script или Windows scripting host в окнах. Вероятно, вы не хотите принуждать пользователя устанавливать движок script. Лучше всего будет работать тот, который работает на ОС из коробки IMHO.

На самом деле, в Windows вы, вероятно, захотите написать небольшое приложение С#, чтобы вы могли более легко запросить архитектуру. Приложение С# может просто порождать все исполняемые файлы быстрее.

В качестве альтернативы вы можете поместить свои разные версии кода в dll или shared object, а затем динамически загружать их на основе обнаруженной архитектуры. Пока у них есть одна и та же сигнатура вызова, она должна работать.

Ответ 4

Посмотрите на liboil: http://liboil.freedesktop.org/wiki/. Он может динамически выбирать реализации вычислений, связанных с мультимедиа, во время выполнения. Вы можете обнаружить, что вы можете ликовать себя, а не только его методы.

Ответ 5

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

Нейл Баттерворт уже предлагал создавать отдельные динамические библиотеки, но для этого требуются некоторые нетривиальные кросс-платформенные соображения (загрузка вручную динамических библиотек различна в Linux, Windows, OSX и т.д., и, чтобы получить право, это займет некоторое время).

Дешевое решение состоит в том, чтобы просто написать все ваши варианты с использованием уникальных имен и использовать указатель функции для выбора правильного во время выполнения.

Я подозреваю, что дополнительная развязка, вызванная указателем функции, будет амортизирована фактической работой, которую вы выполняете (но вы хотите подтвердить это).

Кроме того, для получения различных оптимизаций компилятора, скорее всего, потребуются разные файлы .c/.cpp, а также некоторые элементы вашего инструмента построения. Но это, вероятно, менее общая работа, чем отдельные библиотеки (которые нуждались в этом уже в той или иной форме).

Ответ 6

Поскольку вы не указали, имеете ли вы ограничения на количество файлов, я предлагаю другое решение: скомпилируйте 5 исполняемых файлов, а затем создайте шестой исполняемый файл, который запускает соответствующий двоичный файл. Вот несколько псевдокодов, для Linux

int main(int argc, char* argv[])
{
    char* target_path[MAXPATH];
    char* new_argv[];
    char* specific_version = determine_name_of_specific_version();
    strcpy(target_path, "/usr/lib/myapp/versions");
    strcat(target_path, specific_version);

    /* append NULL to argv */
    new_argv = malloc(sizeof(char*)*(argc+1));
    memcpy(new_argv, argv, argc*sizeof(char*));
    new_argv[argc] = 0;
    /* optionally set new_argv[0] to target_path */

    execv(target_path, new_argv);
}

С положительной стороны, этот подход позволяет обеспечить прозрачность пользователя как с 32-битными, так и с 64-битными двоичными файлами, в отличие от любых предложенных библиотечных методов. На минусовой стороне в Win32 нет execv (но хорошая эмуляция в cygwin); в Windows вы должны создать новый процесс, а не повторять текущий.

Ответ 7

Вы упомянули компилятор Intel. Это смешно, потому что он может сделать что-то подобное по умолчанию. Однако есть улов. Компилятор Intel не вставлял проверки подходящей функциональности SSE. Вместо этого они проверили, есть ли у вас определенный чип Intel. Все равно будет медленный случай по умолчанию. В результате процессоры AMD не будут получать подходящие версии с поддержкой SSE. Есть хаки, плавающие вокруг, что заменит проверку Intel правильной проверкой SSE.

Для разницы в 32/64 бит потребуется два исполняемых файла. Формат ELF и PE сохраняет эту информацию в заголовке exectuables. Не слишком сложно запустить 32-битную версию по умолчанию, проверьте, находитесь ли вы в 64-разрядной системе, а затем перезапустите 64-битную версию. Но может быть проще создать соответствующую символическую ссылку во время установки.

Ответ 8

Позволяет разбить проблему до двух ее составных частей. 1) Создание оптимизированного кода, зависящего от платформы, и 2) создание на нескольких платформах.

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

В первой части я предлагаю вам взглянуть на Gnu Atuotools (Automake, AutoConf и Libtool). Если вы когда-либо загружали и строили программу GNU из исходного кода, вы знаете, что перед запуском make вам нужно запустить. /configure. Цель configure script состоит в том, чтобы 1) убедиться, что ваша система имеет все необходимые библиотеки и утилиты, необходимые для сборки и запуска программы, и 2) настроить Make файлы для целевой платформы. Autotools - это набор утилит для создания configure script.

Используя autoconf, вы можете создавать небольшие макросы, чтобы проверить, поддерживает ли машина все инструкции CPU, необходимые для вашего кодового кода. В большинстве случаев макросы уже существуют, вам просто нужно скопировать их в ваш autoconf script. Затем automake и autoconf могут настроить Make файлы, чтобы выполнить соответствующую реализацию.

Все это немного для создания примера здесь. Для изучения требуется немного времени. Но документация все там. Существует даже свободная книга, доступная в Интернете. И этот процесс применим к вашим будущим проектам. Думаю, для многоплатформенной поддержки это самый надежный и простой способ. Множество предложений, размещенных в других ответах, - это то, что касается Autotools (обнаружение CPU, статическая и общая библиотека), и вам не нужно слишком много думать об этом. Единственная морщина, с которой вам придется иметь дело, - выяснить, доступны ли Autotools для MinGW. Я знаю, что они являются частью Cygwin, если вы можете пойти по этому пути.