C++ Rusia: cómo sucedió

Si al principio de la obra dices que hay un código C++ colgado en la pared, al final seguramente te disparará en el pie.

Bjarne Stroustrup

Del 31 de octubre al 1 de noviembre se celebró en San Petersburgo la conferencia C++ Russia Piter, una de las conferencias de programación a gran escala en Rusia, organizada por JUG Ru Group. Los oradores invitados incluyen miembros del Comité de Estándares de C++, oradores de CppCon, autores de libros de O'Reilly y mantenedores de proyectos como LLVM, libc++ y Boost. La conferencia está dirigida a desarrolladores experimentados de C++ que quieran profundizar sus conocimientos e intercambiar experiencias en la comunicación en vivo. Los estudiantes, estudiantes de posgrado y profesores universitarios reciben muy buenos descuentos.

La edición de Moscú de la conferencia estará disponible para visitar a partir de abril del próximo año, pero mientras tanto nuestros estudiantes le contarán las cosas interesantes que aprendieron en el último evento. 

C++ Rusia: cómo sucedió

Fotos de álbum de la conferencia

sobre nosotros

En este post trabajaron dos estudiantes de la Escuela Superior de Economía de la Universidad Nacional de Investigación de San Petersburgo:

  • Liza Vasilenko es una estudiante de cuarto año de pregrado que estudia Lenguajes de Programación como parte del programa de Matemáticas Aplicadas e Informática. Después de familiarizarme con el lenguaje C++ en mi primer año en la universidad, posteriormente adquirí experiencia trabajando con él a través de pasantías en la industria. Mi pasión por los lenguajes de programación en general y la programación funcional en particular dejó su huella en la selección de ponencias de la conferencia.
  • Danya Smirnov es estudiante de primer año del programa de maestría "Programación y análisis de datos". Mientras aún estaba en la escuela, escribí problemas de las Olimpíadas en C++, y de alguna manera sucedió que el lenguaje aparecía constantemente en las actividades educativas y finalmente se convirtió en el principal lenguaje de trabajo. Decidí participar en la conferencia para mejorar mis conocimientos y también conocer nuevas oportunidades.

En el boletín, el liderazgo docente a menudo comparte información sobre eventos educativos relacionados con nuestra especialidad. En septiembre vimos información sobre C++ Rusia y decidimos registrarnos como oyentes. Esta es nuestra primera experiencia de participación en este tipo de conferencias.

Estructura de la conferencia

  • Доклады

Durante dos días, los expertos leyeron 30 informes que abarcaban muchos temas candentes: usos ingeniosos de las características del lenguaje para resolver problemas aplicados, próximas actualizaciones del lenguaje en relación con el nuevo estándar, compromisos en el diseño de C++ y precauciones al trabajar con sus consecuencias, ejemplos de una interesante arquitectura del proyecto, así como algunos detalles ocultos de la infraestructura del lenguaje. Se realizaron tres representaciones simultáneamente, la mayoría de las veces dos en ruso y una en inglés.

  • Zonas de discusión

Después del discurso, todas las preguntas no formuladas y las discusiones inconclusas se trasladaron a áreas especialmente designadas para la comunicación con los oradores, equipadas con pizarrones. Una buena manera de pasar el descanso entre discursos con una conversación amena.

  • Charlas relámpago y debates informales

Si desea dar un informe breve, puede registrarse en la pizarra para la Lightning Talk nocturna y obtener cinco minutos de tiempo para hablar sobre cualquier tema de la conferencia. Por ejemplo, una introducción rápida a los desinfectantes para C++ (para algunos era nuevo) o una historia sobre un error en la generación de ondas sinusoidales que sólo se puede escuchar, pero no ver.

Otro formato es el panel de discusión “Con un Comité de Corazón a Corazón”. En el escenario están algunos miembros del comité de estandarización, sobre el proyector hay una chimenea (oficialmente - para crear una atmósfera sincera, pero la razón "porque TODO ESTÁ EN FUEGO" parece más divertida), preguntas sobre el estándar y la visión general de C++. , sin acaloradas discusiones técnicas y holiwars. Resultó que en el comité también hay personas vivas que pueden no estar completamente seguras de algo o no saber algo.

Para los fanáticos de Holivar, el asunto quedó en el tercer evento: la sesión BOF "Go vs. C++". Tomamos a un amante de Go, un amante de C++, antes del inicio de la sesión preparan juntos 100500 diapositivas sobre un tema (como problemas con los paquetes en C++ o la falta de genéricos en Go), y luego mantienen una animada discusión entre ellos y con la audiencia, y la audiencia intenta comprender dos puntos de vista a la vez. Si un holívar comienza fuera de contexto, el moderador interviene y reconcilia a las partes. Este formato es adictivo: varias horas después del inicio, sólo se completaron la mitad de las diapositivas. El final tuvo que acelerarse mucho.

  • Stands para socios

