TEL-420 Sistemas Paralelos P2a taller scheduling · M2
M2 Guía de Práctica / Taller
Descargar .md

P2a · Taller de paralelización de bucles con la cláusula schedule

Contenido del programa: §20.2 · contenido 2.4 Horas de laboratorio: 4 · Modalidad: individual


1. Objetivo

Aplicar #pragma omp parallel for con las distintas políticas de la cláusula schedule, medir el comportamiento real de cada una sobre el mismo programa y justificar con los datos cuál conviene.

Al terminar de la práctica el estudiante es capaz de: escribir un bucle paralelo con la cláusula schedule, medir su rendimiento, calcular S_p y E_p, y elegir la política de reparto en función del coste de la iteración y no por costumbre.


2. Requisitos previos

  • Haber leído el apartado 2.4 del contenido del módulo.
  • Tener el entorno de compilación con OpenMP disponible. Verificación rápida:
echo '#include <omp.h>
#include <stdio.h>
int main(void){ printf("hilos disponibles: %d\n", omp_get_max_threads()); return 0; }' > t.c
gcc -fopenmp t.c -o t && OMP_NUM_THREADS=4 ./t

Debe imprimir el número de hilos que haya fijado OMP_NUM_THREADS. Si imprime 1, no hay soporte OpenMP: es el error más frecuente de esta sesión.


3. Material de laboratorio

RecursoDetalle
Sistema operativoUbuntu 20.04 o superior, o Windows con WSL2
CompiladorGCC con -fopenmp, o Clang con -Xpreprocessor -fopenmp
CPUEquipo del laboratorio (Dell OptiPlex, Intel Core i7)
EditorVisual Studio Code con extensión C/C++

4. Desarrollo de la práctica

Paso 1: Programa secuencial de partida

Cree suma_irregular.c, que suma un vector en el que las iteraciones no cuestan lo mismo:

/* Sistemas Paralelos - TEL420
 * Estudiante: NOMBRE COMPLETO
 * RU / CI: XXXXXXXX
 * Practica: P2a - Taller de scheduling con OpenMP
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N 20000000

/* Coste de la iteracion i, irregular pero DETERMINISTA, para que las tres
 * versiones midan exactamente el mismo trabajo. */
static double coste(int i) {
    return (i % 97 == 0) ? 50.0 : 1.0;
}

int main(void) {
    struct timespec t0, t1;
    double suma = 0.0;

    /* Una de cada 97 iteraciones cuesta 50 veces mas: el reparto desequilibrado
     * se nota. Con un coste uniforme, cualquier politica daria lo mismo. */
    clock_gettime(CLOCK_MONOTONIC, &t0);
    for (int i = 0; i < N; i++) suma += coste(i);
    clock_gettime(CLOCK_MONOTONIC, &t1);

    double ts = (t1.tv_sec - t0.tv_sec) + 1e-9 * (t1.tv_nsec - t0.tv_nsec);
    printf("secuencial      : %.4f s   suma = %.1f\n", ts, suma);
    return 0;
}

Compile y ejecute para tener el valor de referencia T_1:

gcc -O2 suma_irregular.c -o suma_irregular
./suma_irregular

Paso 2: La versión paralela

/* Sistema: Paralelos - TEL420
 * Estudiante: NOMBRE COMPLETO
 * RU / CI: XXXXXXXX
 */
#include <stdio.h>
#include <time.h>
#include <omp.h>

#define N 20000000
static double coste(int i) { return (i % 97 == 0) ? 50.0 : 1.0; }

int main(void) {
    struct timespec t0, t1;
    double suma = 0.0;

    #pragma omp parallel for reduction(+:suma) schedule(static)
    for (int i = 0; i < N; i++) suma += coste(i);

    clock_gettime(CLOCK_MONOTONIC, &t0);
    #pragma omp parallel for reduction(+:suma) schedule(static)
    for (int i = 0; i < N; i++) suma += coste(i);
    clock_gettime(CLOCK_MONOTONIC, &t1);

    double tp = (t1.tv_sec - t0.tv_sec) + 1e-9 * (t1.tv_nsec - t0.tv_nsec);
    printf("paralelo static : %.4f s   suma = %.1f\n", tp, suma);
    return 0;
}

