Traducción de la publicación de Titus Winters en el Grupo de trabajo 21 (WG21) - Comité de normalización del lenguaje C ++. El autor analiza un tema importante: soporte para compatibilidad binaria hacia atrás o ABI (interfaz binaria de aplicación).
En los últimos años en WG21, he promovido activamente que el progreso es más importante que la compatibilidad con versiones anteriores. Pero yo mismo ya no creo en esto, especialmente con respecto al mantenimiento de la compatibilidad binaria (ABI). En las últimas tres versiones (C ++ 14, C ++ 17 y C ++ 20) ABI fue tan estable como pudimos. Incluso si WG21 decide romper la compatibilidad con versiones anteriores de ABI en C ++ 23, hemos estado proporcionando compatibilidad binaria en muchas plataformas durante más de 10 años. En mi opinión, la ley de Hyrum domina en las alteraciones a gran escala de los sistemas de software. Ahora no puede decir cuántos usuarios tienen la suposición de la estabilidad de la biblioteca estándar ABI (no importa cuán sabio o cuán explícito o implícito) esté firmemente "cosido en la subcorteza", tal vez la mitad de los desarrolladores de C ++ en el mundo.
Mantengo una lista de lo que WG21 podría arreglar en el idioma si decidimos "romper" el ABI. Y no puedo decir con confianza que el costo total del retrabajo, que implicará la implementación de solo esta lista, es comparable al costo de violar ABI en todo el ecosistema. Vimos muchas pequeñas mejoras en la consistencia de la API, la calidad del código estándar de la biblioteca, etc., pero sin duda no hay un solo cambio "innovador" que justifique este costo para el desarrollador promedio. Quizás obtendríamos mejores implementaciones del estándar, daríamos la oportunidad de resolver problemas para implementaciones que no cumplen con las especificaciones estándar de hoy. Pero ninguna mejora en mi lista claramente vale la pena.
Más importante aún, debido a las limitaciones de ABI, no podemos eliminar pérdidas significativas de rendimiento. No podemos deshacernos del costo significativo de transmitir unique_ptr por valor [el rendimiento de Chandler en CppCon 2019, que se publicará más adelante], no podemos cambiar std :: hash o la ubicación de la clase en la memoria para unorped_map sin obligar a todos a recopilar todo en todas partes. El rendimiento de los hash se ha estudiado ampliamente a lo largo de los años y, teniendo en cuenta la optimización de las búsquedas en la tabla y el hash adecuado, estamos seguros de que podemos proporcionar una implementación desordenada_map / std :: hash que es compatible con API y proporciona un aumento del rendimiento del 200-300%. Pero las restricciones ABI no permiten esto. Estudios adicionales sobre la optimización y el ajuste de SSO para std :: string sugieren un aumento no trivial en el rendimiento (1% en microbenchmarks y escalado): la API no se ve afectada, pero las restricciones ABI no lo permiten.
La pérdida total de productividad bloqueada exclusivamente por ABI alcanza varios puntos porcentuales, posiblemente hasta un 5-10%. Esto no es algo de lo que el ecosistema en su conjunto no pueda prescindir, pero puede que no sea aceptable para algunas organizaciones (Google entre ellas). Esto, por supuesto, es una gran pérdida de rendimiento que es aceptable para C ++: recuerde que este es un lenguaje que afirma que no deja espacio para un competidor más productivo. La mayoría de los usuarios no parecen preocupados por esta degradación del rendimiento: hay otras implementaciones de tablas hash para aquellos preocupados por el rendimiento absoluto. La ineficiencia general asociada con el paso de unique_ptr en valor y otros problemas del lenguaje ABI se destacan en un número muy pequeño de tareas. Las organizaciones que necesitan la máxima productividad pueden seguir su propio camino (y hacerlo), utilizando bibliotecas no estándar y herramientas de configuración no estándar. Esto es natural y debe entenderse claramente.
Un cambio en ABI afectará a un número relativamente mayor de usuarios. Sospecho que una proporción significativa de estos usuarios no sospecha cuán fuerte es su dependencia de ABI. En el ecosistema de los servidores de Google, casi todo se recopila de la fuente, hay pocas dependencias externas y hay una oportunidad mejor que el promedio para realizar una refactorización a gran escala. Pero incluso para nosotros, los cambios recientes que rompieron ABI en la biblioteca estándar costaron 5-10 años de ingeniería.
El costo total de romper la compatibilidad con versiones anteriores de ABI para todo el ecosistema C ++ puede estimarse de manera conservadora en el " Ingeniero del Milenio ": coordinar la reconstrucción de cada proveedor de complementos, .so o dll en el mundo requerirá enormes recursos humanos. Junto con la separación del ecosistema debido a los módulos C ++ 20, cambiar el ABI en el cronograma de desarrollo e implementación de C ++ 23 puede conducir a una separación dura del ecosistema.
Hay muchas preguntas que no se pueden responder con esta discusión. ¿Cuánto tiempo podemos continuar hasta el punto en que cambiar la ABI de solo útil se convierta en una necesidad crítica? Si elegimos explícitamente el soporte de estabilidad ABI, ¿qué tan costoso será el cambio cuando y cuando surja una necesidad tan crítica? Si los problemas de seguridad como Spectre y Meltdown requieren un cambio en la convención de llamadas, ¿cuánto costará C ++ para superar este hito? ¿Qué proporción de desarrolladores usan C ++ porque pretendemos poner el rendimiento por encima de todo lo demás? Peor aún: ¿cuánto tiempo puede C ++ pretender ser el lenguaje más rápido y no tener que hacer tales optimizaciones?
Si conscientemente no podemos permitir o no queremos cambiar el ABI, entonces esta decisión debe expresarse en voz alta. Debemos decir claramente que este es un lenguaje que coloca la estabilidad de ABI por encima del último porcentaje de productividad. Estoy dispuesto a argumentar que en la práctica este ha sido el caso en los últimos años. Necesitamos que los usuarios sepan qué esperar de nosotros y que las bibliotecas como Boost, Folly o Absail hagan la elección correcta si se necesita rendimiento. Pero esto no ayuda en absoluto con las restricciones relacionadas con ABI en el lenguaje en sí, como el costo de transmitir unique_ptr. La biblioteca estándar conserva su importancia en este modelo de desarrollo: la biblioteca estándar es lo que usamos para compatibilidad y estabilidad. Esto puede requerir un cambio en el enfoque y la dirección del desarrollo: es posible que deseemos diseñar para una mayor flexibilidad en condiciones cambiantes, y no para un rendimiento "limpio".
Si argumentamos que la productividad es más importante que la estabilidad ABI, debemos decidir de inmediato cuándo exactamente "romperemos" la compatibilidad con versiones anteriores y haremos todo lo posible para que el ecosistema acepte dichos cambios. Y declara clara y en voz alta que vamos por este camino. Debe comprender que cuanto más tiempo pase entre dichos cambios, más caros se volverán, porque con el tiempo habrá una dependencia cada vez mayor de ABI. Nuestros "implementadores" dejaron muy claro que los cambios de C ++ 11 que rompen la compatibilidad fueron dolorosos y costosos. El deseo de evitar la repetición de dichos costos es natural, pero debe elegir: o no los repetimos, porque no cambiamos el ABI o reducimos los costos.
En esencia, hay tres posibilidades para WG21:
- Decidir en qué versión se cambiará la ABI no importa en C ++ 23 o C ++ 26. Advierta a las personas e inmediatamente desarrolle herramientas y diagnósticos para ayudar a identificar los lugares que se romperán. Se enfoca en prácticas y herramientas más consistentes para soportar futuros cambios de ABI. A un implementador en particular no le interesa exponer a sus usuarios a las consecuencias de cambiar el ABI; si otras implementaciones no lo hacen, cambiar el ABI debería ser una actividad coordinada para el beneficio de los futuros usuarios. Idealmente, debe romper todo, para dejar en claro que el código compilado en modo C ++ 23 es incompatible con el código compilado en modos anteriores. Si alguien puede prescindir de la reconstrucción, y otros tendrán errores en el diseño o en el tiempo de ejecución, esto solo aumentará los malentendidos y la decepción.
- Decida que nos esforzamos por lograr la estabilidad de ABI formalizando la práctica de hoy. Este ha sido el caso durante muchos años cuando los implementadores estándar tenían derecho a vetar los cambios abi de ABI: ya establecimos la compatibilidad con ABI por encima de la pureza y el rendimiento del diseño. Si reconocemos esto y les decimos claramente a los usuarios, el ecosistema será mejor. El valor de las bibliotecas adicionales crecerá para aquellos que necesitan exprimir las últimas caídas de rendimiento, pero no requieren la estabilidad proporcionada por el estándar. Otros lenguajes orientados al rendimiento pueden desafiar nuestra posición en el futuro.
- No poder elegir una dirección y guardar el status quo. Este es el peor de los casos para mí: seguimos prestando más atención a la compatibilidad con ABI. Decimos "rendimiento" y votamos "compatibilidad binaria". Tal disonancia daña el ecosistema e implica una falta de acuerdo sobre las prioridades del lenguaje. Espero sinceramente que a través de los esfuerzos de los implementadores y la DG, lleguemos al consenso necesario.
Creo que la opción No. 1 es más adecuada para los usuarios que necesitan el máximo rendimiento, pero tiene un costo increíble para el ecosistema y puede conducir a la fragmentación del lenguaje en el futuro. La opción número 2 es una opción aburrida, responsable y digna: es triste admitir que nos pintamos en la esquina de la habitación e intentamos minimizar las pérdidas asociadas con esto. Elegir la opción # 3 significa no administrar, y rezo para que esto se evite: cualquier elección explícita es mejor que la disonancia actual y la incapacidad de llegar a un acuerdo sobre la elección de objetivos a largo plazo.
Entiendo que hemos alcanzado nuestra posición actual a través de muchos pequeños actos de inacción aparentemente razonable. En los últimos 10 años, no se ha realizado un solo cambio que pueda justificar una violación de la compatibilidad binaria, pero la política implícita de mantener la compatibilidad con versiones anteriores se ha vuelto destructiva para el ecosistema. Sin embargo, al adoptar explícitamente una política de este tipo, abriremos otra posibilidad para que C ++ salga gradualmente del escenario: no puede ser un lenguaje orientado al sistema y orientado al rendimiento, dejando mucho espacio para un lenguaje más productivo. En teoría, cada proveedor puede decidir "romper" ABI en cualquier versión futura, pero la dirección general del pensamiento parece diferente. Estoy seguro de que se requiere discusión y consenso entre los implementadores de la norma y el WG21: ¿qué prioridades debo elegir?