Los socios de la conferencia estuvieron representados en los pabellones: en los stands hablaron sobre proyectos actuales, ofrecieron prácticas y empleo, realizaron concursos y pequeños concursos y también sortearon bonitos premios. Al mismo tiempo, algunas empresas incluso se ofrecieron a realizar las etapas iniciales de entrevistas, lo que podría resultar útil para quienes acudieran no sólo a escuchar informes.

Detalles técnicos de los informes.

Escuchamos informes ambos días. A veces fue difícil elegir un informe entre los paralelos: acordamos dividirnos e intercambiar los conocimientos adquiridos durante los descansos. Y aun así parece que queda mucho por fuera. Aquí nos gustaría hablaros del contenido de algunos de los informes que nos parecieron más interesantes.

Excepciones en C++ a través del prisma de optimizaciones del compilador, Roman Rusyaev

C++ Rusia: cómo sucedió
Deslizar desde presentaciones

Como sugiere el título, Roman analizó cómo trabajar con excepciones usando LLVM como ejemplo. Al mismo tiempo, para aquellos que no utilizan Clang en su trabajo, el informe aún puede dar una idea de cómo se podría optimizar potencialmente el código. Esto se debe a que los desarrolladores de compiladores y las correspondientes bibliotecas estándar se comunican entre sí y muchas soluciones exitosas pueden coincidir.

Entonces, para manejar una excepción, necesita hacer muchas cosas: llamar al código de manejo (si lo hay) o liberar recursos en el nivel actual y aumentar la pila. Todo esto lleva al hecho de que el compilador agrega instrucciones adicionales para llamadas que potencialmente generan excepciones. Por lo tanto, si la excepción no se genera, el programa seguirá realizando acciones innecesarias. Para reducir de alguna manera la sobrecarga, LLVM tiene varias heurísticas para determinar situaciones en las que no es necesario agregar código de manejo de excepciones o en las que se puede reducir la cantidad de instrucciones "adicionales".

El ponente examina alrededor de una docena de ellos y muestra tanto situaciones en las que ayudan a acelerar la ejecución del programa como aquellas en las que estos métodos no son aplicables.

Por lo tanto, Roman Rusyaev lleva a los estudiantes a la conclusión de que el código que contiene manejo de excepciones no siempre se puede ejecutar sin gastos generales y les da el siguiente consejo:

  • al desarrollar bibliotecas, en principio vale la pena abandonar las excepciones;
  • Si aún se necesitan excepciones, siempre que sea posible, vale la pena agregar modificadores noexcept (y const) en todas partes para que el compilador pueda optimizar tanto como sea posible.

En general, el orador confirmó la opinión de que es mejor utilizar las excepciones al mínimo o abandonarlas por completo.

Las diapositivas del informe están disponibles en el siguiente enlace: [“Excepciones de C++ a través de la lente de las optimizaciones del compilador LLVM”]

Generadores, corrutinas y otras dulzuras que desenrollan el cerebro, Adi Shavit

C++ Rusia: cómo sucedió
Deslizar desde presentaciones

Uno de los muchos informes de esta conferencia dedicada a las innovaciones en C++20 fue memorable no solo por su colorida presentación, sino también por su clara identificación de los problemas existentes con la lógica de procesamiento de colecciones (for loop, callbacks).

Adi Shavit destaca lo siguiente: los métodos disponibles actualmente atraviesan toda la colección y no brindan acceso a ningún estado intermedio interno (o lo hacen en el caso de las devoluciones de llamada, pero con una gran cantidad de efectos secundarios desagradables, como el Callback Hell) . Parecería que hay iteradores, pero incluso con ellos no todo es tan sencillo: no hay puntos comunes de entrada y salida (comienzo → fin versus rbegin → rend, etc.), no está claro cuánto tiempo iteraremos. ¡A partir de C++20, estos problemas están resueltos!

Primera opción: rangos. Al empaquetar iteradores, obtenemos una interfaz común para el principio y el final de una iteración, y también obtenemos la capacidad de componer. Todo esto facilita la creación de canales de procesamiento de datos completos. Pero no todo es tan sencillo: parte de la lógica de cálculo se encuentra dentro de la implementación de un iterador específico, lo que puede complicar la comprensión y depuración del código.

C++ Rusia: cómo sucedió
Deslizar desde presentaciones