Fije el reloj con omp_get_wtime(), que mide tiempo de reloj y no tiempo de CPU. Mida siempre después de una vuelta de calentamiento: la primera ejecución paga la carga de la biblioteca y la paginación de memoria, y ese coste falsearía la medición.

Paso 3: Recorrido por las políticas

Cambie la cláusula schedule y mida con 1, 2, 4, 8 y 16 hilos:

VarianteCláusula a probar
Aschedule(static)
Bschedule(static, 1000)
Cschedule(dynamic)
Dschedule(dynamic, 1000)
Eschedule(guided)
Fschedule(guided, 500)
for h in 1 2 4 8 16; do
  echo "=== $h hilos ==="
  OMP_NUM_THREADS=$h ./suma_irregular
done

Paso 4: Sentido de identidad automático

Con schedule(static, chunk) el reparto es round-robin. Compruébelo:

/* Sistema: Paralelos - TEL420
 * Estudiante: NOMBRE COMPLETO
 * Muestra que hilo procesa cada bloque de iteraciones */
#pragma omp parallel for schedule(static, 4)
for (int i = 0; i < 20; i++) {
    #pragma omp critical
    printf("iteracion %2d la tomo el hilo %d\n", i, omp_get_thread_num());
}

Repita cambiando static por dynamic y observe la diferencia en el patrón de asignación.


5. Evidencias requeridas

N°EvidenciaCómo se toma
1Salida de omp_get_max_threads() con 8 hilos fijadosTerminal
2`T_1 de la versión secuencialTerminal
3Tabla completa de tiempos para las 6 variantes y 5 números de hilosHoja de cálculo o tabla del informe
4Salida del programa de identidad paraleloTerminal
5Diferencia de top -H con el programa en ejecuciónTerminal, captura

Personalización obligatoria. Todas las capturas deben mostrar el nombre completo y el RU/CI, según 00-marco/glosario-y-convenciones.md, apartado 4.2. Una captura sin marca se considera no entregada.


6. Estructura del informe

Formato PDF, máximo 3 páginas, con esta estructura:

SecciónContenido
1. PortadaDatos institucionales, materia, título, nombre completo, RU, fecha
2. MarcoQué se midió, con qué equipo y qué versión de compilador
3. ResultadosTabla de T_1, T_p, S_p y E_p para cada variante
4. AnálisisGráfico de S_p frente al número de hilos, una línea por política
5. ConclusionesQué política conviene para un coste uniforme y para un coste irregular, con justificación

7. Criterios de evaluación

CriterioDescripciónPuntaje
FuncionamientoEl programa compila y la suma coincide con la versión secuencial20
Metodología de mediciónUsa omp_get_wtime(), calentamiento previo y repite las mediciones25
Tabla de resultadosCompleta: 6 variantes × 5 números de hilos, con S_p y E_p calculados20
AnálisisLas conclusiones se desprenden de los datos, no de intuiciones20
AutenticidadLas 5 evidencias llevan la marca de personalización15
Total100

Rúbrica completa: ../rubrica-pruebas-rendimiento.md.


8. Preguntas de análisis

  1. Explique por qué schedule(static) y schedule(dynamic) dan resultados tan distintos con coste irregular, y por qué con coste uniforme la diferencia es mucho menor.
  2. La `E_p` de la mejor configuración, ¿supera el 100 %? Si es así, ¿es un error de medición?
  3. Si el chunk de static fuera mayor que N/p, ¿qué pasaría?
  4. ¿En qué situación sería schedule(runtime) mejor que las demás opciones?

Práctica derivada de 00-marco/programa-docente-TEL420-V2.docx, apartado 20.2, contenido 2.4.