Saltar a contenido

Semana 6: OpenMP

Acceso memoria cache

Aspectos

  • La memoria cache el cuello de botella
  • Importa como accedemos a la memoria, hay que hacerlo de forma secuencial, tener en cuenta que los arrays n dimensionales son siempre 1D solo que hay capa lógica de manejo [i][j] = [i*j + j]
#include <chrono>
#include <cstdio>

using namespace std;

const int N = 10000;
// arreglo bidimensional de N x N
int arr[N][N];

void llenar() {
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      arr[i][j] = 1; // arr[i*j+j] = 1
    }
  }
}

void llenarv2() {
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      arr[j][i] = 1;
    }
  }
}

int main() {
  auto start = chrono::steady_clock::now();
  llenar();
  auto end = chrono::steady_clock::now();
  auto diff = end - start;
  printf("%s : %f\n", "Duración en milisegundos: ",
         chrono::duration<double, milli>(diff).count());

  auto start2 = chrono::steady_clock::now();
  llenarv2();
  auto end2 = chrono::steady_clock::now();
  auto diff2 = end2 - start2;
  printf("%s : %f\n", "Duración en milisegundos: ",
         chrono::duration<double, milli>(diff2).count());
}
Duración en milisegundos:  : 108.878309
Duración en milisegundos:  : 163.521305

OpenMP

Librería

  • Nos ofrece un API para paralelizar
  • Funciona a través de anotaciones #pragma
  • Tiene funciones avanzadas para la gestión memoria, variables, etc
#include <chrono>
#include <cstdio>
#include <omp.h>
#include <vector>

using namespace std;

const int SIZE = 1e9;
vector<int> v(SIZE);

void llenar() {
#pragma omp parallel for
  for (int i = 0; i < SIZE; i++)
    v[i] = 1;
}

void llenarv2() {
#pragma omp parallel for schedule(static, 5000)
  for (int i = 0; i < SIZE; i++)
    v[i] = 1;
}

void critica() {
#pragma omp critical
  for (int i = 1; i < SIZE; ++i) {
    v[i] = v[i - 1] + 1;
  }
}

void sumar(int &res) {
  int sum = 0;
#pragma omp parallel for reduction(+ : sum)
  for (int i = 0; i < SIZE; i++)
    sum += v[i];
  res = sum;
}

int main() {
  auto start = chrono::high_resolution_clock::now();
  llenar();
  auto end = chrono::high_resolution_clock::now();
  // Duración en milisegundos
  auto diff = end - start;

  printf("Tiempo: %f\n", chrono::duration<double, milli>(diff).count());

  auto start2 = chrono::high_resolution_clock::now();
  llenarv2();
  auto end2 = chrono::high_resolution_clock::now();
  // Duración en milisegundos
  auto diff2 = end2 - start2;
  printf("Tiempo dynamic: %f\n",
         chrono::duration<double, milli>(diff2).count());

  int sum = 0;
  sumar(ref(sum));
  printf("Suma: %d\n", sum);

  auto start3 = chrono::high_resolution_clock::now();
  critica();
  auto end3 = chrono::high_resolution_clock::now();
  // Duración en milisegundos
  auto diff3 = end3 - start3;
  printf("Tiempo crítica: %f\n",
         chrono::duration<double, milli>(diff3).count());

  // Imprimir los primeros 20 elementos
  for (int i = SIZE - 20; i < SIZE; ++i)
    printf("%d ", v[i]);
}
compilar:
    g++ -o exe *.cpp -fopenmp
    ./exe
    rm ex

Ejercicio en clase

Ejercicio

Dados dos vectores u y v de tamaño 10000000

  • Llenar con datos constantes
  • Calcular abs(u - v) guardar en un vector w
  • Sumar vector w
#include <cmath>
#include <cstdio>
#include <omp.h>
#include <vector>

using namespace std;

const int SIZE = 10000000;
vector<int> u(SIZE);
vector<int> v(SIZE);
vector<int> w(SIZE);

void llenar() {
#pragma omp parallel for
  for (int i = 0; i < SIZE; i++) {
    u[i] = 1;
    v[i] = 3;
  }
}
void restaabs() {
#pragma omp parallel for
  for (int i = 0; i < SIZE; i++) {
    w[i] = abs(u[i] - v[i]);
  }
}

void sumaw(int &sum) {
#pragma omp parallel for reduction(+ : sum)
  for (int i = 0; i < SIZE; i++) {
    sum += w[i];
  }
}

int main() {
  int sum = 0;
  llenar();
  restaabs();
  sumaw(sum);
  printf("Suma: %d\n", sum);
  return 0;
}Ejercicio

Dados dos vectores u y v de tamaño 10000000

- Llenar con datos constantes
- Calcular abs(u - v) guardar en un vector w
- Sumar vector w