Bueno, para este caso, C++ 20 agregó corrutinas (funciones cuyo comportamiento es similar a los generadores en Python): la ejecución se puede aplazar devolviendo algún valor actual preservando un estado intermedio. De esta forma, logramos no sólo trabajar con los datos tal como aparecen, sino también encapsular toda la lógica dentro de una corrutina específica.

Pero hay un problema: por el momento, los compiladores existentes solo los admiten parcialmente y tampoco se implementan tan claramente como nos gustaría: por ejemplo, todavía no vale la pena usar referencias y objetos temporales en las corrutinas. Además, existen algunas restricciones sobre lo que pueden ser corrutinas, y las funciones constexpr, constructores/destructores y main no están incluidos en esta lista.

Así, las corrutinas resuelven una parte importante de los problemas con la simplicidad de la lógica de procesamiento de datos, pero sus implementaciones actuales requieren mejoras.

Materiales:

Trucos de C++ de Yandex.Taxi, Anton Polukhin

En mis actividades profesionales, a veces tengo que implementar cosas puramente auxiliares: un contenedor entre la interfaz interna y la API de alguna biblioteca, registro o análisis. En este caso, normalmente no es necesaria ninguna optimización adicional. Pero, ¿qué pasa si estos componentes se utilizan en algunos de los servicios más populares de RuNet? En tal situación, ¡tendrás que procesar terabytes por hora solo de registros! Entonces cada milisegundo cuenta y por eso hay que recurrir a varios trucos: Anton Polukhin habló de ellos.

Quizás el ejemplo más interesante fue la implementación del patrón de puntero a implementación (pimpl). 

#include <third_party/json.hpp> //PROBLEMS! 
struct Value { 
    Value() = default; 
    Value(Value&& other) = default; 
    Value& operator=(Value&& other) = default; 
    ~Value() = default; 

    std::size_t Size() const { return data_.size(); } 

private: 
    third_party::Json data_; 
};

En este ejemplo, primero quiero deshacerme de los archivos de encabezado de bibliotecas externas; esto se compilará más rápido y usted podrá protegerse de posibles conflictos de nombres y otros errores similares. 

Bien, movimos #include al archivo .cpp: necesitamos una declaración directa de la API empaquetada, así como std::unique_ptr. Ahora tenemos asignaciones dinámicas y otras cosas desagradables como datos dispersos en un montón de datos y garantías reducidas. std::aligned_storage puede ayudar con todo esto. 

struct Value { 
// ... 
private: 
    using JsonNative = third_party::Json; 
    const JsonNative* Ptr() const noexcept; 
    JsonNative* Ptr() noexcept; 

    constexpr std::size_t kImplSize = 32; 
    constexpr std::size_t kImplAlign = 8; 
    std::aligned_storage_t<kImplSize, kImplAlign> data_; 
};

El único problema: debe especificar el tamaño y la alineación de cada contenedor; hagamos nuestra plantilla pimpl con parámetros , use algunos valores arbitrarios y agregue una marca al destructor para saber que hicimos todo bien: 

~FastPimpl() noexcept { 
    validate<sizeof(T), alignof(T)>(); 
    Ptr()->~T(); 
}

template <std::size_t ActualSize, std::size_t ActualAlignment>
static void validate() noexcept { 
    static_assert(
        Size == ActualSize, 
        "Size and sizeof(T) mismatch"
    ); 
    static_assert(
        Alignment == ActualAlignment, 
        "Alignment and alignof(T) mismatch"
    ); 
}

Dado que T ya está definido al procesar el destructor, este código se analizará correctamente y en la etapa de compilación generará el tamaño requerido y los valores de alineación que deben ingresarse como errores. Por lo tanto, a costa de una ejecución de compilación adicional, nos deshacemos de la asignación dinámica de clases empaquetadas, ocultamos la API en un archivo .cpp con la implementación y también obtenemos un diseño que es más adecuado para el almacenamiento en caché por parte del procesador.

El registro y el análisis parecían menos impresionantes y, por lo tanto, no se mencionarán en esta revisión.

Las diapositivas del informe están disponibles en el siguiente enlace: ["Trucos de C++ de Taxi"]

Técnicas modernas para mantener su código SECO, Björn Fahller

En esta charla, Björn Fahller muestra varias formas diferentes de combatir el defecto estilístico de las comprobaciones repetidas del estado:

assert(a == IDLE || a == CONNECTED || a == DISCONNECTED);

¿Suena familiar? Al utilizar varias técnicas potentes de C++ introducidas en estándares recientes, puede implementar elegantemente la misma funcionalidad sin ninguna penalización en el rendimiento. Comparar:   

