В настоящее время вы можете использовать библиотеку: https://github.com/deano2390/FlowTextView . Например:
Некоторые правки включают очень важные предложения в комментарии и другие ответы. Я хотел бы отметить, что способ решения этой проблемы во многом зависит от среды, в которой вы планируете работать. Минимальные встроенные системы могут не иметь достаточно ресурсов для поддержки динамического связывания. Небольшие по размеру небольшие системы вполне могут поддерживать динамическое связывание, поскольку их память достаточно мала, чтобы сделать экономию ОЗУ от динамического связывания очень привлекательной. Полноценные потребительские ПК имеют, как отмечает Марк, огромные ресурсы, и вы, вероятно, можете позволить проблемам удобства побудить вас задуматься над этим вопросом.
Для решения проблем производительности и эффективности: это зависит от .
Как правило, динамические библиотеки требуют некоторого рода клеевого слоя, который часто означает двойную диспетчеризацию или дополнительный уровень косвенности при адресации функций и может стоить небольшой скорости (но действительно ли время вызова функции является большой частью вашего времени работы? ?).
Однако, если вы запускаете несколько процессов, которые все вызывают одну и ту же библиотеку, вы можете в конечном итоге сохранить строки кэша (и, таким образом, выиграть в производительности) при использовании динамического связывания относительно статического связывания. (Если современные ОС не достаточно умны, чтобы замечать идентичные сегменты в статически связанных двоичных файлах. Кажется, это сложно, кто-нибудь знает?)
Другая проблема: время загрузки. Вы оплачиваете расходы на погрузку в какой-то момент. Когда вы платите, эта стоимость зависит от того, как работает ОС, а также от того, какие ссылки вы используете. Возможно, вы бы предпочли отложить оплату, пока не узнаете, что вам это нужно.
Обратите внимание, что статическое-динамическое связывание традиционно не является проблемой оптимизации, поскольку обе они включают отдельную компиляцию вплоть до объектных файлов. Однако это не требуется: компилятор может в принципе «скомпилировать» «статические библиотеки» вначале в переваренную форму AST и «связать» их, добавив эти AST к тем, которые сгенерированы для основного кода, что расширяет возможности глобальной оптимизации. Ни одна из систем, которые я использую, не делает этого, поэтому я не могу комментировать, насколько хорошо это работает.
Способ ответить на вопросы о производительности всегда всегда путем тестирования (и использовать среду тестирования настолько, насколько это возможно, среду развертывания).
Статическое связывание включает файлы, которые нужны программе, в одном исполняемом файле.
Динамическое связывание - это то, что вы считаете обычным: оно создает исполняемый файл, для которого все еще требуются библиотеки DLL и т. Д., Которые находятся в одном каталоге (или библиотеки DLL могут находиться в системной папке).
(DLL = динамическая ссылка библиотека)
Динамически связанные исполняемые файлы компилируются быстрее и не так ресурсоемки.
Статическое связывание дает вам только один исполняемый файл, чтобы внести изменения, необходимые для перекомпиляции всей вашей программы. Принимая во внимание, что при динамическом связывании вам нужно вносить изменения только в dll, и когда вы запускаете свой exe-файл, изменения будут восприниматься во время выполнения. Проще предоставлять обновления и исправления ошибок с помощью динамического связывания (например, windows).
В Unix-подобных системах динамическое связывание может усложнить жизнь «root» для использования приложения с общими библиотеками, установленными в труднодоступных местах. Это связано с тем, что динамический компоновщик обычно не обращает внимания на LD_LIBRARY_PATH или его эквивалент для процессов с привилегиями root. Иногда статическое связывание спасает день.
В качестве альтернативы, процесс установки должен найти библиотеки, но это может затруднить сосуществование нескольких версий программного обеспечения на компьютере.
Динамическое связывание требует дополнительного времени для ОС, чтобы найти динамическую библиотеку и загрузить ее. Со статической связью все вместе, и это однократная загрузка в память.
Также см. DLL Hell . Это сценарий, в котором загружаемая ОС не является той DLL, которая поставляется с вашим приложением, или той версией, которую ожидает ваше приложение.
Еще одна проблема, которая еще не обсуждалась, - это исправление ошибок в библиотеке.
При статическом связывании вам нужно не только перестроить библиотеку, но и заново связать и перераспределить исполняемый файл. Если библиотека только используется в одном исполняемом файле, это может не быть проблемой. Но чем больше исполняемых файлов, которые необходимо повторно связать и перераспределить, тем больше будет боль.
Используя динамическое связывание, вы просто перестраиваете и перераспределяете динамическую библиотеку, и все готово.
Лучший пример динамического связывания - когда библиотека зависит от используемого оборудования. В древние времена математическая библиотека C считалась динамичной, поэтому каждая платформа могла использовать все возможности процессора для ее оптимизации.
Еще лучшим примером может быть OpenGL. OpenGl - это API, который по-разному реализован AMD и NVidia. И вы не можете использовать реализацию NVidia на карте AMD, потому что аппаратное обеспечение отличается. Из-за этого вы не можете статически связать OpenGL с вашей программой. Динамическое связывание используется здесь, чтобы позволить API быть оптимизированным для всех платформ.
В этом подробно обсуждаются общие библиотеки на linux и влияние на производительность.
Одной из причин создания статически связанной сборки является проверка того, что у вас есть полное закрытие для исполняемого файла, то есть все ссылки на символы разрешены правильно.
Как часть большой системы, которая создавалась и тестировалась с использованием непрерывной интеграции, ночные регрессионные тесты выполнялись с использованием статически связанной версии исполняемых файлов. Иногда мы видим, что символ не разрешается, и статическая ссылка не работает, даже если динамически связанная исполняемая программа успешно соединяется.
Это обычно происходило, когда символы, которые были глубоко укоренены в общих библиотеках, имели название ошибки и поэтому не имели статической связи. Динамический компоновщик не полностью разрешает все символы, независимо от использования оценки по глубине или ширине, поэтому вы можете получить динамически связанный исполняемый файл, который не имеет полного закрытия.
Это довольно просто, правда. Когда вы вносите изменения в свой исходный код, вы хотите подождать 10 минут для его сборки или 20 секунд? Двадцать секунд - это все, что я могу вынести. Кроме того, я либо достаю меч, либо начинаю думать о том, как я могу использовать отдельную компиляцию и связывание, чтобы вернуть его в зону комфорта.
Динамическое связывание является единственным практическим способом удовлетворения некоторых лицензионных требований, таких как LGPL .
1 / Я принимал участие в проектах, в которых динамическое связывание и статическое связывание были сравнены, и разница не была определена достаточно маленькой, чтобы перейти на динамическое связывание (я не был частью теста, я просто знаю вывод)
2 / Динамическое связывание часто ассоциируется с PIC (позиционно-независимый код, код, который не нужно изменять в зависимости от адреса, по которому он загружен). В зависимости от архитектуры PIC может привести к другому замедлению, но это необходимо для того, чтобы получить выгоду от совместного использования динамически связанной библиотеки между двумя исполняемыми файлами (и даже двумя процессами одного исполняемого файла, если ОС использует рандомизацию адреса загрузки в качестве меры безопасности). Я не уверен, что все ОС позволяют разделить две концепции, но Solaris и Linux делают и ISTR, что HP-UX делает также.
3 / Я был в других проектах, которые использовали динамическое связывание для функции «простого патча». Но этот «легкий патч» делает распространение небольших исправлений немного проще и сложнее, чем кошмар версий. Нам часто приходилось настаивать на всем, а также отслеживать проблемы на сайте клиента, потому что была указана неправильная версия.
Я пришел к выводу, что я использовал исключение статического связывания:
для таких вещей, как плагины, которые зависят от динамического связывания
при совместном использовании важно (большие библиотеки, используемые несколькими процессами одновременно, такие как среда выполнения C / C ++, библиотеки GUI, ... которые часто управляются независимо и для которых строго определен ABI)
Если кто-то хочет использовать «легкий патч», я бы сказал, что библиотеки должны управляться так же, как большие библиотеки, описанные выше: они должны быть почти независимыми с определенным ABI, который не должен быть изменен исправлениями.
Я согласен с замечаниями dnmckee, а также:
1) основано на том факте, что для вызова функции DLL всегда используется дополнительный косвенный переход. Сегодня это обычно незначительно. Внутри DLL есть некоторые дополнительные затраты на процессоры i386, потому что они не могут генерировать независимый от позиции код. На amd64 переходы могут быть относительно счетчика программы, так что это огромное улучшение.
2) Это правильно. Оптимизация, основанная на профилировании, обычно дает 10-15% производительности. Теперь, когда скорость процессора достигла своего предела, возможно, стоит сделать это.
Я бы добавил: (3) компоновщик может упорядочить функции в более эффективную кэш-группировку, чтобы минимизировать дорогостоящие пропуски уровня кеша. Это также может особенно повлиять на время запуска приложений (основываясь на результатах, которые я видел с компилятором Sun C ++)
И не забывайте, что с DLL не может быть выполнено удаление мертвого кода. В зависимости от языка код DLL может быть неоптимальным. Виртуальные функции всегда виртуальны, потому что компилятор не знает, перезаписывает ли его клиент.
По этим причинам, если нет реальной необходимости в DLL, просто используйте статическую компиляцию.
РЕДАКТИРОВАТЬ (чтобы ответить на комментарий, подчеркивание пользователя)
Вот хороший ресурс о проблеме кода, независимого от позиции http://eli.thegreenplace.net/2011/11/03/ position-independent-code-pic-in-shared-library /
Как объяснено, x86 не имеет их AFAIK ни для чего другого, кроме 15-битных диапазонов перехода, а не для безусловных переходов и вызовов. Вот почему функции (от генераторов), имеющие более 32K, всегда были проблемой и нуждались во встроенных батутах.
Но в популярных x86 ОС, таких как Linux, вам не нужно заботиться о том, чтобы файл SO / DLL не генерировался с помощью переключателя gcc
-fpic
(который обеспечивает использование таблиц косвенных переходов). Потому что, если вы этого не сделаете, код будет просто исправлен, как обычный компоновщик переместит его. Но при этом он делает сегмент кода недоступным для совместного использования, и ему потребуется полное отображение кода с диска в память и касание всего этого, прежде чем его можно будет использовать (очистка большей части кэшей, обращение к TLB) и т. Д. Было время когда это считалось медленным ... слишком медленным.
Так что вы больше не получите никакой пользы.
Я не помню, какие ОС (Solaris или FreeBSD) доставляли мне проблемы с моей системой сборки Unix, потому что я просто не делал этого и задавался вопросом, почему она вылетала, пока я не применил -fPIC
к gcc
.
Существует огромное и все большее число систем, в которых экстремальный уровень статического связывания может оказать огромное положительное влияние на приложения и производительность системы.
Я имею в виду то, что часто называют «встроенными системами», многие из которых в настоящее время все чаще используют операционные системы общего назначения, и эти системы используются для всего, что только можно себе представить.
Чрезвычайно распространенным примером являются устройства, использующие системы GNU / Linux, использующие Busybox . Я довел это до крайности с NetBSD , создав загрузочный образ системы i386 (32-разрядный), который включает в себя как ядро, так и его корневую файловую систему, причем последний содержит единственную статически-связанную (by crunchgen
) двоичный файл с жесткими ссылками на все программы, который сам содержит все (по крайней мере, последний счет 274) стандартных полнофункциональных системных программ (большинство, кроме набора инструментов), и он меньше 20 мегабайт байтов (и, вероятно, работает очень удобно в системе с 64 МБ памяти (даже с несжатой корневой файловой системой и полностью в ОЗУ), хотя я не смог найти такой маленький для тестирования далее).
В более ранних публикациях упоминалось, что время запуска двоичных файлов со статической связью быстрее (и это может быть много быстрее), но это только часть картины, особенно когда весь объектный код связан с одним и тем же файлом, и особенно, когда операционная система поддерживает подкачку кода по требованию непосредственно из исполняемого файла. В этом идеальном сценарии время запуска программ буквально пренебрежимо мало, поскольку почти все страницы кода уже будут в памяти и будут использоваться оболочкой (и и init
любыми другими фоновыми процессами, которые могут выполняться ), даже если запрошенная программа никогда не запускалась с момента загрузки, поскольку, возможно, для выполнения требований программы во время выполнения должна быть загружена только одна страница памяти.
ld.so
стоимость запуска каждого процесса, который запускается каждый раз. его запуск займет часы и часы циклов ЦП от задач, требующих запуска многих процессов, особенно когда одни и те же программы используются снова и снова, например, компиляторы в системе разработки. Связанные со статическими данными программы могут сократить время сборки нескольких архитектур для всей системы на моих системах на часов . Мне еще предстоит встроить инструментальную цепочку в мой отдельный crunchgen
двоичный файл, но я подозреваю, что когда я это сделаю, будет сэкономлено больше времени на сборку из-за выигрыша в кеше ЦП.