2009-10-12 23 views
7

He estado utilizando punteros compartidos para el tiempo soem ahora, y tengo problemas de rendimiento en mi programa ... Así que me gustaría saber si los punteros compartidos conducen a la disminución del rendimiento. Si es así, ¿qué tan difícil? Muchas gracias.Punteros compartidos y el rendimiento

Mi programa es multi-hilo, utilizando std :: TR1 :: shared_ptr

+6

Heh. No hay respuesta aquí, pero me encantó la forma en que se redactó el título. Me imagino a la CPU ejecutando alegremente instrucciones cuando de repente ve una instrucción de puntero compartido entrar en la parte posterior de la tubería. "Oh no, esto va a huuuuurt ...... ¡OW!" –

+0

¿Qué te hace pensar que el problema son los punteros compartidos? –

+0

Tener gente diciendo que pierden tiempo de CPU, eso es todo: P – Guest

Respuesta

4

punteros compartidos son contados referencia. Particularmente cuando está usando multi-threading, incrementando y disminuyendo el recuento de referencias puede tomar una cantidad de tiempo significativa. La razón por la cual el multihilo duele aquí es que si pasaste un puntero compartido entre hilos, el recuento de referencias terminaría compartido entre esos hilos, por lo que cualquier manipulación debe sincronizarse entre los hilos. Eso puede ralentizar bastante las cosas.

Editar: Para aquellos que se preocupan de cuánto enredo de hilos más lento puede hacer algunas operaciones bastante simples, ver las pruebas de Herb Sutter con algunas implementaciones de CoW Strings. Si bien sus pruebas distan mucho de ser perfectas (por ejemplo, solo probó en Windows), aún le da una idea del tipo de desaceleración que puede esperar. Para la mayoría de los propósitos prácticos, puede/podría pensar en una cadena de CoW como algo así como shared_ptr<charT>, con muchas funciones miembro (irrelevantes) agregadas.

+0

Esto solo es válido si usa un mecanismo de recuento de referencias que tenga en cuenta el hilo. No parece que este sea el caso de acuerdo con la documentación http://www.boost.org/doc/libs/1_38_0/libs/smart_ptr/shared_ptr.htm#ThreadSafety – JaredPar

+2

No dice qué shared_ptr está usando. Algunos ya conocen el hilo y otros no. Está abierto a cuestionarse si el que está usando es o no. Dicho esto, sin duda tenías razón en que las posibilidades de que esto sea la causa de un problema de rendimiento son bastante remotas. –

+0

Se agregó más información – Guest

10

Es virtualmente imposible responder correctamente esta pregunta dados los datos. La única forma de saber realmente qué está causando un problema de rendimiento en su aplicación es ejecutar un generador de perfiles en el programa y examinar la salida.

Habiendo dicho eso, es muy poco probable que un shared_ptr esté causando la desaceleración. El tipo shared_ptr y muchas de las primeras variantes de crecimiento interno se utilizan en un número cada vez mayor de programas C++. Yo mismo los uso en mi trabajo (profesional en casa). Pasé mucho tiempo perfilando mis aplicaciones de trabajo y shared_ptr nunca ha estado cerca de un problema en mi código ni en ningún otro código que se ejecute dentro de la aplicación. Es mucho más probable que el error esté en otra parte.

3

Muy poco probable, tendría que pasar la mayor parte del tiempo pasando punteros.

El efecto de los ptr compartidos suele ser menor, y es difícil incluso construir un caso límite donde se conviertan en un problema (suponiendo una implementación adecuada y un compilador que optimice correctamente).

Impactos de ptr compartida:

  • aumento del tamaño de la asignación.
    Eso importaría solo si tiene muchos punteros compartidos para objetos muy pequeños (digamos, decenas de millones de shared_ptr<int>) y/o está trabajando cerca de un límite de memoria. Hay una pequeña posibilidad de que una disminución notable en el rendimiento si las asignaciones adicionales superan un nivel de caché/NUMA dentro de un bucle interno

  • aumento numer de las asignaciones
    shared_ptr allcoates un objeto de seguimiento (contador de referencia, el recuento débil y Deleter) . Esto ejerce presión sobre el montón y puede causar una desaceleración general si tiene un número total elevado de asignaciones y desasignaciones.
    se puede evitar mediante el uso de make_shared, poniendo referente y trackng objeto en una única asignación

  • referencia contando
    aumenta el costo de una copia de un puntero. En una aplicación con un único subproceso, notaría que solo usted pasa la mayor parte del tiempo copiando punteros de todos modos. En una aplicación de subprocesos múltiples, aún necesitaría tener una gran contención en los mismos punteros.
    El costo de la copia puede evitarse en muchos lugares al pasar un shared_ptr<T> const & p. Ej. como argumento de la función.

  • dereferencing
    El costo desreferenciar adicional es cero en una versión de lanzamiento de un compilador de optimización. Las compilaciones de depuración a menudo equivalen a llamadas de función y controles NULL adicionales. Aún así, especialmente en las compilaciones de depuración, tendría que pasar la mayor parte del tiempo desmarcando los punteros para que haga la diferencia.


