Continuando en la línea de la respuesta de @ Mike_Dunlavey:
En primer lugar, obtener un perfil basado en el tiempo, el uso de su herramienta favorita: VTune o PTU o OProf.
Luego, obtenga un perfil de falta de caché. La caché L1 falla, o la caché L2 falla, o ...
I.e. el primer perfil asocia un "tiempo pasado" con cada contador de programa. El segundo asocia un valor de "número de fallas de caché" con cada contador de programa.
Nota: A menudo "reduzco" los datos, resumiéndolos por función o (si tengo la tecnología) por bucle. O en contenedores de, digamos, 64 bytes. La comparación de los contadores de programa individuales a menudo no es útil, porque los contadores de rendimiento son confusos: el lugar donde ve que se informa una falta de caché suele ser varias instrucciones diferentes de las que realmente sucedieron.
Bien, ahora grafica estos dos perfiles para compararlos. Aquí hay algunos gráficos que me parecen útiles:
Gráficos "Iceberg": el eje X es PC, el eje Y positivo es el tiempo, el acceso Y negativo es el caché falla. Busca lugares que suban y bajen.
(Las gráficas "Intercaladas" también son útiles: la misma idea, eje X es PC, traza el eje Y de la falta de tiempo y caché, pero con líneas verticales estrechas de diferentes colores, típicamente rojo y azul. tanto las fallas en el tiempo como en el caché tendrán líneas rojas y azules finamente intercaladas, casi púrpuras, lo que se extiende a los errores de caché L2 y L3, todos en el mismo gráfico. Por cierto, es probable que desee "normalizar" los números, ya sea para % edad de tiempo o de caché total de accidentes, o, mejor aún, la edad% del punto de datos máxima de tiempo o de caché falla. Si tienes la escala incorrecta, no verá nada.)
gráficos XY : para cada contenedor de muestreo (PC, o función, o bucle, o ...) trace un punto cuyo La coordenada X es la hora normalizada, y cuya coordenada Y es la caché normalizada pierde. Si obtienes muchos puntos de datos en la esquina superior derecha, un gran% de tiempo de edad Y un gran% de caché de edad falla, eso es una evidencia interesante. O bien, olvide el número de puntos: si la suma de todos los porcentajes en la esquina superior es grande ...
Tenga en cuenta que, por desgracia, a menudo tiene que realizar estos análisis usted mismo. La última vez que revisé, VTune no lo hace por ti. He usado gnuplot y Excel. (Advertencia: Excel muere por encima de 64 mil puntos de datos.)
Más consejos:
Si se colocarán en línea su puntero inteligente, es posible obtener la cuenta de todo el lugar. En un mundo ideal, usted podría rastrear sus PC a la línea original del código fuente. En este caso, es posible que desee diferir un poco la reducción: observe todas las PC individuales; volver a asignarlos a líneas de código fuente; y luego mapea esos en la función original. Muchos compiladores, p. GCC, tiene opciones de tabla de símbolos que le permiten hacer esto.
Por cierto, sospecho que su problema NO es con el puntero inteligente que causa la caché. A menos que esté haciendo smart_ptr < int> en todas partes. Si está haciendo smart_ptr < Obj>, y sizeof (Obj) + es mayor que decir, 4 * sizeof (Obj *) (y si el smart_ptr en sí no es enorme), entonces no es mucho.
Es más probable que el puntero inteligente sea el nivel extra de indirección que está causando el problema.
Casualmente, estaba hablando con un tipo en el almuerzo que tenía un puntero inteligente de referencia contado que estaba usando un mango, es decir.un nivel de indirección, algo así como
template<typename T> class refcntptr {
refcnt_handle<T> handle;
public:
refcntptr(T*obj) {
this->handle = new refcnt_handle<T>();
this->handle->ptr = obj;
this->handle->count = 1;
}
};
template<typename T> class refcnt_handle {
T* ptr;
int count;
friend refcnt_ptr<T>;
};
(no me codificar de esta manera, pero sirve para la exposición.)
La indirecto doble this-> manejar-> ptr puede ser una gran problema de rendimiento O incluso un triple indirecto, this-> handle-> ptr-> campo. Por lo menos, en una máquina con 5 cachés L1 de ciclo, cada uno de estos campos-> handle-> ptr-> tomaría 10 ciclos. Y será mucho más difícil de superponer que una persecución de un solo puntero. Pero, lo que es peor, si cada uno de ellos es un caché L1, incluso si hubiera tan solo 20 ciclos para el L2 ... bueno, es mucho más difícil ocultar 2 * 20 = 40 ciclos de latencia de falta de memoria caché, que una sola falla L1.
En general, es un buen consejo para evitar los niveles de indirección en los punteros inteligentes. En lugar de señalar un identificador, al que apuntan todos los punteros inteligentes, que apunta hacia el objeto, puede hacer que el puntero inteligente sea más grande al hacer que apunte tanto al objeto como al controlador. (Que luego ya no es lo que comúnmente se llama un identificador, sino que se parece más a un objeto de información.)
E.g.
template<typename T> class refcntptr {
refcnt_info<T> info;
T* ptr;
public:
refcntptr(T*obj) {
this->ptr = obj;
this->info = new refcnt_handle<T>();
this->info->count = 1;
}
};
template<typename T> class refcnt_info {
T* ptr; // perhaps not necessary, but useful.
int count;
friend refcnt_ptr<T>;
};
Anyway - a time profile is your best friend.
Oh, sí - hardware de Intel EMON también le puede decir cuántos ciclos esperaba en un PC. Eso puede distinguir una gran cantidad de fallas L1 de un pequeño número de fallas L2.
De hecho, no es posible utilizar un 'weak_ptr' con un contador intrusiva como un contador autorizado se destruyó con el objeto ... y por lo que el' weak_ptr' no tiene manera de comprobar si el objeto o no es válido sin acceder realmente a él. –
@ Matthieu: Si se sabe que el gráfico de dependencia es un ciclo único, creo que puede usar el enlace de cada objeto (debe haber solo uno) como indicador de validez. Para el propósito de la destrucción de todos modos. Atravesar un gráfico aleatorio requeriría almacenamiento local de subprocesos, pero eso no es imposible. – Potatoswatter