Clang vs GCC: ¿qué produce mejores binarios? [cerrado]

238

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 -O3puede 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?

SF.
fuente
55
Todavía parece una pregunta y respuestas valiosas, y muchas están interesadas.
YasserAsmi
99
@YasserAsmi: Y las dos métricas (huella de memoria y velocidad de ejecución) están lejos de ser arbitrarias o están sujetas a "opinión". Pero parece que la enfermedad de la Física se propaga aquí y la gente comenzó a votar para cerrar sin leer los detalles del texto de la pregunta aquí también.
SF.
12
la pregunta pide puntos de referencia y comparaciones, la respuesta da ambas ... ¿por qué es esta opinión en lugar de una comparación objetiva?
oemb1905
2
No veo por qué se cerró esta pregunta. Ya sea que se base en opiniones o hechos, queremos saber la respuesta, y marcarla como cerrada le da un tinte negativo, donde no debería haber una.
Timothy Makobu
2
@TomZych: Si quieres conocer estos factores, haz diferentes preguntas. Este es muy específico e inequívoco: solicita velocidad de ejecución y huella de memoria. Puede interesarle otros factores, buenos para usted, que no significan que esta pregunta no sea válida, simplemente no cumple con sus intereses personales. Es como si fueras un programador de Java y quisieras cerrar cada pregunta de C # porque no habla de Java.
SF.

Respuestas:

239

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 timey 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 de make checkesta manera:

coan_test_timer: info: coan processed 70844 input_files.
coan_test_timer: info: run time in coan: 16.4 secs.
coan_test_timer: info: Average processing time per input file: 0.000231 secs.

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).

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 231 | 237 |0.97 |
----------|-----|-----|-----|
Clang-3.2 | 234 | 186 |1.25 |
----------|-----|-----|------
GCC/Clang |0.99 | 1.27|

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:

  1. -O3 optimización fue marginalmente perjudicial para GCC
  2. -O3 optimización fue muy beneficiosa para Clang
  3. En la optimización de -O2, GCC fue más rápido que Clang en solo un bigote
  4. En la optimización de -O3, Clang fue significativamente más rápido que GCC.

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 un std::shared_ptr<X>. El sesgo hacia std::unique_ptrera tonto, ya que estos punteros de hecho se transfirieron, pero std::unique_ptrparecía la opción más adecuada para reemplazar std::auto_ptren 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 compilar std::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;

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 234 | 234 |1.00 |
----------|-----|-----|-----|
Clang-3.2 | 188 | 187 |1.00 |
----------|-----|-----|------
GCC/Clang |1.24 |1.25 |

Los puntos de nota aquí son:

  1. Ninguno de los compiladores ahora se beneficia en absoluto de la optimización -O3.
  2. Clang supera a GCC con la misma importancia en cada nivel de optimización.
  3. El rendimiento de GCC solo se ve afectado marginalmente por el cambio de tipo de puntero inteligente.
  4. El rendimiento de Clang -O2 se ve afectado de manera importante por el cambio de tipo de puntero inteligente.

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:

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.1 | 442 | 443 |1.00 |
----------|-----|-----|-----|
Clang-3.3 | 374 | 370 |1.01 |
----------|-----|-----|------
GCC/Clang |1.18 |1.20 |

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:

  • GCC es indiferente a la optimización de -O3
  • clang se beneficia muy marginalmente de la optimización de -O3
  • Clang supera a GCC por un margen igualmente importante en cada nivel de optimización.

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

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 428 | 428 |1.00 |
----------|-----|-----|-----|
Clang-3.4 | 390 | 365 |1.07 |
----------|-----|-----|------
GCC/Clang | 1.1 | 1.17|

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 -O2y -O3má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

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 210 | 208 |1.01 |
----------|-----|-----|-----|
Clang-3.4 | 252 | 250 |1.01 |
----------|-----|-----|------
GCC/Clang |0.83 | 0.83|

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: -O3la 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 -O3optimizació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.