assert(a == any_of(IDLE, CONNECTED, DISCONNECTED));

Para manejar un número no fijo de cheques, inmediatamente necesita usar plantillas variadas y expresiones de plegado. Supongamos que queremos verificar la igualdad de varias variables con el elemento state_type de la enumeración. Lo primero que me viene a la mente es escribir una función auxiliar is_any_of:


enum state_type { IDLE, CONNECTED, DISCONNECTED };

template <typename ... Ts>
bool is_any_of(state_type s, const Ts& ... ts) { 
    return ((s == ts) || ...); 
}

Este resultado intermedio es decepcionante. Hasta ahora el código no se vuelve más legible:

assert(is_any_of(state, IDLE, DISCONNECTING, DISCONNECTED)); 

Los parámetros de plantilla que no son de tipo ayudarán a mejorar un poco la situación. Con su ayuda, transferiremos los elementos enumerables de la enumeración a la lista de parámetros de la plantilla: 

template <state_type ... states>
bool is_any_of(state_type t) { 
    return ((t == states) | ...); 
}
	
assert(is_any_of<IDLE, DISCONNECTING, DISCONNECTED>(state)); 

Al usar auto en un parámetro de plantilla sin tipo (C++17), el enfoque simplemente se generaliza a comparaciones no solo con elementos state_type, sino también con tipos primitivos que se pueden usar como parámetros de plantilla sin tipo:


template <auto ... alternatives, typename T>
bool is_any_of(const T& t) {
    return ((t == alternatives) | ...);
}

A través de estas sucesivas mejoras se consigue la sintaxis fluida deseada para los controles:


template <class ... Ts>
struct any_of : private std::tuple<Ts ...> { 
// поленимся и унаследуем конструкторы от tuple 
        using std::tuple<Ts ...>::tuple;
        template <typename T>
        bool operator ==(const T& t) const {
                return std::apply(
                        [&t](const auto& ... ts) {
                                return ((ts == t) || ...);
                        },
                        static_cast<const std::tuple<Ts ...>&>(*this));
        }
};

template <class ... Ts>
any_of(Ts ...) -> any_of<Ts ... >;
 
assert(any_of(IDLE, DISCONNECTING, DISCONNECTED) == state);

En este ejemplo, la guía de deducción sirve para sugerir los parámetros de plantilla de estructura deseados al compilador, que conoce los tipos de argumentos del constructor. 

Además, más interesante. Bjorn enseña cómo generalizar el código resultante para operadores de comparación más allá de == y luego para operaciones arbitrarias. A lo largo del camino, se explican características como el atributo no_unique_address (C++20) y los parámetros de plantilla en funciones lambda (C++20) mediante ejemplos de uso. (Sí, ahora la sintaxis lambda es aún más fácil de recordar: son cuatro pares consecutivos de paréntesis de todo tipo). La solución final que utiliza funciones como detalles del constructor realmente me calienta el alma, sin mencionar la expresión tupla en las mejores tradiciones de lambda. cálculo.

Al final, no olvides pulirlo:

  • Recuerda que las lambdas son constexpr gratis; 
  • Agreguemos un reenvío perfecto y observemos su fea sintaxis en relación con el paquete de parámetros en el cierre lambda;
  • Demos al compilador más oportunidades de optimización con noexcept condicional; 
  • Ocupémonos de una salida de error más comprensible en las plantillas gracias a los valores de retorno explícitos de lambdas. Esto obligará al compilador a realizar más comprobaciones antes de que se llame realmente a la función de plantilla, en la etapa de verificación de tipos. 

Para obtener más información, consulte los materiales de la conferencia: 

nuestras impresiones

Nuestra primera participación en C++ Rusia fue memorable por su intensidad. C++ Rusia me dio la impresión de ser un evento sincero, donde la línea entre la formación y la comunicación en vivo es casi imperceptible. Todo, desde el estado de ánimo de los ponentes hasta las competencias de los socios del evento, favorece los debates acalorados. El contenido de la conferencia, que consta de informes, cubre una gama bastante amplia de temas que incluyen innovaciones en C++, estudios de casos de grandes proyectos y consideraciones arquitectónicas ideológicas. Pero sería injusto ignorar el componente social del evento, que ayuda a superar las barreras del idioma no sólo en relación con C++.

¡Agradecemos a los organizadores de la conferencia por la oportunidad de participar en tal evento!
Es posible que hayas visto la publicación de los organizadores sobre el pasado, presente y futuro de C++ Rusia. en el blog de JUG Ru.

¡Gracias por leer y esperamos que nuestro recuento de eventos haya sido útil!

Fuente: habr.com

Añadir un comentario