Revisión de informes de C ++ Russia Piter 2019

En un programa conjunto de ITMO y JetBrains Master , pedimos a los estudiantes que son enviados a la conferencia que escriban un informe con una revisión de los informes.
Publicamos uno de estos informes sobre la conferencia C ++ Russia Piter 2019. El autor es un estudiante de segundo año, Artyom Khoroshev.



A principios de noviembre, asistí a la conferencia cpp-russia-piter, a continuación hablaré sobre los informes que recuerdo.

Roman Rusyaev: excepciones de C ++ a través del prisma de las optimizaciones del compilador


Un informe interesante en el que el orador sobre el ejemplo de LLVM habló sobre las excepciones de costo cero en C ++ moderno.

LLVM IR presenta el programa como un gráfico de control de flujo. En los nodos del gráfico hay bloques de instrucciones que deben seguirse. Al final de cada bloque, hay un terminador que transfiere el control al siguiente bloque. El terminador puede ser una transición condicional a otro bloque, una instrucción de retorno o una instrucción de invocación especial, que tiene la semántica de llamar a la función y, si tiene éxito, transfiere el flujo de control a un bloque y, en caso de una excepción, indica el bloque al que desea procesarlo. Las instrucciones dentro de un bloque tienen la propiedad de que si golpeamos un bloque, ejecutaremos todas las instrucciones o no caeremos en este bloque en absoluto. Hay optimizaciones que solo pueden funcionar dentro de un solo bloque. En el caso de bloques pequeños, tendrán un contexto más pequeño, como resultado es peor hacer optimizaciones.
El orador habló sobre cómo los compiladores modernos pueden convertir las declaraciones de invocación en declaraciones de llamada, que ya no son terminales, y, como resultado, le dan al compilador más espacio para optimizaciones. Pero, para no depender del compilador, puede escribir la función noexcept usted mismo (si esto es correcto) para asegurarse de que el compilador realizará todas las optimizaciones.

(informe de diapositivas)

Maxim Khizhinsky: viviendas de clase confort para actores y manipuladores


El orador estableció un objetivo para deshacerse de una serie de problemas asociados con la programación paralela:

  • datos compartidos
  • cambio de contexto,
  • sincronizando
  • Creación frecuente de flujo sobre la marcha para necesidades a corto plazo.

Como resultado, el orador sugirió dividir su programa en componentes, cuya vida útil será igual a la vida útil del programa, y ​​colocar los componentes en "apartamentos", cuyo número debería ser igual al número de hilos. Como resultado, los componentes en sí mismos son de un solo subproceso, y la comunicación entre los componentes debe ocurrir a través del paso de mensajes. Estoy de acuerdo en que esto resuelve el problema, pero a costa de toda la personalización de nuestro programa, debemos hacerlo en el momento de la compilación. Como mínimo, es necesario en el momento de la compilación distribuir uniformemente los componentes entre los "apartamentos", lo que evita que el sistema se reequilibre dependiendo de la carga. Como resultado, en mi opinión, la solución en sí no parece lo suficientemente flexible.

(informe de diapositivas)

Nikolay Beloborodov: el uso de asignadores de losas en aplicaciones de red altamente cargadas


El nombre habla por sí mismo. El orador contó cómo aumentaron significativamente el rendimiento del sistema utilizando un asignador de losas . El asignador de losas opera en varias entidades:

  • la losa es una pieza de memoria contigua (generalmente un tamaño fijo) que se divide en secciones del mismo tamaño. Estas áreas se utilizan para almacenar objetos del mismo tamaño,
  • caché: una lista de losas con la misma división,
  • asignador de losa - un conjunto de cachés.

Gracias a esta construcción, los objetos del mismo tamaño se almacenan localmente. La desasignación está diseñada como una marca de que un sitio en particular es débil y puede reutilizarse. Esto evita la fragmentación de la memoria.

A partir de esta definición de la losa de un asignador, queda claro que es adecuado para resaltar la liberación de objetos cuyo tamaño se encuentra en un intervalo limitado. Por ejemplo, al asignar un tamaño cada vez más grande, se creará una nueva caché, las cachés antiguas no se reutilizarán.

