Valgrind

Esta herramienta nos permite evaluar programas en C++, sin embargo agrega overhead, nos permite analizar lo que sucede en la memoria cache

D1 misses: Numero de fallos cache L1 C1 miss rate: Porcentaje de fallo cache L1 LLd miss rate: Suma de acceso a memoria + fallos cache L1

Esto nos permite analizar si nuestras funciones tienen mal manejo de memoria

Invocar a la tool

 valgrind --tool=callgrind --dump-in
str=yes  --simulate-cache=yes --collect-jumps=yes .
/programa

Esto nos permite analizar el programa y va generar un archivo .out.pid que nos permite mediante programas Kcachegrind analizar las trazas y detectar donde hay problemas

Archivo bueno.cpp

#include <cstdio>

int SIZE = 10000;

long sumaMatriz(int **matriz) {
  long suma = 0;
  for (int i = 0; i < SIZE; i++) {
    for (int j = 0; j < SIZE; j++) {
      suma += matriz[i][j]; 
    }
  }
  return suma;
}

int main(int argc, char *argv[]) {
  int **arr = new int *[SIZE];
  for (int i = 0; i < SIZE; i++) {
    arr[i] = new int[SIZE];
    for (int j = 0; j < SIZE; j++) {
      arr[i][j] = i + j;
    }
  }
  long res = sumaMatriz(arr);
  printf("%ld\n", res);
  return 0;
}

Aqui se suman las columnas por filas y luego columnas (forma correcta)

malo.cpp

Va hacer la suma por columnas y luego por filas (patron incorrecto)

#include <cstdio>

int SIZE = 10000;

long sumaMatriz(int **matriz) {
  long suma = 0;
  for (int i = 0; i < SIZE; i++) {
    for (int j = 0; j < SIZE; j++) {
      suma += matriz[j][i]; // Suma por columnas
    }
  }
  return suma;
}

int main(int argc, char *argv[]) {
  int **arr = new int *[SIZE];
  for (int i = 0; i < SIZE; i++) {
    arr[i] = new int[SIZE];
    for (int j = 0; j < SIZE; j++) {
      arr[i][j] = i + j;
    }
  }
  long res = sumaMatriz(arr);
  printf("%ld\n", res);
  return 0;
}

Debemos compilar con la opción -g para habilitar debug y así el instrumentador pueda analizar el código

g++ -g -o bueno Bueno.cpp
g++ -g -o malo Malo.cpp

Y posteriormente hacer instrumentación

 valgrind --tool=callgrind --d
ump-instr=yes  --simulate-cache=yes --collect-jumps=yes ./bueno

 valgrind --tool=callgrind --d
ump-instr=yes  --simulate-cache=yes --collect-jumps=yes ./malo

Va a generar dos archivos out

 ls *.out*             
callgrind.out.45187  callgrind.out.46257

Estos dos archivos los podemos cargar con la herramienta Kcachegrind

Para el caso de Bueno.cpp

Aqui el L1 DataWrite Miss es 0%

Para el caso de Malo.cpp

El L1 DataWrite Miss es del 99.06% clara evidencia de sobreescritura de memoria cache

Observar que la Write es el factor que indica que hay mal uso de memoria