cambios SS 2024-10-31 13:15:21
This commit is contained in:
@@ -1,83 +0,0 @@
|
||||
# <mark style="background: #FF5582A6;">ASM / C</mark>
|
||||
### a) ¿Qué hace el código?
|
||||
Recorre el array `arrayx` (empezando por la posición 1) de tamaño `N_ITER` almacenando en la posición `i - 1` la suma del elemento `i` más el `i + 1` entre la constante `f11`.
|
||||
### b) ¿Cómo se puede escribir en alto nivel?
|
||||
```C
|
||||
#define N_ITER 25
|
||||
|
||||
float arrayx[N_ITER];
|
||||
const float f11 = 21.0;
|
||||
|
||||
for(int i = 1; i < N_ITER; i++)
|
||||
{
|
||||
arrayx[i - 1] = ( arrayx[i] + arrayx[i + 1] ) / f11;
|
||||
}
|
||||
```
|
||||
### c) ¿Son paralelizables directamente?
|
||||
Sí porque no hay dependencias reales.
|
||||
```C
|
||||
#define N_ITER 25
|
||||
|
||||
float arrayx[N_ITER];
|
||||
const float f11 = 21.0;
|
||||
|
||||
for(int i = 5; i < N_ITER; i+=5)
|
||||
{
|
||||
arrayx[i - 1] = ( arrayx[i] + arrayx[i + 1] ) / f11;
|
||||
arrayx[i - 2] = ( arrayx[i + 1] + arrayx[i + 2] ) / f11;
|
||||
arrayx[i - 3] = ( arrayx[i + 2] + arrayx[i + 3] ) / f11;
|
||||
arrayx[i - 4] = ( arrayx[i + 3] + arrayx[i + 4] ) / f11;
|
||||
arrayx[i - 5] = ( arrayx[i + 4] + arrayx[i + 5] ) / f11;
|
||||
}
|
||||
```
|
||||
### d) Como la BTB predice siempre tomado ¿cuándo acierta?
|
||||
La BTB acertará en todas las iteraciones del bucle excepto en la última porque predice salto tomado y no debería tomarlo porque el bucle ha acabado.
|
||||
![[Pasted image 20241022134454.png]]
|
||||
### e) Si la BTB de un GPP es muy sofisticada ¿acertará en los saltos?
|
||||
...
|
||||
# <mark style="background: #D2B3FFA6;">Visual Studio</mark>
|
||||
La función más paralelizable es **`pp4()`** ya que sólo tiene una operación de cada tipo (**`LD, SF, ADDF, SLTI, ADDI`**) y no hay saltos condicionales en el bucle.
|
||||
```C
|
||||
double pp4()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i<N_ITER; i++) {
|
||||
a[i] = a[i] + 8.0;
|
||||
}
|
||||
return a[N_ITER - 1];
|
||||
}
|
||||
```
|
||||
|
||||
La función que más se parece (parecer en términos de mismo tipo de instrucciones) es **`pp2()`**.
|
||||
```C
|
||||
double pp2()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i<N_ITER; i++) {
|
||||
a[i] = 3.14 / sqrt(b[i]+c[i]);
|
||||
}
|
||||
return a[N_ITER - 1];
|
||||
}
|
||||
```
|
||||
|
||||
### <mark style="background: #FFB8EBA6;">Máquina en la que se ejecuta el código real:</mark>
|
||||
|
||||
![[Pasted image 20241022144429.png|400]]
|
||||
|
||||
### <mark style="background: #FFB8EBA6;">Comparativa</mark>
|
||||
|
||||
| `pp2()` | `nuestro` |
|
||||
| -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| double pp2()<br>{<br> int i;<br> for (i = 0; i<N_ITER; i++) {<br> a[i] = 3.14 / sqrt(b[i]+c[i]);<br> }<br> return a[N_ITER - 1];<br>} | # define N_ITER 25<br><br>float arrayx[N_ITER];<br>const float f11 = 21.0;<br><br>for(int i = 1; i < N_ITER; i++)<br>{<br> arrayx[i - 1] = ( arrayx[i] + arrayx[i + 1] ) / f11; <br>} |
|
||||
| Este código tiene:<br>- 2x LDF<br>- 1x STF<br>- 1x ADDF<br>- 1x DIVF<br>- 1x constante | Nuestro código tiene:<br>- 2x LDF<br>- 1x STF<br>- 1x ADDF<br>- 1x DIVF<br>- 1x constante |
|
||||
| | |
|
||||
### <mark style="background: #FFB8EBA6;">Duración de CPI en los bucles</mark>
|
||||
|
||||
| Release | Debug |
|
||||
| ------------------------------------ | ------------------------------------ |
|
||||
| ![[Pasted image 20241022144832.png]] | ![[Pasted image 20241022150214.png]] |
|
||||
|
||||
(Todo para config1 y config2)
|
||||
5. GFLOPS (o MIPS)
|
||||
6. CPI (pp2 pp4) y Ac
|
||||
7. Cronograma
|
||||
@@ -226,21 +226,74 @@ En C/C++ parece fácil: usándolo. La mayoría de SO están escritos en C así q
|
||||
- Funciones terminadas en W (`MessageBoxW`)
|
||||
### <mark style="background: #FFB86CA6;">Tratamiento de errores</mark>
|
||||
Las funciones devuelven un entero, y si este es negativo es que ha terminado con error. Para más información sobre el error se usa `GetLastError()` y para mensajes al usuario `FormatMessage()`.
|
||||
# <mark style="background: #FFF3A3A6;">TEMA 5: Control de ejecución: procesos e hilos</mark>
|
||||
En Windows todos los procesos tienen al menos el "main thread". El programa termina cuando termina el hilo principal. Dos modelos básicos:
|
||||
- **Multihilo:** cada proceso tiene múltiples hilos. Típico de Windows aunque se usa cada vez más en UNIX/Linux.
|
||||
# <mark style="background: #FFF3A3A6;">TEMA 5: Control de ejecución: procesos e threads</mark>
|
||||
En Windows todos los procesos tienen al menos el "main thread". El programa termina cuando termina el thread principal. Dos modelos básicos:
|
||||
- **Multithread:** cada proceso tiene múltiples threads. Típico de Windows aunque se usa cada vez más en UNIX/Linux.
|
||||
- **Multiproceso:** múltiples procesos. Típico de UNIX/Linux, aunque también se puede en Windows. En Linux es **muy fácil** usando `fork()`.
|
||||
## <mark style="background: #ADCCFFA6;">1. Operaciones básicas con hilos</mark>
|
||||
Organizar la ejecución en varios hilos **¿para qué?**
|
||||
## <mark style="background: #ADCCFFA6;">1. Operaciones básicas con threads</mark>
|
||||
Organizar la ejecución en varios threads **¿para qué?**
|
||||
EJEMPLO: Programa de proceso de audio
|
||||
- **Hilo 1:** lee datos de la entrada de audio, los procesa y envía el resultado a la salida.
|
||||
- **thread 1:** lee datos de la entrada de audio, los procesa y envía el resultado a la salida.
|
||||
- Periódico
|
||||
- Intensivo en datos
|
||||
- Requiere velocidad de proceso
|
||||
- **WORKER THREADS**
|
||||
- **Hilo 2:** controla la UI (subir, bajar volumen, play, pause...) Complicado si alguna tarea es bloqueante.
|
||||
- **thread 2:** controla la UI (subir, bajar volumen, play, pause...) Complicado si alguna tarea es bloqueante.
|
||||
- No periódico
|
||||
- No intensivo
|
||||
- No requiere velocidad de proceso
|
||||
- **USER INTERFACE THREAD**
|
||||
-
|
||||
### <mark style="background: #FFB86CA6;">Crear un thread</mark>
|
||||
En Windows se usa `CreateThread()` que devuelve un `HANDLE`. Los elementos que integran el thread son:
|
||||
- Función de entrada
|
||||
- Parámetro(s)
|
||||
- Pila
|
||||
- Almacenamiento local al thread
|
||||
- El HANDLE y el ID
|
||||
Los threads son **asíncronos**.
|
||||
#### <mark style="background: #D2B3FFA6;">Ejemplo</mark>
|
||||
```C
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
setlocale(LC_ALL, "Spanish");
|
||||
|
||||
DWORD Ciclo = 1000; // Ciclo entre pitidos (en ms)
|
||||
DWORD Frecuencia = 500;
|
||||
DWORD Duracion = 100;
|
||||
|
||||
while (TRUE){
|
||||
// bip a frec. y duración que se le pasa
|
||||
Beep(Frecuencia, Duracion);
|
||||
Sleep(Ciclo);
|
||||
}
|
||||
|
||||
printf("\nPulse tecla RETORNO para terminar\n");
|
||||
getchar(); // bloqueo
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Para evitar el bloqueo, en C++ existen alternativas (en C **NO**), como `std::thread` o `std::async`.
|
||||
### <mark style="background: #FFB86CA6;">Suspender/Reactivar un thread</mark>
|
||||
El thread se puede suspender o reactivar usando respectivamente `SuspendThread` y `ResumeThread`. El thread está suspendido si el contador != 0.
|
||||
### <mark style="background: #FFB86CA6;">Terminar un thread</mark>
|
||||
Un thread finaliza cuando termina su función de entrada, cuando se termina a sí mismo con `ExitThread` o cuando lo "terminan" con `TerminateThread`.
|
||||
## <mark style="background: #ADCCFFA6;">2. Organización de los datos en un thread</mark>
|
||||
Los hilos comparten variables globales y código. También pueden pasarse punteros (posibilidad de errores).
|
||||
### <mark style="background: #FFB86CA6;">Código reentrante</mark>
|
||||
Código que puede ser usado desde más de un hilo. Se puede aplicar a:
|
||||
- Código completo del hilo
|
||||
- Algunas funciones
|
||||
- Bibliotecas
|
||||
Cada instancia de un thread tiene sus propios recursos, los cuales hay que proteger con mecanismos de control de la concurrencia.
|
||||
## <mark style="background: #ADCCFFA6;">3. Creando procesos</mark>
|
||||
`CreateProcess` crea un proceso concurrente. Algunos puntos a destacar en la descripción de `CreateProcess`:
|
||||
- **`IpApplicationName`**: es opcional. Si es `NULL`, el nombre del programa se incluye en el siguiente parámetro.
|
||||
- **`IpCommandLine`**: si el path del ejecutable no está, se sigue la secuencia de búsqueda que viene al final de este parámetro.
|
||||
- **`IpProcessAttributes, IpThreadAttributes, bInheritHandles, IpEnvironment, IpCurrentDirectory`**: <strong style="color:red;">no los usaremos</strong>, poner a NULL/FALSE.
|
||||
- **`IpStartupInfo`**: la estructura `STARTUPINFO` <strong style="color:red;">no la usaremos</strong> pero tiene que estar.
|
||||
- **`IpProcessInformation`**: rellena la estructura `PROCESS_INFORMATION` con información sobre el proceso y thread creado (handles, process/threads ids).
|
||||
## <mark style="background: #ADCCFFA6;">4. Punto de vista de la programación y procesos POSIX</mark>
|
||||
- Aunque el proceso creado se le llama "hijo" **no hay relación lógica** entre ambos, al menos en Windows. En UNIX/Linux/POSIX **sí hay**.
|
||||
- La creación del proceso es asíncrona, **NO** es una "llamada".
|
||||
- En la biblioteca de C: `exec` (UNIX) / `_exec` (Win) funcionan distinto en Windows y UNIX/Linux. En Windows se basa en `CreateProcess` y en UNIX/Linux el proceso actual carga un nuevo programa:
|
||||
- `posix spawn` es lo equivalente a `CreateProcess` que básicamente hace: `fork` + `exec`
|
||||
Reference in New Issue
Block a user