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

17

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

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

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

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

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

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

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

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

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

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

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

    
задан Peter Smit 18.08.2009 в 22:06
источник
  • Разве это не то, что Apple делает со своими «универсальными» двоичными файлами (PPC - x86)? –  Edmundo 26.08.2009 в 00:02
  • Я убедился, что у меня есть все ответы, которые, как я думал, были хорошими, все они получают от меня немного :). Приветствия для принятия. –  ShuggyCoUk 27.08.2009 в 16:45
  • О, и если вы узнаете больше информации, как вы идете, не стесняйтесь редактировать мой ответ и сделать его CW ... –  ShuggyCoUk 27.08.2009 в 16:46

8 ответов

5

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

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

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

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

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

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

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

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

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

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

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

  1. Будьте осторожны с этим - трудно побить достойных оптимизирующих компиляторов на этом
ответ дан ShuggyCoUk 24.08.2009 в 20:44
источник
16

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

    
ответ дан anon 18.08.2009 в 22:31
источник
  • Спасибо! Может быть, у вас есть несколько конкретных указателей, как их компилировать? И как должен выглядеть заглушка? –  Peter Smit 19.08.2009 в 07:48
  • В Windows вы можете запустить 64-битную DLL из 32-битного процесса? Я не думал, что вы могли бы .. но хотелось бы посмотреть, как вы могли это сделать :) –  Goz 26.08.2009 в 19:17
  • Затем можно было бы предоставить еще один уровень: 32-разрядный загрузчик, который обнаружил, что он работает на 64-битной архитектуре, 64-разрядный бегун exec'ed, который, в свою очередь, загружает 64-битную библиотеку. –  Pavel Shved 26.08.2009 в 22:28
  • Ну, по сути, это то, о чем я думал. Запустите 32-битный процесс, который обнаруживает все, что ему нужно сделать, а затем вместо запуска новой DLL запускает новый процесс, который будет обрабатывать 32-разрядные или 64-разрядные. –  Goz 27.08.2009 в 08:01
6

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

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

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

cat /proc/cpuinfo

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

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

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

    
ответ дан Byron Whitlock 18.08.2009 в 22:11
источник
  • Вам действительно не нужен скрипт для обнаружения процессора - вы можете сделать это с помощью собственных системных вызовов, зависящих от ОС. –  Adam Rosenfield 21.08.2009 в 06:52
  • Но если вы используете скрипт, он становится переносимым по архитектуре ОС и 64/32 бит. –  Byron Whitlock 21.08.2009 в 20:50
  • Учитывая, что он уже пишет (совершенно сознательно) OS-зависимый код, я не думаю, что необходимо обеспечить, чтобы обнаружение ОС было портативным. Хотя перенос этой части приложения был бы переносимым, возможно, упростилось бы. –  Brian 24.08.2009 в 20:58
5

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

    
ответ дан camh 19.08.2009 в 11:12
источник
3

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

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

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

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

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

    
ответ дан jhoule 18.08.2009 в 22:52
источник
  • Это ужасное предложение, и вам нужно будет использовать его. Я не часто делаю такие заявления, но в этом случае я чувствую, что должен. Не делай это. –  Aug 18 '09 at 22:54 19.08.2009 в 00:54
  • Я абсолютно не хочу иметь разные файлы .cpp. Это кошмар для поддержания! Если у меня есть оптимизация для определенных платформ в моем коде, я думаю, что ifdefs будет служить мне. –  Peter Smit 19.08.2009 в 07:50
  • ОК, мне нужно, чтобы я чувствовал себя немного защищенным здесь, учитывая силу этих комментариев. Во-первых, я понимаю, что вы хотите скомпилировать различные версии математической интенсивной процедуры для одной и той же архитектуры (например, x86), но с различными реализациями / оптимизациями (SSE, -O1 / O2 / O3 и т. Д.). Я считаю, что «-mtune» и «-mfpmath» GCC не могут контролироваться препроцессором, поэтому вам придется перекомпилировать тот же .cpp для генерации разных файлов .o. Предложение Нейла состоит в том, чтобы они оказались в разных динамических библиотеках. Мой должен был иметь их всех в одном двоичном (продолжение). –  jhoule 19.08.2009 в 19:33
  • Я предлагаю избегать внедрения межплатформенной системы плагинов. Вы в основном можете перекомпилировать один и тот же фрагмент кода с различными параметрами, но компоновщик будет жаловаться на обманы. Дайте им разные имена (сгенерированные тем же источником с макросами, если хотите), и у вас есть несколько подпрограмм, выполняющих ту же работу несколько иначе. Отдельный .cpp может быть излишним: я просто предположил, что для инструмента построения было проще. Мое основное соображение состояло в том, что вы могли бы одновременно выбирать несколько подпрограмм C как вызов метода C ++ с помощью функции ptr-to-func. Это то, что точки входа в DLL тоже! –  jhoule 19.08.2009 в 19:40
  • Моя точка зрения была в основном о разных файлах .cpp, которые я нашел бы ужасным решением. Также имея макросы, которые переименовывают мои библиотеки, я нахожу загромождение своего кода, но это было бы действительно решением. Однако я продолжаю искать более общее решение. –  Peter Smit 20.08.2009 в 08:45
3

Поскольку вы не указали, имеете ли вы ограничения на количество файлов, я предлагаю другое решение: скомпилируйте 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 вы должны создать новый процесс, а не повторять текущий.

    
ответ дан Martin v. Löwis 21.08.2009 в 14:33
источник
1

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

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

    
ответ дан MSalters 21.08.2009 в 15:55
источник
  • Как называется эта функциональность Intel? Или у вас есть ссылки на документацию и упомянутые хаки? –  Peter Smit 26.08.2009 в 11:23
1

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

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

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

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

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

ответ дан Steve K 26.08.2009 в 00:45
источник