c++ - para - seo imagenes wordpress
Enlace estático vs enlace dinámico (15)
¿Existen razones de rendimiento convincentes para elegir el enlace estático sobre el enlace dinámico o viceversa en ciertas situaciones? He escuchado o leído lo siguiente, pero no sé lo suficiente sobre el tema para dar fe de su veracidad.
1) La diferencia en el rendimiento en tiempo de ejecución entre el enlace estático y el enlace dinámico suele ser despreciable.
2) (1) no es cierto si utiliza un compilador de perfiles que usa datos de perfil para optimizar los hotpaths del programa porque con el enlace estático, el compilador puede optimizar su código y el código de la biblioteca. Con la vinculación dinámica solo se puede optimizar su código. Si la mayor parte del tiempo se pasa ejecutando el código de la biblioteca, esto puede hacer una gran diferencia. De lo contrario, (1) todavía se aplica.
1 / He estado en proyectos donde el enlace dinámico frente al enlace estático se comparó y la diferencia no se determinó lo suficientemente pequeña como para cambiar al enlace dinámico (no formé parte de la prueba, solo sé la conclusión)
2 / La vinculación dinámica a menudo se asocia con PIC (Código independiente de la posición, código que no necesita modificarse según la dirección en la que se carga). Dependiendo de la arquitectura, el PIC puede generar otra desaceleración, pero es necesario para poder beneficiarse de compartir una biblioteca enlazada dinámicamente entre dos ejecutables (e incluso dos procesos del mismo ejecutable si el sistema operativo utiliza la asignación aleatoria de direcciones de carga como medida de seguridad). No estoy seguro de que todos los sistemas operativos permitan separar los dos conceptos, pero Solaris y Linux lo hacen e ISTR que HP-UX también.
3 / He estado en otros proyectos que utilizaron enlaces dinámicos para la función "parche fácil". Pero este "parche fácil" hace que la distribución de pequeñas correcciones sea un poco más fácil y complicada una pesadilla de versiones. A menudo terminábamos teniendo que empujar todo y tener que rastrear los problemas en el sitio del cliente porque la versión incorrecta era token.
Mi conclusión es que he usado el enlace estático, excepto:
Para cosas como complementos que dependen de enlaces dinámicos.
cuando la compartición es importante (las bibliotecas grandes se usan por varios procesos al mismo tiempo, como el tiempo de ejecución C / C ++, las bibliotecas GUI, ... que a menudo se administran de forma independiente y para las cuales se define estrictamente la ABI)
Si uno quiere usar el "parche fácil", argumentaría que las bibliotecas deben administrarse como las bibliotecas grandes anteriores: deben ser casi independientes con un ABI definido que no debe cambiarse mediante arreglos.
1) se basa en el hecho de que llamar a una función DLL siempre está usando un salto indirecto adicional. Hoy en día, esto suele ser despreciable. Dentro de la DLL hay un poco más de sobrecarga en las CPU i386, porque no pueden generar código independiente de la posición. En amd64, los saltos pueden ser relativos al contador del programa, por lo que esta es una gran mejora.
2) Esto es correcto. Con optimizaciones guiadas por perfiles, generalmente puede ganar entre un 10 y un 15 por ciento de rendimiento. Ahora que la velocidad de la CPU ha alcanzado sus límites, podría valer la pena hacerlo.
Yo agregaría: (3) el enlazador puede organizar funciones en una agrupación más eficiente de caché, de modo que se minimicen los costosos niveles de caché. También podría afectar especialmente el tiempo de inicio de las aplicaciones (según los resultados que he visto con el compilador Sun C ++)
Y no olvide que con DLL no se puede realizar la eliminación del código muerto. Dependiendo del idioma, el código DLL puede que tampoco sea óptimo. Las funciones virtuales son siempre virtuales porque el compilador no sabe si un cliente lo está sobrescribiendo.
Por estas razones, en caso de que no haya una necesidad real de DLL, entonces solo use la compilación estática.
EDITAR (para responder al comentario, por el usuario).
Aquí hay un buen recurso sobre el problema del código independiente de la posición http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/
Como se explicó, x86 no tiene AFAIK para nada más que rangos de salto de 15 bits y no para saltos y llamadas incondicionales. Es por eso que las funciones (de los generadores) que tienen más de 32K siempre han sido un problema y han necesitado trampolines integrados.
Pero en el popular sistema operativo x86 como Linux, no tiene por qué preocuparse si el archivo SO / DLL no se genera con el conmutador gcc
-fpic
(que impone el uso de las tablas de salto indirecto). Porque si no lo hace, el código simplemente se arregla como un vinculador normal lo reubicaría. Pero al hacer esto, hace que el segmento de código no se pueda compartir y necesitaría un mapeo completo del código del disco a la memoria y tocarlo todo antes de poder usarlo (vaciar la mayoría de los cachés, golpear los TLB), etc. Hubo un tiempo. cuando esto fue considerado lento ... demasiado lento.
Entonces ya no tendrías ningún beneficio.
No recuerdo qué sistema operativo (Solaris o FreeBSD) me dio problemas con mi sistema de compilación Unix porque simplemente no estaba haciendo esto y me pregunté por qué se bloqueó hasta que apliqué -fPIC
a gcc
.
El mejor ejemplo para la vinculación dinámica es cuando la biblioteca depende del hardware utilizado. En la antigüedad, se decidió que la biblioteca matemática de C era dinámica, de modo que cada plataforma pueda usar todas las capacidades del procesador para optimizarla.
Un ejemplo aún mejor podría ser OpenGL. OpenGl es una API que se implementa de forma diferente por AMD y NVidia. Y no puede usar una implementación NVidia en una tarjeta AMD, porque el hardware es diferente. No puedes vincular OpenGL estáticamente en tu programa, debido a eso. La vinculación dinámica se utiliza aquí para permitir que la API se optimice para todas las plataformas.
En sistemas similares a Unix, la vinculación dinámica puede dificultar la vida de "raíz" para usar una aplicación con las bibliotecas compartidas instaladas en ubicaciones remotas. Esto se debe a que el enlazador dinámico generalmente no presta atención a LD_LIBRARY_PATH o su equivalente para procesos con privilegios de raíz. A veces, entonces, el enlace estático salva el día.
Alternativamente, el proceso de instalación tiene que ubicar las bibliotecas, pero eso puede dificultar la coexistencia de múltiples versiones del software en la máquina.
Es bastante simple, de verdad. Cuando realiza un cambio en su código fuente, ¿desea esperar 10 minutos para que se genere o 20 segundos? Veinte segundos es todo lo que puedo soportar. Más allá de eso, saco la espada o empiezo a pensar en cómo puedo usar la compilación y el enlace por separado para devolverla a la zona de confort.
Estoy de acuerdo con los puntos que menciona dnmckee, más:
- Las aplicaciones vinculadas estáticamente podrían ser más fáciles de implementar, ya que hay menos o ninguna dependencia de archivos adicionales (.dll / .so) que pueden causar problemas cuando faltan o se instalan en el lugar equivocado.
Existe un vasto y creciente número de sistemas en los que un nivel extremo de enlace estático puede tener un enorme impacto positivo en las aplicaciones y el rendimiento del sistema.
Me refiero a lo que a menudo se denomina "sistemas integrados", muchos de los cuales utilizan cada vez más sistemas operativos de uso general, y estos sistemas se utilizan para todo lo que se pueda imaginar.
Un ejemplo extremadamente común son los dispositivos que utilizan sistemas GNU / Linux utilizando Busybox . He llevado esto al extremo con NetBSD al crear una imagen de sistema de arranque i386 (32 bits) que incluye tanto un kernel como su sistema de archivos raíz, este último que contiene un único binario con enlace estático (por crunchgen
) con enlaces duros a todos los programas que a su vez contienen todos (bueno, en el último recuento de 274) de los programas estándar del sistema con todas las funciones (la mayoría excepto la cadena de herramientas), y tiene un tamaño inferior a 20 mega bytes (y probablemente se ejecuta muy cómodamente en un sistema con solo 64 MB de memoria (incluso con el sistema de archivos raíz sin comprimir y completamente en la memoria RAM), aunque no he podido encontrar uno tan pequeño para probarlo.
Se ha mencionado en publicaciones anteriores que el tiempo de inicio de los binarios con enlace estático es más rápido (y puede ser mucho más rápido), pero eso es solo una parte de la imagen, especialmente cuando todo el código objeto está vinculado al mismo archivo, e incluso más especialmente cuando el sistema operativo admite la paginación por demanda del código directamente desde el archivo ejecutable. En este escenario ideal, el tiempo de inicio de los programas es literalmente insignificante, ya que casi todas las páginas de código ya estarán en la memoria y estarán en uso por el shell (e init
cualquier otro proceso en segundo plano que pueda estar ejecutándose), incluso si el programa solicitado tiene nunca se ha ejecutado desde el inicio, ya que tal vez solo se necesita cargar una página de memoria para cumplir con los requisitos de tiempo de ejecución del programa.
Sin embargo, eso todavía no es toda la historia. También normalmente compilo y uso las instalaciones del sistema operativo NetBSD para mis sistemas de desarrollo completos mediante la vinculación estática de todos los binarios. Aunque esto requiere una tremenda cantidad de espacio en el disco (~ 6.6 GB en total para x86_64 con todo, incluidas las herramientas y el enlace estático X11) (especialmente si uno tiene tablas de símbolos de depuración completas disponibles para todos los programas, otro ~ 2.5GB), el resultado aún se ejecuta más rápido en general, y para algunas tareas incluso usa menos memoria que un sistema típico de enlace dinámico que pretende compartir páginas de códigos de bibliotecas. El disco es barato (incluso disco rápido), y la memoria para almacenar en caché los archivos de disco que se usan con más frecuencia también es relativamente barato, pero los ciclos de la CPU realmente no lo son, y pagar el ld.so
inicio de ld.so
para cada proceso que se inicia cada vez que comienza tomará horas y Las horas de CPU se alejan de las tareas que requieren iniciar muchos procesos, especialmente cuando los mismos programas se utilizan una y otra vez, como los compiladores en un sistema de desarrollo. Los programas de cadena de herramientas vinculados estáticamente pueden reducir los tiempos de construcción de múltiples arquitecturas del SO completo para mis sistemas por horas . Todavía tengo que construir la cadena de herramientas en mi único binario crunchgen
''ed, pero sospecho que cuando lo haga habrá más horas de compilación ahorradas debido a la ganancia para el caché de la CPU.
La vinculación dinámica es la única forma práctica de cumplir con algunos requisitos de licencia, como la LGPL .
La vinculación dinámica requiere tiempo adicional para que el SO encuentre la biblioteca dinámica y la cargue. Con la vinculación estática, todo está unido y es una carga de un disparo en la memoria.
También, vea DLL Hell . Este es el escenario en el que la DLL que carga el sistema operativo no es la que vino con su aplicación, o la versión que su aplicación espera.
La vinculación estática incluye los archivos que el programa necesita en un solo archivo ejecutable.
La vinculación dinámica es lo que consideraría lo habitual, hace un ejecutable que aún requiere DLL y está en el mismo directorio (o las DLL pueden estar en la carpeta del sistema).
(DLL = biblioteca de enlace dinámico )
Los ejecutables vinculados dinámicamente se compilan más rápido y no son tan pesados en recursos.
Otro problema aún no discutido es la solución de errores en la biblioteca.
Con la vinculación estática, no solo tiene que reconstruir la biblioteca, sino que también tendrá que volver a vincular y redestruir el archivo ejecutable. Si la biblioteca solo se usa en un ejecutable, esto puede no ser un problema. Pero mientras más ejecutables necesiten ser vinculados y redistribuidos, mayor será el dolor.
Con la vinculación dinámica, simplemente reconstruye y redistribuye la biblioteca dinámica y listo.
Una razón para hacer una construcción enlazada estáticamente es verificar que tiene un cierre completo para el ejecutable, es decir, que todas las referencias de símbolos se resuelvan correctamente.
Como parte de un gran sistema que se estaba construyendo y probando usando integración continua, las pruebas de regresión nocturnas se ejecutaron usando una versión estáticamente vinculada de los ejecutables. Ocasionalmente, veríamos que un símbolo no se resolvería y el enlace estático fallaría a pesar de que el ejecutable enlazado dinámicamente se enlazaría con éxito.
Esto solía ocurrir cuando los símbolos que estaban profundamente asentados dentro de las bibliotecas compartidas tenían un nombre mal escrito y, por lo tanto, no se vinculaban estáticamente. El enlazador dinámico no resuelve por completo todos los símbolos, independientemente de usar la evaluación de profundidad primero o de amplitud, por lo que puede terminar con un ejecutable enlazado dinámicamente que no tiene cierre completo.
la vinculación estática le brinda un solo archivo ejecutable, para hacer un cambio que necesita para volver a compilar todo el programa. Mientras que en la vinculación dinámica necesita hacer cambios solo en la dll y cuando ejecute su exe, los cambios se recogerán en tiempo de ejecución. Es más fácil proporcionar actualizaciones y correcciones de errores mediante la vinculación dinámica (por ejemplo, ventanas).
This discute en gran detalle acerca de las bibliotecas compartidas en Linux e impliaction de rendimiento.
- El enlace dinámico puede reducir el consumo total de recursos (si más de un proceso comparte la misma biblioteca (incluida la versión en "el mismo", por supuesto)). Creo que este es el argumento que impulsa su presencia en la mayoría de los entornos. Aquí los "recursos" incluyen espacio en disco, RAM y espacio en caché. Por supuesto, si su enlazador dinámico no es lo suficientemente flexible, existe un riesgo de DLL infernal .
- La vinculación dinámica significa que las correcciones de errores y las actualizaciones de las bibliotecas se propagan para mejorar su producto sin tener que enviar nada.
- Los complementos siempre requieren enlaces dinámicos .
- La vinculación estática significa que puede saber que el código se ejecutará en entornos muy limitados (al principio del proceso de arranque o en el modo de rescate).
- La vinculación estática puede hacer que los binarios sean más fáciles de distribuir a diversos entornos de usuario (al costo de enviar un programa grande y con más recursos).
- La vinculación estática puede permitir tiempos de inicio ligeramente más rápidos, pero esto depende en cierta medida tanto del tamaño y la complejidad de su programa como de los detalles de la estrategia de carga de los sistemas operativos.
Algunas ediciones para incluir las sugerencias muy relevantes en los comentarios y en otras respuestas. Me gustaría tener en cuenta que la forma en que lo explique depende mucho del entorno en el que planea ejecutarse. Es posible que los sistemas integrados mínimos no tengan los recursos suficientes para admitir la vinculación dinámica. Los sistemas pequeños ligeramente más grandes pueden admitir la vinculación, ya que su memoria es lo suficientemente pequeña como para hacer que los ahorros de RAM de la vinculación dinámica sean muy atractivos. Como lo señala Mark, las PC de consumo tienen enormes recursos, y probablemente pueda dejar que los problemas de conveniencia lo lleven a pensar en este asunto.
Para abordar los problemas de rendimiento y eficiencia: depende .
Clásicamente, las bibliotecas dinámicas requieren algún tipo de capa de pegamento que a menudo significa doble despacho o una capa adicional de direccionamiento indirecto en el direccionamiento de funciones y puede costar un poco de velocidad (pero, ¿el tiempo de llamada de funciones es en realidad una parte importante del tiempo de ejecución?).
Sin embargo, si está ejecutando varios procesos, todos los cuales llaman mucho a la misma biblioteca, puede terminar guardando líneas de caché (y, por lo tanto, ganando en rendimiento de ejecución) al usar el enlace dinámico relativo al uso del enlace estático. (A menos que los sistemas operativos modernos sean lo suficientemente inteligentes como para notar segmentos idénticos en binarios vinculados estáticamente. Parece difícil, ¿alguien sabe?)
Otro tema: el tiempo de carga. Usted paga los costos de carga en algún momento. Cuando pagas, este costo depende de cómo funciona el sistema operativo y de los enlaces que utilizas. Tal vez prefiera posponer el pago hasta que sepa que lo necesita.
Tenga en cuenta que el enlace estático vs dinámico no es tradicionalmente un problema de optimización, ya que ambos implican una compilación separada en archivos de objetos. Sin embargo, esto no es necesario: un compilador puede en principio "compilar" "bibliotecas estáticas" a un formulario AST digerido inicialmente, y "vincularlas" agregando esos AST a los generados para el código principal, lo que permite una optimización global. Ninguno de los sistemas que utilizo hace esto, así que no puedo comentar qué tan bien funciona.
La forma de responder a las preguntas de rendimiento es siempre probando (y use un entorno de prueba lo más parecido posible al entorno de implementación).