Programación Concurrente y de Tiempo Real Grado en Ingenierı́a Informática Examen Final Teórico de la Asignatura Enero de 2015 1. Apellidos: Nombre: D.N.I.: Grupo (A ó B): Notas 1. Escriba su nombre, apellidos, D.N.I. y grupo en el espacio habilitado para ello, y en todos los folios blancos que utilice. Firme el documento en la esquina superior derecha de la primera página. 2. Dispone de diez minutos para leer los enunciados y formular preguntas o aclaraciones sobre ellos. Transcurrido ese tiempo, no se contestarán preguntas. Dispone de 90 minutos para completar el ejercicio. 3. No complete el documento a lápiz. Utilice bolı́grafo o rotulador. Escriba con letra clara y legible. No podemos corregir lo que no se no puede leer. 4. Utilice los folios blancos que se le proporcionan para resolver los enunciados, pero traslade a este documento únicamente la solución final que obtenga, utilizando el espacio especı́ficamente habilitado para ello, sin sobrepasarlo en ningún caso, y sin proporcionar información o respuestas no pedidas. Entregue tanto el enunciado como los folios blancos. Únicamente se corregirá este documento. 2. Criterios de Corrección 1. El examen se calificará de cero a diez puntos, y ponderará en la calificación final al 40 % bajo los supuestos recogidos en la ficha de la asignatura. 2. Cada enunciado incluye información de la puntuación que su resolución correcta representa, incluida entre corchetes. 3. Un enunciado (cuestión teórica o problema) se considera correcto si la solución dada es correcta completamente. En cualquier otro caso se considera incorrecto y no puntúa. 1 4. Un enunciado de múltiples apartados (cuestión teórica o problema) es correcto si y solo si todos los apartados que lo forman se contestan correctamente. En cualquier otro caso se considera incorrecto y no puntúa. 3. Cuestiones de Desarrollo Corto Conteste a las preguntas que se le formulan en el espacio habilitado para ello. Deberá razonar o justificar su respuesta siempre que se le indique. La ausencia del razonamiento o de la justificación invalidarán la respuesta al no ser esta completa. 1. Considere una arquitectura Java-RMI que utiliza callback de cliente; ¿Serı́a necesaria la activación del DNS rmiregistry en el lado del cliente para hacerla completamente operativa? ¿Por qué? [0.5 puntos]: 2. Escriba una comparativa en formato tabular entre las especificaciones de Java estándar y Java de tiempo real: [0.5 puntos] 2 3. Considere el siguiente programa en Java que hace uso de cerrojos synchronized. ¿Qué caracterı́stica de este tipo de cerrojos evita que el código se bloquee? ¿En qué consiste esa caracterı́stica? [1 punto] import java.util.Scanner; public class cumbresTenebrosas extends Thread{ public static int v = 0; public static int n = 1000; public cumbresTenebrosas(){this.start();} public void Heatcliff(){ if(n>0){ n--; synchronized(this){v--;} this.Heatcliff(); } } public void run(){ this.Heatcliff(); } public static void main(String[]args){ cumbresTenebrosas q = new cumbresTenebrosas(); try{q.join();}catch(InterruptedException e){} System.out.println(v); } } Escriba aquı́ sus respuestas, justificándolas en ambos casos: 3 4. Considerando monitores teóricos tipo Hoare, ¿Qué diferencias hay entre las semánticas de señalización SC y SX? ¿Qué elección a nivel de estructuras de control de flujo debe tomar el programador según una u otra? Escriba a continuación su respuesta razonada: [0.5 puntos] 5. Considere el siguiente programa que utiliza C-CUDA sobre una GPU nVidia: [1.5 puntos] __global__ void vecAdd(double *a, double *b, double *c, int n){ int id = blockIdx.x*blockDim.x+threadIdx.x; if (id < n) c[id] = a[id] * b[id];} int main( int argc, char* argv[] ){ int n = 100000; double *h_a; double *h_b; double *h_c; double *d_a; double *d_b; double *d_c; size_t bytes = n*sizeof(double); h_a = (double*)malloc(bytes); h_b = (double*)malloc(bytes); h_c = (double*)malloc(bytes); cudaMalloc(&d_a, bytes); cudaMalloc(&d_b, bytes); cudaMalloc(&d_c, bytes); int i; for( i = 0; i < n; i++ ) { h_a[i] = sin(i)*sin(i); h_b[i] = cos(i)*cos(i); } cudaMemcpy( d_a, h_a, bytes, cudaMemcpyHostToDevice); cudaMemcpy( d_b, h_b, bytes, cudaMemcpyHostToDevice); int blockSize, gridSize; blockSize = 1024; gridSize = (int)ceil((float)n/blockSize); vecAdd<<<gridSize, blockSize>>>(d_a, d_b, d_c, n); cudaMemcpy( h_c, d_c, bytes, cudaMemcpyDeviceToHost ); double sum = 0; for(i=0; i<n; i++) 4 sum += h_c[i]; printf("final result: %f\n", sum/n); cudaFree(d_a); cudaFree(d_b); free(h_a); free(h_b); free(h_c); } Conteste a las siguientes preguntas, justificando siempre su respuesta: a) Indique qué segmento de código define el kernel, y qué se hace en él paralelamente en los cores de la GPU. b) Indique con qué segmento de código se transfieren los datos a la GPU y qué datos concretos se transmiten. c) Indique qué segmento de código lanza la ejecución del kernel sobre los datos en los núcleos CUDA. 5 6. Considere el siguiente programa escrito en Clojure. Indique la salida -si la hayque produce y el comportamiento que tiene. Justifique su respuesta. [1 punto] (defn inc [valor n] (dosync (let [vactual @valor] (alter valor + n)))) (defn dec [valor n] (dosync (let [vactual @valor] (alter valor - n)))) (def vinic (ref 0)) (future (future (future (future (inc (dec (dec (dec vinic vinic vinic vinic 2000000)) 1000000)) 1000000)) 10)) (. Thread sleep 1000) (println "Valor final: " @vinic) Indique aquı́ la salida y el comportamiento, justificando ambos: 7. Se ha determinado que el coeficiente de bloqueo óptimo para una aplicación paralela es de 0,85. Consteste, razonando su respuesta, las sigiente cuestiones: [1 punto] ¿Qué significado tiene ese coeficiente de bloqueo? 6 En una máquina de 16 cores, ¿cuántas hebras en ejecución concurrente habrá, a partir del coeficiente de bloqueo indicado? Dibuje una curva con el comportamiento teórico esperado en tiempo de respuesta de la aplicación, como una función del coeficiente de bloqueo, sabiendo que para el valor óptimo de 0,85, la aplicación tardó 10 milisegundos. Realice una interpretación de la curva de forma justificada: 8. Considere el siguiente programa escrito en C++11. Indique la salida -si la hay- que produce y el comportamiento que tiene. Justifique su respuesta. [1 punto] 7 #include<iostream> #include <thread> int x = 0; int nVueltas = 1000000; void hola(){ std::cout <<"Hola Mundo..."; for(int i=0; i<nVueltas; i++)x++; } void adios(){ std::cout <<"Spock dice: larga vida y prosperidad..."; for(int i=0; i<nVueltas; i++)int x=2*x; } int main(){ std::thread h(hola); std::thread i(hola); std::thread j(adios); h.join(); i.join(); j.join(); std::cout << x; } Indique aquı́ la salida y el comportamiento del programa anterior, justificando ambos: 4. Problemas 1. Considere el conjunto formado por las instrucciones siguientes: a:=x+y; b:=z-1; 8 c:=a-b w:=c+1; Escriba los conjuntos de lectura y escritura de todas ellas, y utilı́celos para determinar cuáles se pueden ejecutar concurrentemente entre sı́. Proporcione las relaciones entres instrucciones en formato tabular:[1 punto] 9 2. El API de concurrencia estándar de Java no dispone semáforos binarios. Escriba una clase llamada semaf que los proporcione utilizando las primitivas de control de la concurrencia que dicho API estándar proporciona.Escriba ahora, utilizando herencia por extensión de la clase anterior, el código de una clase que proporcione semáforos de conteo general. Llámela gsemaf. [2 puntos] 10