Actualmente estoy usando GCC, pero descubrí Clang recientemente y estoy pensando en cambiar. Sin embargo, hay un factor decisivo: la calidad (velocidad, huella de memoria, confiabilidad) de los binarios que produce; si gcc -O3
puede producir un binario que se ejecuta un 1% más rápido o requiere un 1% menos de memoria, es un factor decisivo .
Clang cuenta con mejores velocidades de compilación y menor huella de memoria en tiempo de compilación que GCC, pero estoy realmente interesado en los puntos de referencia / comparaciones del software compilado resultante, ¿podría señalarme algunos o describir sus experiencias?
Respuestas:
Aquí hay algunos hallazgos míos actualizados aunque estrechos con GCC 4.7.2 y Clang 3.2 para C ++.
ACTUALIZACIÓN: GCC 4.8.1 v clang 3.3 comparación adjunta a continuación.
ACTUALIZACIÓN: GCC 4.8.2 v clang 3.4 comparación se agrega a eso.
Mantengo una herramienta OSS creada para Linux con GCC y Clang, y con el compilador de Microsoft para Windows. La herramienta, coan, es un preprocesador y analizador de archivos fuente C / C ++ y líneas de código de los mismos: su perfil computacional se especializa en el análisis y manejo de archivos de descenso recursivo. La rama de desarrollo (a la que pertenecen estos resultados) comprende actualmente alrededor de 11K LOC en aproximadamente 90 archivos. Está codificado, ahora, en C ++ que es rico en polimorfismo y plantillas, pero aún está empantanado en muchos parches por su pasado no muy lejano en C. hackeado. La semántica de movimiento no se explota expresamente. Es de un solo hilo. No he dedicado ningún esfuerzo serio a optimizarlo, mientras que la "arquitectura" sigue siendo en gran medida ToDo.
Empleé Clang antes de 3.2 solo como compilador experimental porque, a pesar de su velocidad de compilación y diagnóstico superiores, su soporte estándar C ++ 11 se quedó atrás de la versión contemporánea de GCC en los aspectos ejercidos por coan. Con 3.2, esta brecha se ha cerrado.
Mi arnés de prueba de Linux para procesos de desarrollo actual de aproximadamente 70,000 archivos fuente en una mezcla de casos de prueba de analizador de un archivo, pruebas de estrés que consumen miles de archivos y pruebas de escenarios que consumen <1K archivos. Además de informar los resultados de la prueba, el arnés se acumula y muestra los totales de archivos consumidos y el tiempo de ejecución consumido en coan (simplemente pasa cada línea de comando coan al comando Linux
time
y captura y suma los números informados). Los tiempos son halagados por el hecho de que cualquier número de pruebas que toman 0 tiempo medible sumarán 0, pero la contribución de tales pruebas es insignificante. Las estadísticas de tiempo se muestran al final demake check
esta manera:Comparé el rendimiento del arnés de prueba entre GCC 4.7.2 y Clang 3.2, siendo todo igual excepto los compiladores. A partir de Clang 3.2, ya no necesito ninguna diferenciación de preprocesador entre las secciones de código que GCC compilará y las alternativas de Clang. Construí la misma biblioteca C ++ (GCC) en cada caso y ejecuté todas las comparaciones consecutivamente en la misma sesión de terminal.
El nivel de optimización predeterminado para mi versión de lanzamiento es -O2. También probé con éxito las compilaciones en -O3. Probé cada configuración 3 veces consecutivas y promedié los 3 resultados, con los siguientes resultados. El número en una celda de datos es el número promedio de microsegundos consumidos por el ejecutable coan para procesar cada uno de los ~ 70K archivos de entrada (lectura, análisis y escritura de salida y diagnóstico).
Es muy probable que cualquier aplicación en particular tenga rasgos que jueguen injustamente a las fortalezas o debilidades de un compilador. La evaluación comparativa rigurosa emplea diversas aplicaciones. Con eso en mente, las características notables de estos datos son:
Otra comparación interesante de los dos compiladores surgió por accidente poco después de esos hallazgos. Coan emplea generosamente punteros inteligentes y uno de ellos es muy ejercido en el manejo de archivos. Este tipo de puntero inteligente en particular se había definido en versiones anteriores en aras de la diferenciación del compilador, para ser un
std::unique_ptr<X>
si el compilador configurado tenía un soporte suficientemente maduro para su uso como ese, y de lo contrario unstd::shared_ptr<X>
. El sesgo haciastd::unique_ptr
era tonto, ya que estos punteros de hecho se transfirieron, perostd::unique_ptr
parecía la opción más adecuada para reemplazarstd::auto_ptr
en un momento en que las variantes de C ++ 11 eran nuevas para mí.En el curso de las compilaciones experimentales para medir la necesidad continua de Clang 3.2 de esta diferenciación similar, construí inadvertidamente
std::shared_ptr<X>
cuando tenía la intención de compilarstd::unique_ptr<X>
, y me sorprendió observar que el ejecutable resultante, con la optimización predeterminada de O2, fue el más rápido. había visto, a veces alcanzando 184 ms. por archivo de entrada. Con este cambio en el código fuente, los resultados correspondientes fueron estos;Los puntos de nota aquí son:
Antes y después del cambio de tipo de puntero inteligente, Clang puede construir un ejecutable coan sustancialmente más rápido en la optimización -O3, y puede construir un ejecutable igualmente más rápido en -O2 y -O3 cuando ese tipo de puntero es el mejor
std::shared_ptr<X>
- para el trabajo.Una pregunta obvia de la que no soy competente para comentar es por qué Clang debería poder encontrar un 25% de aceleración de O2 en mi aplicación cuando un tipo de puntero inteligente muy usado cambia de único a compartido, mientras que GCC es indiferente al mismo cambio Tampoco sé si debería animar o abuchear el descubrimiento de que la optimización de -O2 de Clang alberga una sensibilidad tan grande a la sabiduría de mis elecciones de puntero inteligente.
ACTUALIZACIÓN: GCC 4.8.1 v clang 3.3
Los resultados correspondientes ahora son:
El hecho de que los cuatro ejecutables ahora tomen un tiempo promedio mucho mayor que antes para procesar 1 archivo no se refleja en el rendimiento de los últimos compiladores. Es debido al hecho de que la rama de desarrollo posterior de la aplicación de prueba ha adquirido mucha sofisticación de análisis mientras tanto y paga por la velocidad. Solo las proporciones son significativas.
Los puntos de nota ahora no son sorprendentemente novedosos:
Al comparar estos resultados con los de GCC 4.7.2 y clang 3.2, se destaca que GCC ha recuperado aproximadamente una cuarta parte de la ventaja de clang en cada nivel de optimización. Pero dado que la aplicación de prueba se ha desarrollado mucho mientras tanto, no se puede atribuir con confianza esto a una actualización en la generación de código de GCC. (Esta vez, noté la instantánea de la aplicación de la que se obtuvieron los tiempos y puedo usarla nuevamente).
ACTUALIZACIÓN: GCC 4.8.2 v clang 3.4
Terminé la actualización para GCC 4.8.1 v Clang 3.3 diciendo que me apegaría a la misma instantánea coan para obtener más actualizaciones. Pero decidí probar esa instantánea (rev. 301) y la última instantánea de desarrollo que tengo que pasa su conjunto de pruebas (rev. 619). Esto le da a los resultados un poco de longitud, y tuve otro motivo:
Mi publicación original señaló que no había dedicado ningún esfuerzo a optimizar Coan para la velocidad. Este seguía siendo el caso a partir de la rev. 301. Sin embargo, después de haber incorporado el aparato de sincronización en el arnés de prueba Coan, cada vez que ejecutaba el conjunto de pruebas, el impacto en el rendimiento de los últimos cambios me miraba a la cara. Vi que a menudo era sorprendentemente grande y que la tendencia era más abruptamente negativa de lo que creía que merecen las ganancias en la funcionalidad.
Por rev. 308 el tiempo de procesamiento promedio por archivo de entrada en el conjunto de pruebas se había más que duplicado desde la primera publicación aquí. En ese momento hice un cambio de sentido en mi política de 10 años de no preocuparme por el rendimiento. En la serie intensiva de revisiones, hasta 619 el rendimiento siempre fue una consideración y un gran número de ellos se limitó a reescribir portadores de carga clave en líneas fundamentalmente más rápidas (aunque sin usar ninguna función de compilador no estándar para hacerlo). Sería interesante ver la reacción de cada compilador a este cambio de sentido,
Aquí está la matriz de tiempos ahora familiar para las últimas compilaciones de dos compiladores de rev.301:
coan - rev.301 resultados
La historia aquí solo cambia marginalmente de GCC-4.8.1 y Clang-3.3. La presentación de GCC es un poco mejor. Clang's es un poco peor. El ruido bien podría explicar esto. Clang todavía sale adelante
-O2
y-O3
márgenes que no importarían en la mayoría de las aplicaciones, pero que importarían en algunas.Y aquí está la matriz para rev. 619.
coan - rev.619 resultados
Tomando las figuras 301 y 619 una al lado de la otra, se expresan varios puntos.
Tenía el objetivo de escribir código más rápido, y ambos compiladores reivindican enfáticamente mis esfuerzos. Pero:
GCC paga esos esfuerzos mucho más generosamente que Clang. En la
-O2
optimización, la construcción 619 de Clang es un 46% más rápida que su construcción 301:-O3
la mejora de Clang es del 31%. Bien, pero en cada nivel de optimización, la construcción 619 de GCC es más del doble de rápida que su 301.GCC más que revierte la antigua superioridad de Clang. Y en cada nivel de optimización, GCC ahora supera a Clang en un 17%.
La capacidad de Clang en la compilación 301 para obtener más influencia que GCC a partir de la
-O3
optimización desapareció en la compilación 619. Ninguno de los compiladores gana significativamente de-O3
.Estaba tan sorprendido por esta inversión de fortunas que sospeché que podría haber hecho accidentalmente una construcción lenta de clang 3.4 (desde que lo construí desde la fuente). Así que volví a ejecutar la prueba 619 con el stock Clang 3.3 de mi distribución. Los resultados fueron prácticamente los mismos que para 3.4.
Entonces, en lo que respecta a la reacción al cambio de sentido: en los números aquí, Clang ha hecho mucho mejor que GCC a la velocidad de extracción de mi código C ++ cuando no lo estaba ayudando. Cuando me propuse ayudar, GCC hizo un trabajo mucho mejor que Clang.
No elevo esa observación a un principio, pero tomo la lección que dice "¿Qué compilador produce los mejores binarios?" es una pregunta que, incluso si especifica el conjunto de pruebas con el que la respuesta será relativa, todavía no es una cuestión clara de simplemente cronometrar los binarios.
¿Es su mejor binario el binario más rápido, o es el que mejor compensa el código de bajo costo? O mejores compensa costosamente código diseñado que da prioridad a la mantenibilidad y la reutilización sobre la velocidad? Depende de la naturaleza y los pesos relativos de sus motivos para producir el binario, y de las restricciones bajo las cuales lo hace.
Y, en cualquier caso, si le importa mucho crear "los mejores" archivos binarios, será mejor que siga comprobando cómo las sucesivas iteraciones de compiladores cumplen su idea de "lo mejor" sobre las sucesivas iteraciones de su código.
fuente
kcachegrind
para identificar las funciones donde los ejecutables generados difieren en rendimiento.Phoronix hizo algunos puntos de referencia sobre esto, pero se trata de una versión instantánea de Clang / LLVM de hace unos meses. Los resultados son que las cosas fueron más o menos un empujón; ni GCC ni Clang son definitivamente mejores en todos los casos.
Como usarías el último Clang, quizás sea un poco menos relevante. Por otra parte, GCC 4.6 está programado para tener algunas optimizaciones importantes para Core 2 e i7, aparentemente.
Me imagino que la velocidad de compilación más rápida de Clang será mejor para los desarrolladores originales, y luego, cuando empuje el código al mundo, la distribución de Linux / BSD / etc. los usuarios finales usarán GCC para los binarios más rápidos.
fuente
El hecho de que Clang compila código más rápido puede no ser tan importante como la velocidad del binario resultante. Sin embargo, aquí hay una serie de puntos de referencia .
fuente
Hay muy poca diferencia general entre GCC 4.8 y clang 3.3 en términos de velocidad del binario resultante. En la mayoría de los casos, el código generado por ambos compiladores funciona de manera similar. Ninguno de estos dos compiladores domina al otro.
Los puntos de referencia que indican que hay una brecha de rendimiento significativa entre GCC y clang son coincidencias.
El rendimiento del programa se ve afectado por la elección del compilador. Si un desarrollador o un grupo de desarrolladores usa exclusivamente GCC, entonces se puede esperar que el programa se ejecute un poco más rápido con GCC que con clang, y viceversa.
Desde el punto de vista del desarrollador, una diferencia notable entre GCC 4.8+ y clang 3.3 es que GCC tiene la
-Og
opción de línea de comando. Esta opción permite optimizaciones que no interfieren con la depuración, por lo que, por ejemplo, siempre es posible obtener trazas de pila precisas. La ausencia de esta opción en clang hace que clang sea más difícil de usar como compilador de optimización para algunos desarrolladores.fuente
La única forma de determinar esto es probarlo. FWIW He visto algunas mejoras realmente buenas usando LLVM gcc 4.2 de Apple en comparación con el gcc 4.2 normal (para código x86-64 con bastante SSE), pero YMMV para diferentes bases de código. Suponiendo que está trabajando con x86 / x86-64 y que realmente le importa el último porcentaje, también debería probar el ICC de Intel, ya que esto a menudo puede vencer a gcc: puede obtener una licencia de evaluación de 30 días de intel.com e inténtalo
fuente
Una diferencia peculiar que he notado en gcc 5.2.1 y clang 3.6.2 es que si tienes un ciclo crítico como:
Entonces gcc, al compilar con
-O3
o-O2
, desenrolla especulativamente el ciclo ocho veces. Clang no lo desenrollará en absoluto. A través de prueba y error, descubrí que en mi caso específico con los datos de mi programa, la cantidad correcta de desenrollado es cinco, por lo tanto, gcc overhot y clang underhot. Sin embargo, el sobrepasar fue más perjudicial para el rendimiento, por lo que gcc tuvo un rendimiento mucho peor aquí.No tengo idea de si la diferencia de desenvolvimiento es una tendencia general o simplemente algo específico de mi escenario.
Hace un tiempo escribí algunos recolectores de basura para que me enseñen más sobre la optimización del rendimiento en C. Y los resultados que obtuve están en mi mente lo suficiente como para favorecer ligeramente el sonido metálico. Especialmente porque la recolección de basura se trata principalmente de perseguir punteros y copiar memoria.
Los resultados son (números en segundos):
Todo esto es código C puro y no afirmo sobre el rendimiento de ninguno de los compiladores al compilar código C ++.
En Ubuntu 15.10, x86.64 y un procesador AMD Phenom (tm) II X6 1090T.
fuente
Básicamente hablando, la respuesta es: depende. Hay muchos puntos de referencia que se centran en diferentes tipos de aplicaciones.
Mi punto de referencia en mi aplicación es: gcc> icc> clang.
Hay E / S raras, pero muchas operaciones de estructura de datos y flotante de CPU.
compilar banderas es -Wall -g -DNDEBUG -O3.
https://github.com/zhangyafeikimi/ml-pack/blob/master/gbdt/profile/benchmark
fuente