Mike Kinghan
fuente
99
¿Por qué es más rápido el sonido? por ejemplo, el compilador de intel utilizaba especialidades de chips de intel. ¿Qué está usando el sonido metálico para obtener una ventaja? ¿Se puede reescribir el código para que gcc tenga el mismo rendimiento?
kirill_igum
27
@krill_igum GCC y clang son programas diferentes (enormemente complejos) escritos por diferentes grupos de programadores para hacer el mismo trabajo: traducir el código fuente en código objeto. Es casi inevitable que uno de ellos haga ese trabajo considerablemente mejor que el otro en cualquier prueba elegida en cualquier momento. No tiene que haber ninguna "cosa" especial que el ganador esté "usando" para "obtener una ventaja", y dado que ambos programas son de código abierto, no tienen secretos entre sí.
Mike Kinghan
3
Es posible utilizar kcachegrindpara identificar las funciones donde los ejecutables generados difieren en rendimiento.
44
-1: Esto es más una novela (o publicación de blog) que una respuesta.
John Saunders
6060
@ JohnSaunders: Lo que para una persona es una respuesta detallada y profunda, para otra es una novela que no merece su atención. Dime qué separa a estas dos personas.
SF.
48

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.

Nietzche-jou
fuente
2
Justo hoy ejecuto algunos puntos de referencia sobre la velocidad de compilación de Clang y es muy decepcionante para C. pura. Compilar 35 archivos C con 270 KLOC clang fue solo un 25% más rápido. Cuando veo lo rápido que tinycc es en Linux, es un mal resultado para un nuevo compilador escrito. Mejora cuando se utilizan optimizaciones -O2 / -O3, pero dado que se usan para la versión de compilación, el rendimiento del compilador no importa en estos casos.
Lothar
77
@mcandre Quizás Nietzche-jou fue compilado con Clang, mientras que usted fue compilado con GCC.
Mateen Ulhaq
18

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 .

mcandre
fuente
12
En realidad lo hace. Durante el desarrollo, el tiempo de compilación (y el consumo de recursos debido a la compilación) son mucho más un cuello de botella que el rendimiento binario. Después de todo, compilamos en modo de depuración en esta etapa. Es solo cuando llega la etapa de prueba y envío que cambia al modo Release e intenta obtener un binario lo más rápido posible.
Matthieu M.
3
@ Matthieu M: Juro que esa respuesta dijo "puede ...", como si estuviera planteando una posible preocupación. Supongo que tal vez valió la pena mencionarlo porque estaba, ya sabes, relacionado con el OP.
JM Becker
De acuerdo, aunque todos los buenos puntos aquí. Prefiero agregar una segunda o tercera unidad RAID 0, una SSD o más RAM más rápida y obtener el mejor rendimiento .exe, siempre que esas medidas puedan llevarlo a la paridad o al cierre. A veces también es útil desarrollar con más de un compilador. Puede informarle sobre características no portátiles, Y detectar errores que de otra forma no se detectarán, o puede llevar a días de tiempo perdido tratando de depurar código que un compilador mejor hubiera advertido / errado.
Intenté hoy comparar un código entero crítico de rendimiento ajustado que escribí y GCC funcionó mucho más rápido (22S clang-llvm 25S) usando tanto -O2 como -O3. Piense que el uso de conmutadores de compilación (gcc o clang) cubre la mayoría de las características no estándar y advertencias estáticas. En su propio proyecto grande, no compilando por lotes el código de otras personas, está haciendo algo mal en su sistema de compilación si el tiempo de compilación domina el tiempo de enlace. Hay herramientas como ccache.samba.org que ayudan si haces limpieza a menudo. Otro problema con el cambio de compiladores es la inversión constante en pruebas / validación que se descarta.
Rob11311
code.google.com/p/distcc es otro proyecto que puede acelerar los tiempos de compilación masiva, si toda una biblioteca necesita recompilación debido a cambios en la estructura de datos o para fines de verificación / validación
Rob11311
11

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 -Ogopció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
Últimamente, (3.3 y 4.8) no veo mucha diferencia entre el tiempo de compilación. (en "mis" programas con tiempos de compilación entre 10 segundos y 30 segundos).
alfC
9

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

Paul R
fuente
8

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:

for (;;) {
    if (!visited) {
        ....
    }
    node++;
    if (!*node) break;
  }

Entonces gcc, al compilar con -O3o -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):

+---------------------+-----+-----+
|Type                 |GCC  |Clang|
+---------------------+-----+-----+
|Copying GC           |22.46|22.55|
|Copying GC, optimized|22.01|20.22|
|Mark & Sweep         | 8.72| 8.38|
|Ref Counting/Cycles  |15.14|14.49|
|Ref Counting/Plain   | 9.94| 9.32|
+---------------------+-----+-----+

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.

Björn Lindqvist
fuente
4

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

kimi
fuente