Использует -start-group и -end-группу при связывании быстрее, чем создание статической библиотеки?

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

g++ main.o hw.a gui.a -o executable

Если gui.a использует что-то определенное в hw.a, ссылка не будет выполнена, потому что в момент обработки hw.a компоновщик еще не знает, что определение необходимо позже, и не включает его в исполняемый исполняемый файл. Взаимодействие с линкером вручную нецелесообразно, поэтому решение должно использовать --start-group и --end-group, что заставляет компоновщик запускаться дважды через библиотеки, пока не будут найдены символы undefined.

g++ main.o -Wl,--start-group hw.a gui.a -Wl,--end-group -o executable

Однако в руководстве GNU ld говорится

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

Итак, я подумал, что лучше всего взять все файлы .a и объединить их в один файл .a с индексом (-s) GNU ar), который говорит, в каком порядке файлы должны быть связаны между собой. Затем вы указываете только один файл .a на g++.

Но мне интересно, быстрее или медленнее, чем с помощью групповых команд. И есть ли проблемы с этим подходом? Я также задаюсь вопросом, есть ли лучший способ решить эти проблемы взаимозависимости?


EDIT: я написал программу, которая принимает список файлов .a и создает объединенный файл .a. Работает с общим форматом ar GNU. Объединение всех статических библиотек LLVM работает следующим образом

$ ./arcat -o combined.a ~/usr/llvm/lib/libLLVM*.a

Я сравнивал скорость с распаковкой всех файлов .a вручную, а затем помещал их в новый .a файл, используя ar, перекомпонуя индекс. Используя мой инструмент arcat, я получаю согласованное время работы около 500 мс. Используя ручной способ, время сильно варьируется и занимает около 2 секунд. Поэтому я считаю, что это того стоит.

Код здесь. Я помещаю его в общественное достояние:)

Ответ 1

Вы можете определить порядок, используя утилиты lorder и tsort, например

libs='/usr/lib/libncurses.a /usr/lib/libedit.a'
libs_ordered=$(lorder $libs | tsort)

приводит к /usr/lib/libedit.a /usr/lib/libncurses.a, потому что libedit зависит от libncurses.

Это, вероятно, только преимущество выше --start-group, если вы не запускаете lorder и tsort снова для каждой команды связи. Кроме того, он не допускает взаимных/циклических зависимостей, таких как --start-group.

Ответ 2

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

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

Однако вы должны быть осторожны с тем обстоятельством, когда оба файла содержат одно и то же определение (вы можете явно проверить это, используя nm, чтобы узнать, какие определения содержатся в каждой библиотеке)