puntas plancha pelo ondas naturales las largo hacer grandes corto con como calor cabello c++ performance loops

c++ - plancha - como hacer ondas naturales en pelo corto



¿Hay alguna ventaja en elegir cualquiera de los bucles como un bucle externo? (4)

¿Hay alguna consideración fundamental con respecto a qué diseño es más probable que permita que se procese una mayor cantidad de mensajes por intervalo de tiempo?

En general, las principales consideraciones con esto a menudo se reducen a dos cosas principales.

  1. Si uno de sus bucles realiza un bucle sobre objetos que potencialmente pueden tener una buena ubicación de memoria (como un bucle sobre una matriz de valores), mantener esa parte en el bucle interno puede mantener los objetos dentro de la caché de la CPU y mejorar el rendimiento.

  2. Si planea intentar paralelizar la operación, mantener la colección "más grande" (en términos de conteo) en el bucle externo le permite paralelizar el bucle externo de manera efectiva, y no provocar la suscripción excesiva de subprocesos, etc. Es típicamente más simple y más limpio para paralelizar un algoritmo en el nivel externo, así el diseño de los bucles con los "bloques" paralelos potencialmente más grandes de trabajo en el bucle externo puede simplificar esto, si es posible más adelante.

Esos oyentes necesitan escribir los mensajes en algún lugar, lo que es bastante costoso, por lo que las llamadas de función por sí mismas podrían no ser muy importantes en cuanto al rendimiento.

Esto probablemente anulará completamente cualquier beneficio de mover un bucle fuera del otro.

En el 95% de todos los casos, solo habrá un oyente.

Si este es el caso, probablemente pondría el bucle de escucha en el ámbito externo, a menos que planee paralelizar esta operación. Dado que esto se ejecutará en un subproceso en segundo plano en un dispositivo integrado, la paralelización es improbable, por lo que tener el bucle de escucha como el bucle externo debería reducir el recuento general de instrucciones (se convierte efectivamente en un bucle sobre M operaciones, en lugar de M bucles sobre una sola operación).

Estoy extendiendo una biblioteca de registro existente. Es un sistema con dos lados: el frontend es donde las tareas escriben sus mensajes de registro, el backend es donde una aplicación puede conectar oyentes a los que reenvía esos mensajes a diferentes receptores. El backend solía ser un oyente cableado, ahora lo estoy extendiendo por flexibilidad. El código se utilizará exclusivamente en dispositivos integrados, donde un alto rendimiento (medido en número de bytes reenviados por milisegundo) es un objetivo de diseño e implementación muy importante.

Por motivos de rendimiento, los mensajes se almacenan en búfer y el reenvío se realiza en una tarea en segundo plano. Esa tarea obtiene una parte de los mensajes de una cola, los formatea a todos y luego los pasa a los oyentes a través de funciones registradas. Esos oyentes filtrarán los mensajes y solo los escribirán en su receptor que pasen el criterio de filtro.

Dado esto, termino teniendo N funciones de notificación (los oyentes) para enviar mensajes M , un problema bastante clásico de N*M Ahora tengo dos posibilidades: puedo repasar los mensajes y luego recorrer las funciones de notificación y pasar el mensaje a cada uno.

for(m in formatted_messages) for(n in notification_functions) n(m); void n(message) { if( filter(message) ) write(message); }

O podría pasar por todas las funciones de notificación y pasarles todos los mensajes que tengo a la vez:

for(n in notification_functions) n(formatted_messages); void n(messages) { for(m in messages) if( filter(m) ) write(m); }

¿Hay alguna consideración fundamental con respecto a qué diseño es más probable que permita que se procese una mayor cantidad de mensajes por intervalo de tiempo? (Observe cómo esta pregunta determina la interfaz del oyente. Esta no es una pregunta de microoptimización, sino sobre cómo hacer un diseño que no impida el rendimiento. Solo puedo medir mucho más tarde, y el rediseño de la interfaz del oyente será costoso .)

Algunas consideraciones que ya he hecho:

  • Esos oyentes necesitan escribir los mensajes en algún lugar, lo que es bastante costoso, por lo que las llamadas de función por sí mismas podrían no ser muy importantes en cuanto al rendimiento.
  • En el 95% de todos los casos, solo habrá un oyente.

El orden de los bucles probablemente tendrá una ventaja mucho menor que el cambio en la firma del oyente (tenga en cuenta que cualquiera que sea el bucle está fuera, el oyente podría mantener la primera interfaz, es decir, ambos bucles pueden estar en el llamador).

La ventaja natural de la segunda interfaz (es decir, el envío de una secuencia de mensajes a cada oyente) es que permite la posible agrupación en la implementación del oyente. Por ejemplo, si escribe en un dispositivo, el oyente puede empaquetar varios mensajes en una sola write , mientras que si la interfaz toma un solo mensaje, entonces el caché del oyente (que tiene un costo de memoria y CPU) o necesita realizar varias writes por llamada.


Entonces, varios factores jugarán aquí:

¿Qué tan juntos están los mensajes en el caché y cuánto espacio ocupan? Si son relativamente pequeños (unos pocos kilobytes, o menos) y están juntos (por ejemplo, no es una lista enlazada con memoria asignada con varios segundos de diferencia en un sistema que realiza muchas otras asignaciones de memoria).

Si están cerca, y son pequeños, entonces creo que la segunda opción es más eficiente, ya que los mensajes se guardarán juntos en caché, donde se llamarán a todos los n y funciones de filtro (también asumiendo que hay MUCHAS funciones, no una, dos o tres) puede causar más "cache-throwout" de mensajes anteriores. Por supuesto, esto también dependerá de la complejidad de las funciones de escucha y filtro. ¿Cuánto trabajo hacen? Si cada función hace un poco de trabajo, probablemente no sea tan importante en qué orden lo hace, porque simplemente será marginal.


No hay ninguna razón "fundamental" por la cual uno es mejor diseño que el otro. Hay algunas diferencias de velocidad muy pequeñas que pueden entrar en juego dependiendo de cómo se utiliza su biblioteca. Personalmente preferiría iterar sobre los oyentes primero y luego los mensajes.

Supongo que los cuerpos de los manejadores suelen ser bastante rápidos. Probablemente querrá recorrer los oyentes como el bucle externo para que esté llamando el mismo código repetidamente. Cosas como la predicción de llamadas indirectas funcionarán mucho mejor de esta manera. Por supuesto, terminas haciendo un uso peor de la caché de datos, pero es de esperar que cada búfer de mensajes sea lo suficientemente pequeño como para caber fácilmente en L1.

¿Por qué no también hacer que los oyentes acepten un const vector<message> & y hacer que hagan su propia iteración? Pueden hacer lo que sea beneficioso para el almacenamiento en búfer y solo hacer una sola escritura costosa al final.