El orador dijo que debido a esto tuvieron que abandonar algunos contenedores, en favor de otros. Por ejemplo, el vector fue reemplazado con una lista, hashmap con un árbol, pero aún así, se obtuvo una ganancia en el rendimiento.

(informe de diapositivas)

Anton Polukhin: trucos de taxi C ++


Los informes de Anton Polukhin siempre son interesantes, y las soluciones que ofrece se ven bien. Esta vez, Anton mostró cómo se puede mejorar el patrón de pimpl en términos de asignación dinámica. Para hacer esto, debe colocar el repositorio para el objeto de implementación en el objeto mismo. Permíteme recordarte que el patrón clásico de pimpl es el siguiente:

// Foo.h struct Foo { Foo(); private: struct Foo_impl; //forward declaration std::unique_ptr<Foo_impl> impl; }; // Foo.cpp //implementation struct Foo::Foo_impl { }; 

Queremos deshacernos de la asignación dinámica, para esto prepararemos un lugar por adelantado directamente en el objeto Foo:
 // Foo.h struct Foo { Foo(); private: struct Foo_impl; //forward declaration std::aligned_storage_t<sizeof(Foo_impl), alignof(Foo_impl)> impl; }; // Foo.cpp //implementation struct Foo::Foo_impl { } 

Este método no funcionará, ya que no tenemos información completa sobre el tipo Foo_impl en Foo.h y se recibirá un error de compilación. La única solución que queda es adivinar el tamaño del almacenamiento por adelantado.

 // Foo.h struct Foo { Foo(); private: struct Foo_impl; //forward declaration constexpr std::size_t kImplSize = 32; constexpr std::size_t kImplAlign = 8; std::aligned_storage_t<kImplSize, kImplAlign> impl; }; // Foo.cpp //implementation struct Foo::Foo_impl { } 

Pero debe agregar una verificación de que el tamaño sigue siendo correcto. Esto es necesario, ya que un intento de colocar el objeto en un búfer inapropiado es UB.

 // Foo.h struct Foo { Foo(); ~Foo(); private: constexpr std::size_t kImplSize = 32; constexpr std::size_t kImplAlign = 8; struct Foo_impl; //forward declaration std::aligned_storage_t<kImplSize, kImplAlign> impl; }; // Foo.cpp //implementation struct Foo::Foo_impl { } struct Foo::~Foo() { static_assert(kImplSize==sizeof(Foo_impl),"Size and sizeof(T) mismatch"); static_assert(kImplAlign==alignof(kImplAlign),"Alignment and alignof(T) mismatch"); // call destructor of Foo_impl } 

Hacemos una comprobación en el archivo cpp, y si algo se indica incorrectamente, terminamos con un error de compilación e imprimimos el tamaño correcto de la estructura para que el programador pueda adivinar desde el segundo intento.

Anton mostró cómo hacer conveniente su biblioteca de serialización en varios formatos, sin olvidar la función ADL: si hay parámetros de plantilla para los argumentos de la función, la función se buscará en el espacio de nombre de parámetro de estos argumentos.

(informe de diapositivas)

Eric Niebler: una abstracción unificadora para asíncrono en C ++


Un informe interesante, que analiza los problemas de las abstracciones asíncronas en el estándar de lenguaje existente: por qué el futuro y la promesa son lentos, y podemos diseñar la biblioteca de tal manera que evite estos gastos generales. Los desarrolladores de Facebook parecen tener una solución decente https://github.com/facebookexperimental/libunifex

(informe de diapositivas)

Dmitry Kozhevnikov y Andrey Davydov: dos informes sobre módulos


El programa tuvo dos informes consecutivos sobre los módulos. Después de escuchar ambos informes, quedó claro que los módulos aún no están listos para su uso. Esto me molestó un poco, ya que básicamente no estaba interesado en cómo se implementa esta nueva característica del lenguaje, y pensé que C ++ 20 saldría y estaría listo para usar de inmediato. Desafortunadamente, resultó no ser así.

(diapositivas de informes: 1 , 2 )

Conclusión


La conferencia pasada estuvo complacida con ejemplos interesantes del uso de características bien conocidas del lenguaje. Una gran cantidad de informes se referían a chips del siguiente estándar: C ++ 20. Esto, por supuesto, es muy útil para todos los desarrolladores de C ++.

Source: https://habr.com/ru/post/480798/


All Articles