Sin informaiton adicional, no podemos ayudar. Debe describir cuáles son los "problemas de rendimiento" (lentitud general, ciertas operaciones que llevan mucho tiempo, muchos intercambios) y algunas cifras clave: qué hace su aplicación, cuántos punteros inteligentes hay, qué tan a menudo se copian, y qué otras operaciones ejecuta, además de hacer malabares con los indicadores inteligentes.

O aprende a usar el monitor de rendimiento y/o un generador de perfiles para averiguar qué causa las ralentizaciones y si hay cuellos de botella particulares.

5

Si su programa parece tener un problema de rendimiento, es perfectamente natural comenzar a adivinar cuál podría ser el problema, pero si desea hacer una apuesta, es casi 100% probable que sea algo completamente diferente. La creación de perfiles puede encontrar el problema. This is the method I use.

0

Una cosa que puede perjudicar el rendimiento es una excesiva aprobación de shared_ptr como parámetros de función. Una solución para eso sería pasar referencias a shared_ptr. Sin embargo, se trata de micro-optimización, por lo que sólo lo hacen cuando realmente se necesita

edición: Al pensar en esto, hay mejores maneras de optimizar:

  • Cuando excesiva que pasa el puntero, probablemente debería dejar el objeto hace algo en lugar de arrastrarlo.
  • Puede pasar (const) la referencia al objeto en lugar del puntero
  • pasar una referencia al puntero cuando éste necesita ser cambiado
0

No adivine sobre el rendimiento: Profile su código.

12

Si su aplicación está pasando alrededor de 700 bytes de mensajes XML que podrían estar contenidos en 65 bytes de los mensajes de protocolo de Google o 85 bytes de ASN.1, entonces probablemente no va a importar. Pero si está procesando un millón de cosas por segundo, entonces no descartaría el costo de agregar 2 ciclos completos de escritura modificada de lectura (RMW) al pasar un puntero.

Una escritura de modificación de lectura completa es del orden de 50 ns, por lo que dos son 100 ns. Este costo es el costo de un lock-inc y un lock-dec, lo mismo que 2 CAS's. Esta es la mitad de una reserva y publicación de la sección crítica de Windows.Esto se compara con un único impulso de ciclo de máquina (400 segundos PICO en una máquina de 2.5GHz)

Y esto ni siquiera incluye los otros costos para invalidar la línea de caché que contiene realmente el conteo, los efectos del bloqueo de BUS en otros procesadores, etc. etc.

Pasar punteros inteligentes por referencia de referencia casi SIEMPRE es preferible. Si el destinatario no crea un nuevo puntero compartido cuando quiere -garantizar o controlar- la vida del pointee , entonces es un error en el destinatario. Para ir de un lado a otro, pasar el hilo de seguridad de una referencia segura, contar punteros inteligentes por valor solo es pedir golpes de rendimiento.

El uso de punteros contados de referencia simplifica los tiempos de vida sin duda, pero pasar los punteros compartidos por valor para tratar de proteger contra los defectos en el destinatario es una completa y absoluta tontería.

El uso excesivo del conteo de referencias puede convertir rápidamente un programa esbelto que puede procesar mensajes de 1 mm por segundo (mps) en uno gordo que maneja 150k mps en el mismo hardware. De repente, necesitas medio rack de servidores y $ 10000/año en electricidad.

Siempre estará mejor si puede administrar la vida útil de sus objetos sin contar las referencias.

Un ejemplo de una mejora simple es, por ejemplo, si va a desplegar un objeto y conoce la amplitud del incremento (por ejemplo n) incrementado en n en lugar de incrementarlo individualmente en cada despliegue.

BTW cuando la CPU ve un prefijo de bloqueo, realmente dice "Oh no, esto va a doler".

Dicho todo esto, estoy de acuerdo con todos que debe verificar el punto caliente.