1. Del archivo a la petición de bloque
En el tema 40 leímos el superbloque de una imagen con pread. Su desplazamiento dentro del archivo era conocido, pero eso no nos decía cuánto movimiento mecánico provocaba la lectura. El archivo podía estar en un HDD, en un SSD o en una caché que ya contenía todos sus bytes.
Tiempo de respuesta: intervalo entre solicitar una operación y observar su finalización en una capa definida. Tiempo de servicio: tiempo empleado en atenderla, separado de la espera previa en cola según el modelo elegido.
Una llamada no equivale necesariamente a una petición física: puede no generar ninguna, originar varias o aprovechar una lectura anticipada. Este bloque del curso comenzará con un HDD idealizado y después estudiará cómo ordenar solicitudes pendientes.
2. Platos, pistas, sectores y direcciones LBA
Un HDD guarda información sobre superficies magnéticas que giran. El actuador desplaza los cabezales hasta la zona de la pista requerida; después se espera a que pase la región buscada. En la descripción clásica, un cilindro reúne pistas de distintas superficies situadas a igual radio.
El sistema operativo normalmente solicita direcciones LBA, números de bloques lógicos. El firmware traduce esas direcciones a su organización interna. No debemos convertir un LBA en cilindro aplicando una geometría inventada ni suponer que conocemos defectos reasignados, zonas y pistas reales.
| Unidad | Qué representa | Qué no determina por sí sola |
|---|---|---|
| Sector lógico del dispositivo | Unidad direccionable expuesta por la interfaz. | El tamaño del bloque del sistema de archivos. |
| Sector físico | Granularidad física que el dispositivo informa. | La duración de cada petición de la aplicación. |
| Bloque del sistema de archivos | Unidad de su organización y asignación. | Que una lectura de ese tamaño llegue aislada al medio. |
| Petición de E/S | Operación de un tamaño y dirección en una capa concreta. | Una relación uno a uno con read o pread. |
En los próximos temas usaremos números de cilindro como coordenadas didácticas. Ese modelo permite comparar recorridos, pero no reconstruye la geometría de un HDD moderno a partir de sus direcciones LBA.
3. El contrato de un modelo de acceso
Tservicio = Tbúsqueda + Trotación + Ttransferencia + Totros
Trespuesta = Tcola + Tservicio
Ttransferencia_ms = 1000 × bytes / bytes_por_segundoSumaremos fases no solapadas de una petición aislada. La búsqueda incluye el asentamiento del cabezal; «otros» agrupa el coste fijo que decidamos modelar, sin volver a contar la misma fase. Supondremos una tasa sostenida constante y que no hay reintentos, ahorro de energía, fallos ni aciertos de caché.
La latencia rotacional media se aplicará cuando la posición angular al llegar a la pista sea uniforme. No significa que cada acceso espere exactamente media vuelta. Si la petición llega a un HDD detenido, el arranque requiere otro modelo y puede dominar la operación.
Los valores usados serán supuestos didácticos: 7200 RPM, búsqueda media de 8 ms, transferencia de 200 MB/s decimales y otros costes de 0,2 ms. No son una ficha técnica ni una promesa para una marca o dispositivo.
4. Tiempo de búsqueda y asentamiento
La búsqueda mueve el conjunto de cabezales hasta la pista y lo estabiliza para acceder a los datos. Depende de la posición inicial y final, del perfil de aceleración, del control del actuador y de la operación. Un desplazamiento corto y uno de extremo a extremo no cuestan lo mismo.
Reducir la distancia puede reducir este coste, pero tiempo y distancia no son proporcionales en general. Si un ejercicio asigna 0,05 ms por cilindro, está definiendo una aproximación particular; no está describiendo una ley mecánica universal.
En el cálculo de un acceso aleatorio usaremos una búsqueda media suministrada. En un acceso secuencial su coste puede amortizarse sobre muchos bytes, aunque cambiar de pista, superficie o zona y atravesar fragmentos puede agregar trabajo. «Secuencial» no garantiza búsqueda cero para todo el archivo.
5. Revoluciones y latencia rotacional
RPM significa revoluciones por minuto. Dividir 60 segundos por ese número da segundos por vuelta. Al convertir a milisegundos obtenemos:
Tvuelta_ms = 60000 / RPM
Trotación_media_ms = Tvuelta_ms / 2 = 30000 / RPM
7200 RPM → 8,333333 ms por vuelta → 4,166667 ms de espera media
5400 RPM → 11,111111 ms por vuelta → 5,555556 ms de espera mediaCon fase uniforme, la espera está entre cero y casi una vuelta: puede estar pasando el sector justo al llegar o podemos haberlo perdido por poco. La media es la mitad del periodo porque todas las fases se consideran igualmente probables.
La rotación continúa durante la búsqueda. Primero modelamos el tiempo de llegada a la pista y después la espera angular residual. No se suma además otra vuelta completa por el hecho de haber estado moviéndose.
Si el enunciado da una espera concreta de 6 ms, se usa ese valor en lugar de la media. Aumentar RPM reduce el periodo, pero no elimina la búsqueda ni permite calcularla a partir de RPM.
6. Transferencia y conversión de unidades
Con una tasa R en bytes por segundo, transferir B bytes cuesta B/R segundos. Debemos expresar numerador y denominador en unidades compatibles: MB significa 1000000 bytes y MiB significa 1048576 bytes; KiB significa 1024 bytes.
R = 200 × 1000000 = 200000000 bytes/s
T = 4096 / 200000000 s = 0,00002048 s = 0,02048 ms
1 MiB a la misma tasa:
T = 1048576 / 200000000 × 1000 = 5,24288 msLa tasa sostenida del medio, el máximo de la interfaz y el caudal de la aplicación son cantidades diferentes. Un enlace rápido no hace desaparecer los milisegundos de posicionamiento. La tasa del HDD también puede variar por zona; una constante R es una simplificación declarada.
Para no duplicar costes, en este modelo R representa la fase efectiva de transferencia completa que decidimos agrupar. Si se estudian por separado medio, búfer y enlace, debe indicarse qué fases se solapan antes de sumar sus duraciones.
7. Cálculo completo de un acceso
Consideremos una lectura aleatoria de 4 KiB que debe llegar al medio. El dispositivo está girando, la cola inicial es cero y usamos los parámetros definidos en la sección 3.
| Componente | Cálculo | Tiempo |
|---|---|---|
| Búsqueda | Media suministrada. | 8 ms |
| Rotación | 30000 / 7200. | 4,166667 ms |
| Transferencia | 4096 / 200000000 × 1000. | 0,020480 ms |
| Otros | Supuesto fijo. | 0,2 ms |
| Total de servicio | Suma, sin redondear los resultados intermedios. | 12,387147 ms aproximadamente. |
La transferencia ocupa aproximadamente el 0,165 % del servicio. Duplicar solamente su tasa deja casi todo el tiempo intacto. Para una solicitud de 1 MiB, en cambio, el servicio modelado es 17,609547 ms y la transferencia representa una fracción considerablemente mayor.
Si esa lectura de 4 KiB espera 20 ms en cola, su respuesta pasa a 32,387147 ms. El dispositivo sigue necesitando el mismo servicio supuesto; el usuario observa también la espera.
8. Latencia, IOPS y caudal
IOPS mide operaciones por segundo. El caudal mide bytes por segundo. Para operaciones completadas del mismo tamaño B, caudal = IOPS × B. Sin declarar el tamaño, dos cifras de IOPS no constituyen una comparación suficiente.
IOPS ≈ 1000 / Tservicio_ms
Con 12,3871467 ms: aproximadamente 80,73 IOPS
Caudal de bloques de 4096 bytes: aproximadamente 0,331 MB/s
Si agregamos 20 ms externos de espera a cada operación serial:
tasa observada ≈ 1000 / 32,3871467 ≈ 30,88 operaciones/sEl primer recíproco describe nuestro modelo sin cola. Con paralelismo no se obtiene la capacidad del dispositivo invirtiendo sin más la latencia de una petición. Para tamaños variables se suman bytes completados y se divide por el tiempo total.
La media tampoco describe toda la experiencia. Conviene registrar mediana y percentiles como p95 o p99 y declarar cómo se calculan. Un pequeño grupo de solicitudes lentas puede afectar a una aplicación que necesita esperar a todas.
9. Acceso aleatorio, secuencial y agrupación
Comparemos 256 bloques de 4 KiB: en total 1 MiB. En el escenario aleatorio idealizado, cada bloque paga búsqueda, rotación media y otros costes. En el escenario agrupado, todos forman una única transferencia contigua con un único posicionamiento y un único coste fijo.
Fijo = 8 + 4,1666667 + 0,2 = 12,3666667 ms
Aleatorio: 256 × (Fijo + 0,02048) ≈ 3171,109547 ms
Agrupado: Fijo + 256 × 0,02048 ≈ 17,609547 ms
Relación entre tiempos: aproximadamente 180,08La diferencia surge de amortizar el coste fijo. Para una transferencia suficientemente grande, el caudal del modelo se acerca a R porque posicionarse una vez pesa cada vez menos por byte.
No afirmamos que 256 llamadas pequeñas y consecutivas produzcan 256 búsquedas reales. La caché, la anticipación, la fusión de solicitudes y el propio dispositivo pueden agruparlas. Tampoco un archivo contiguo en su espacio lógico tiene necesariamente una sola región física. El simulador contrasta dos supuestos extremos explícitos.
10. Colas y concurrencia
Cuando llegan más solicitudes de las que se pueden completar, aparece espera. Aunque cada servicio sea corto, una petición puede encontrar muchas anteriores. Además, el orden de atención cambia la posición desde la que comienza la búsqueda siguiente.
La profundidad de cola indica cuántas solicitudes permanecen pendientes en la capa observada. Con varias pendientes, el sistema y el dispositivo tienen oportunidades para reordenar y aprovechar paralelismo, pero la latencia individual también puede aumentar.
L = λ × W
L: promedio temporal de solicitudes dentro del sistema
λ: solicitudes completadas por segundo
W: respuesta media en segundos
100 operaciones/s × 0,030 s = 3 solicitudes pendientes de mediaL es un promedio observado, no necesariamente el límite configurado. Esta relación no predice por sí sola la tasa ni significa que duplicar la cola duplique IOPS. Tampoco describe un intervalo en el que la cola crece indefinidamente.
Los algoritmos de los temas 42 y 43 decidirán qué solicitud atender a continuación. Minimizar recorrido será una aproximación útil; la fase de rotación, los plazos y la equidad añaden criterios que ese único número no representa.
11. Cachés, escrituras y SSD
Si los datos están en la caché de páginas, una lectura puede resolverse sin visitar el disco. Si no están, aún puede responder una caché del dispositivo. Un resultado muy rápido no demuestra una búsqueda mecánica muy corta. Los temas 12 y 13 desarrollaron esta distinción.
Una escritura almacenada temporalmente puede terminar para la aplicación antes de persistir en el medio. Comparar esa duración con la de una escritura seguida de fsync mezcla puntos de confirmación distintos. Para durabilidad hay que recuperar las garantías y límites estudiados en los temas 15 y 35.
O_DIRECT reduce la intervención de la caché de páginas para los datos en los entornos que lo soportan; exige respetar alineación y no certifica ausencia de todas las cachés. Un fallo de alineación o soporte se debe informar, no reinterpretar como una medida válida.
Un SSD no tiene búsqueda de cabezal ni espera rotacional. Su latencia procede de electrónica, firmware, acceso a flash, colas y actividades internas, entre otros factores. Asignarle RPM cero en la fórmula 30000/RPM sería una división inválida: necesita otro modelo.
12. Programa en C: calcular el presupuesto temporal
Guarda acceso-disco.c. Recibe RPM, búsqueda en ms, bytes de la solicitud, tasa en MB/s decimales, otros costes en ms y cola en ms. Usa siempre rotación media. Los rangos son límites del ejercicio; acepta punto decimal, no coma. Devuelve 2 para uso o valores inválidos y 0 para un cálculo válido.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
static int numero(const char *s, double min, double max, double *v)
{
char *fin;
if (!*s) return -1;
/* Solo decimal positivo, sin exponentes, espacios ni signos. */
int puntos = 0, digitos = 0;
for (const char *p = s; *p; ++p) {
if (*p == '.') { if (++puntos > 1) return -1; }
else if (*p >= '0' && *p <= '9') ++digitos;
else return -1;
}
if (!digitos) return -1;
errno = 0;
*v = strtod(s, &fin);
return errno || *fin || !isfinite(*v) || *v < min || *v > max ? -1 : 0;
}
int main(int argc, char **argv)
{
double rpm, busca, bytes, tasa, otros, cola;
if (argc != 7 ||
numero(argv[1], 1, 30000, &rpm) ||
numero(argv[2], 0, 1000, &busca) ||
numero(argv[3], 1, 1073741824, &bytes) ||
numero(argv[4], 0.001, 10000, &tasa) ||
numero(argv[5], 0, 1000, &otros) ||
numero(argv[6], 0, 60000, &cola) || bytes != floor(bytes)) {
fprintf(stderr, "Uso: %s RPM busqueda_ms bytes MB_s otros_ms cola_ms\n",
argv[0]);
return 2;
}
double rotacion = 30000.0 / rpm;
double transferencia = bytes / (tasa * 1000000.0) * 1000.0;
double servicio = busca + rotacion + transferencia + otros;
double respuesta = cola + servicio;
printf("ROTACION_MS=%.9f\n", rotacion);
printf("TRANSFERENCIA_MS=%.9f\n", transferencia);
printf("SERVICIO_MS=%.9f\n", servicio);
printf("RESPUESTA_MS=%.9f\n", respuesta);
printf("IOPS_SERIAL_SIN_COLA=%.9f\n", 1000.0 / servicio);
printf("CAUDAL_SERIAL_MB_S=%.9f\n", bytes / servicio / 1000.0);
printf("TRANSFERENCIA_PCT=%.9f\n", 100.0 * transferencia / servicio);
return 0;
}$ cc -std=c11 -O2 -Wall -Wextra -Wpedantic acceso-disco.c -lm -o acceso-disco
$ ./acceso-disco 7200 8 4096 200 0.2 0
ROTACION_MS=4.166666667
TRANSFERENCIA_MS=0.020480000
SERVICIO_MS=12.387146667
RESPUESTA_MS=12.387146667
IOPS_SERIAL_SIN_COLA=80.728841509
CAUDAL_SERIAL_MB_S=0.330665335
TRANSFERENCIA_PCT=0.165332667El programa rechaza NaN, infinito, RPM cero y bytes fraccionarios antes de dividir. Los límites mantienen la aritmética en un rango cómodo de double. No abre archivos ni infiere parámetros a partir del nombre de un dispositivo.
13. Programa en C: medir lecturas de un archivo
Guarda medir-lecturas.c. En Linux lee 4096 bytes por operación, con una sola operación pendiente, de forma secuencial o pseudoaleatoria reproducible. Usa un archivo regular estable de al menos un bloque, hasta 100000 operaciones y el reloj monotónico. Imprime media, p50 y p95 por rango más próximo: posición techo(p × N) en la muestra ordenada.
#define _POSIX_C_SOURCE 200809L
#define _FILE_OFFSET_BITS 64
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
static double intervalo(struct timespec a, struct timespec b)
{
return (double)(b.tv_sec - a.tv_sec) * 1000.0 +
(double)(b.tv_nsec - a.tv_nsec) / 1000000.0;
}
static int comparar(const void *a, const void *b)
{
double x = *(const double *)a, y = *(const double *)b;
return (x > y) - (x < y);
}
static int completo(int fd, unsigned char *b, off_t inicio)
{
size_t usados = 0;
while (usados < 4096) {
ssize_t r = pread(fd, b + usados, 4096 - usados, inicio + (off_t)usados);
if (r > 0) usados += (size_t)r;
else if (r < 0 && errno == EINTR) continue;
else { if (r == 0) errno = EIO; return -1; }
}
return 0;
}
int main(int argc, char **argv)
{
if (argc != 4 || (strcmp(argv[2], "seq") && strcmp(argv[2], "rand"))) {
fprintf(stderr, "Uso: %s archivo seq|rand cantidad\n", argv[0]);
return 2;
}
const char *s = argv[3];
if (!*s || strlen(s) > 6) return 2;
for (const char *p = s; *p; ++p) if (*p < '0' || *p > '9') return 2;
unsigned long n = strtoul(s, NULL, 10);
if (n < 1 || n > 100000) return 2;
int fd = open(argv[1], O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK);
if (fd == -1) { perror("open"); return 1; }
struct stat st;
if (fstat(fd, &st) == -1) { perror("fstat"); close(fd); return 1; }
if (!S_ISREG(st.st_mode) || st.st_size < 4096) {
fprintf(stderr, "Se requiere un archivo regular de al menos 4096 bytes\n");
close(fd); return 1;
}
uint64_t bloques = (uint64_t)(st.st_size / 4096), estado = 41;
double *muestras = malloc(n * sizeof *muestras);
if (!muestras) { perror("malloc"); close(fd); return 1; }
unsigned char buffer[4096];
unsigned long suma = 0;
double total = 0;
struct timespec principio, fin, a, b;
if (clock_gettime(CLOCK_MONOTONIC, &principio)) goto fallo;
for (unsigned long i = 0; i < n; ++i) {
estado = estado * UINT64_C(6364136223846793005) + UINT64_C(1);
uint64_t indice = !strcmp(argv[2], "seq") ? (uint64_t)i % bloques :
(estado >> 16) % bloques;
off_t offset = (off_t)(indice * 4096);
if (clock_gettime(CLOCK_MONOTONIC, &a)) goto fallo;
if (completo(fd, buffer, offset)) goto fallo;
if (clock_gettime(CLOCK_MONOTONIC, &b)) goto fallo;
muestras[i] = intervalo(a, b);
total += muestras[i];
suma += buffer[0];
}
if (clock_gettime(CLOCK_MONOTONIC, &fin)) goto fallo;
double pared = intervalo(principio, fin);
qsort(muestras, n, sizeof *muestras, comparar);
printf("N=%lu MEDIA_MS=%.9f P50_MS=%.9f P95_MS=%.9f\n", n, total / n,
muestras[(n + 1) / 2 - 1], muestras[(95 * n + 99) / 100 - 1]);
printf("PARED_MS=%.9f CAUDAL_MB_S=%.9f SUMA=%lu\n", pared,
pared > 0 ? (double)n * 4096 / pared / 1000 : 0.0, suma);
free(muestras);
if (close(fd)) { perror("close"); return 1; }
return 0;
fallo:
perror("medicion"); free(muestras); close(fd); return 1;
}La media usa las duraciones de las lecturas completas; el caudal usa el tiempo total del bucle, que incluye generación de posiciones y trabajo auxiliar. La suma de un byte por operación hace observable el consumo de datos, pero no es un verificador de integridad. La cola del experimento es una operación; otros procesos pueden generar E/S simultánea.
La secuencia pseudoaleatoria admite repeticiones y tiene sesgo de módulo: sirve para un ensayo reproducible, no para garantizar un muestreo uniforme perfecto. La lectura secuencial vuelve al principio al agotar los bloques completos. Se ignora la fracción final menor que 4096 bytes.
El archivo debe permanecer sin cambios. Se manejan lecturas cortas, EINTR y EOF inesperado. O_NOFOLLOW rechaza el enlace final, no los enlaces de todos los componentes de la ruta. El programa mide llamadas buffered; no separa búsqueda y rotación. clock_gettime(2) documenta el reloj utilizado.
14. Laboratorio: observar sin confundir las capas
En una terminal Bash de Linux, con compilador y Python 3 disponibles, trabaja en un directorio nuevo. Guarda allí los dos fuentes anteriores. Los ejemplos siguientes no llevan el símbolo $ para que puedas copiarlos como un bloque. El archivo ocupa 64 MiB de contenido escrito; no se usa truncate porque un archivo disperso no garantiza lecturas del medio.
mkdir laboratorio41
cd laboratorio41
# Guardar aqui acceso-disco.c y medir-lecturas.c antes de continuar.
cc -std=c11 -O2 -Wall -Wextra -Wpedantic acceso-disco.c -lm -o acceso-disco
cc -std=c11 -O2 -Wall -Wextra -Wpedantic medir-lecturas.c -o medir-lecturas
python3 - <<'PY'
import os
with open('datos41.bin', 'xb') as f:
for _ in range(64):
f.write(os.urandom(1024 * 1024))
f.flush()
os.fsync(f.fileno())
PY
./medir-lecturas datos41.bin seq 10000
./medir-lecturas datos41.bin rand 10000
./medir-lecturas datos41.bin rand 10000
findmnt -T datos41.bin
lsblk -o NAME,TYPE,ROTA,LOG-SEC,PHY-SEC,MODELLa creación ya puede dejar todos los datos en caché: fsync no los expulsa. Es normal obtener latencias mucho menores que los 12,39 ms del modelo. No se exige que la segunda ejecución mejore ni que el orden secuencial gane; observa resultados y explica la capa medida.
findmnt ayuda a identificar el montaje y lsblk el dispositivo que el entorno expone. En una máquina virtual, un volumen remoto o una pila con varias capas, ROTA no prueba la mecánica física subyacente. Registra sistema de archivos, almacenamiento, tamaños, carga, versión de herramientas y si el entorno es virtual.
Variante con fio y lectura directa
Si está disponible fio y el sistema de archivos admite E/S directa, ejecuta dos cargas sobre el archivo existente. Se conserva profundidad uno y se impiden escrituras. Si aparece un error de soporte o alineación, registra el fallo; cambiar a buffered cambia el experimento.
fio --readonly --name=azar41 --filename=datos41.bin --allow_file_create=0 --rw=randread --bs=4k --size=64m --ioengine=psync --iodepth=1 --numjobs=1 --direct=1 --time_based=1 --runtime=10 --group_reporting
fio --readonly --name=secuencia41 --filename=datos41.bin --allow_file_create=0 --rw=read --bs=1m --size=64m --ioengine=psync --iodepth=1 --numjobs=1 --direct=1 --time_based=1 --runtime=10 --group_reportingLa documentación de fio describe estas opciones. Contrasta IOPS, ancho de banda y distribución de latencias. Las cargas cambian patrón y tamaño a la vez para ilustrar escenarios; agrega read de 4k y randread de 1m si quieres separar el efecto de cada variable.
Un archivo de 64 MiB puede caber en cachés del dispositivo, incluso con direct=1. El ensayo muestra la pila disponible y no garantiza accesos aleatorios al plato. Un experimento físico requiere un conjunto adecuado, condiciones controladas y repeticiones, sin convertir su resultado en una medida independiente de búsqueda y rotación.
Observar contadores sin atribuirles una fase mecánica
Como ampliación, toma diferencias de los contadores de lectura de /sys/block/DEV/stat antes y después de una carga, sustituyendo DEV por el nombre realmente identificado. Incluyen actividad de otros procesos. La documentación del kernel sobre estadísticas de bloques define solicitudes completadas, sectores contabilizados en unidades de 512 bytes y tiempo acumulado de lecturas.
El cociente entre incremento de tiempo e incremento de operaciones describe una media en esa capa, si hubo operaciones. No separa búsqueda, rotación y transferencia. Tampoco se compara directamente el contador de sectores con un tamaño de bloque ext4 sin convertir unidades.
15. Verificador de cálculos y casos negativos
Desde laboratorio41 ejecuta el siguiente verificador Python 3. Contrasta resultados numéricos, la suma de componentes, el efecto de agregar cola y el rechazo de entradas fuera del contrato. Después comprueba que el lector no cambió el contenido del archivo y que rechaza argumentos inválidos.
python3 - <<'PY'
from pathlib import Path
import hashlib
import math
import subprocess
def ejecutar(args, estado=0):
r = subprocess.run(args, capture_output=True, text=True)
if r.returncode != estado:
raise SystemExit(f'Estado inesperado: {args}: {r.returncode}: {r.stderr}')
return r.stdout
def calcular(*args):
texto = ejecutar(['./acceso-disco', *map(str, args)])
return dict((k, float(v)) for k, v in
(linea.split('=') for linea in texto.splitlines()))
for rpm, busca, cantidad, tasa, otros, cola in (
(7200, 8, 4096, 200, 0.2, 0),
(5400, 10, 1048576, 150, 0.5, 20),
(15000, 0, 1, 10000, 0, 60000),
):
r = calcular(rpm, busca, cantidad, tasa, otros, cola)
rot = 30000 / rpm
trans = cantidad / (tasa * 1000000) * 1000
servicio = busca + rot + trans + otros
esperado = {'ROTACION_MS': rot, 'TRANSFERENCIA_MS': trans,
'SERVICIO_MS': servicio, 'RESPUESTA_MS': servicio + cola,
'IOPS_SERIAL_SIN_COLA': 1000 / servicio,
'CAUDAL_SERIAL_MB_S': cantidad / servicio / 1000,
'TRANSFERENCIA_PCT': 100 * trans / servicio}
for clave, valor in esperado.items():
if not math.isclose(r[clave], valor, rel_tol=1e-8, abs_tol=1e-8):
raise SystemExit(f'Diferencia en {clave}: {r[clave]} != {valor}')
base = calcular(7200, 8, 4096, 200, 0.2, 0)
con_cola = calcular(7200, 8, 4096, 200, 0.2, 20)
assert base['SERVICIO_MS'] == con_cola['SERVICIO_MS']
assert math.isclose(con_cola['RESPUESTA_MS'] - base['RESPUESTA_MS'], 20)
for valores in (['0', '8', '4096', '200', '0.2', '0'],
['7200', '-1', '4096', '200', '0.2', '0'],
['7200', '8', '1.5', '200', '0.2', '0'],
['7200', '8', '4096', '0', '0.2', '0'],
['nan', '8', '4096', '200', '0.2', '0'],
['7200', '8', '4096', 'inf', '0.2', '0']):
ejecutar(['./acceso-disco', *valores], 2)
ejecutar(['./acceso-disco'], 2)
def sha():
with Path('datos41.bin').open('rb') as f:
return hashlib.file_digest(f, 'sha256').digest()
antes = sha()
for modo in ('seq', 'rand'):
texto = ejecutar(['./medir-lecturas', 'datos41.bin', modo, '100'])
campos = dict(x.split('=') for x in texto.split())
assert campos['N'] == '100'
assert 0 <= float(campos['P50_MS']) <= float(campos['P95_MS'])
assert float(campos['MEDIA_MS']) >= 0
assert sha() == antes
ejecutar(['./medir-lecturas', 'datos41.bin', 'otro', '10'], 2)
ejecutar(['./medir-lecturas', 'datos41.bin', 'seq', '0'], 2)
ejecutar(['./medir-lecturas', '.', 'seq', '1'], 1)
print('OK: formulas, limites, percentiles ordenados y archivo sin cambios')
PYEste bloque usa Python 3.11 o posterior por hashlib.file_digest. No exige que la lectura aleatoria sea más lenta ni toma una latencia baja como prueba de acceso al plato. Las comprobaciones temporales revisan propiedades de la salida, no una especificación de velocidad del equipo.
16. Simuladores de acceso y agrupación
Construir un presupuesto de tiempo
Se aplica el mismo modelo que en acceso-disco.c. Puedes sustituir la media por una fase angular concreta: 0 % indica llegada inmediata y 50 % media vuelta. La fase corresponde al momento de llegada a la pista, después de la búsqueda. La espera en cola se añade solamente a la respuesta.
¿Dónde se consume el tiempo?
| Fase | Tiempo, ms | Porcentaje del servicio |
|---|
Amortizar los posicionamientos sobre más bytes
Conserva el total de bytes y compara N accesos independientes con una transferencia agrupada. Se fija 7200 RPM, 8 ms de búsqueda, 200 MB/s y 0,2 ms de otros costes por petición, sin cola. La agrupación paga esos costes una sola vez; no modela cruces de pista ni límites de petición reales.
Igual contenido, distinta organización
| Escenario | Posicionamientos | Tiempo total, ms | Caudal, MB/s |
|---|
17. Diagnóstico y resumen
| Observación | Interpretación a comprobar |
|---|---|
| 200 MB/s anunciados y poco caudal con 4 KiB aleatorios. | El posicionamiento puede dominar cada operación pequeña. |
| Una lectura buffered tarda microsegundos. | Puede resolverse desde caché; no se ha aislado la mecánica. |
| La latencia crece con la carga. | Revisar cola, competencia y cambios del servicio. |
| Hay más IOPS, pero no más bytes por segundo. | Comprobar tamaños de operación y capa de medida. |
| La lectura directa sigue siendo muy rápida. | Puede intervenir otra caché, almacenamiento flash o virtualización. |
| Se divide entre RPM cero para representar un SSD. | El modelo mecánico no corresponde a ese dispositivo. |
| La aplicación finaliza muchas escrituras muy rápido. | Identificar si confirmó búferes, cachés o persistencia. |
- El tiempo de servicio del modelo suma búsqueda, rotación residual, transferencia y otros costes declarados.
- El tiempo de respuesta incorpora la espera en cola.
- La media rotacional es 30000/RPM ms bajo el supuesto de fase uniforme.
- La transferencia requiere convertir bytes y tasa a unidades compatibles.
- En solicitudes pequeñas aleatorias de HDD suele pesar más el posicionamiento que la transferencia.
- Una operación agrupada amortiza costes; una aplicación secuencial no determina por sí sola las peticiones físicas.
- IOPS, caudal y latencia describen aspectos diferentes y requieren declarar la carga.
- Medir una llamada no descompone sus fases mecánicas; caché, cola y dispositivo forman parte de la interpretación.
18. Preguntas de repaso y práctica
1. ¿Qué diferencia servicio de respuesta?
En el modelo, respuesta es espera en cola más servicio. Hay que mantener la misma capa de observación.
2. ¿Cuánto tarda una vuelta a 7200 RPM?
60000/7200 = 8,333333 ms aproximadamente.
3. ¿Por qué usamos media vuelta de espera?
Porque suponemos fase angular uniforme al llegar a la pista. No es el tiempo de cada acceso individual.
4. ¿El disco deja de girar durante la búsqueda?
No. La espera de rotación que añadimos es la residual después de llegar y asentarse.
5. ¿RPM permite calcular la búsqueda?
No. Describe velocidad de rotación, no el desplazamiento del actuador.
6. ¿Cuánto tarda transferir 4096 bytes a 200 MB/s?
0,02048 ms usando MB decimales.
7. ¿Son iguales 200 MB/s y 200 MiB/s?
No. Son 200000000 y 209715200 bytes por segundo respectivamente.
8. ¿Duplicar la tasa divide por dos el acceso pequeño del ejemplo?
No. Solo reduce a la mitad la fase de transferencia, que ocupa una fracción pequeña del total.
9. ¿Cuándo vale aproximadamente IOPS = 1000/Tms?
Para operaciones seriales sin pausas, si T es el tiempo medio correspondiente. Con servicio sin cola, describe ese modelo particular.
10. ¿Qué dato falta para convertir IOPS en MB/s?
El tamaño de las operaciones y la conversión de unidades; con tamaños variables se necesitan los bytes totales.
11. ¿Por qué agrupar puede mejorar el caudal?
Porque un posicionamiento y otros costes fijos se distribuyen sobre más bytes.
12. ¿Una secuencia de pread pequeños obliga a buscar en cada llamada?
No. Caché, anticipación y fusión pueden cambiar las peticiones que llegan al dispositivo.
13. ¿Qué significa L = 3 en la ley de Little?
Que hay tres solicitudes pendientes de media dentro de la frontera observada, no que el límite de cola sea tres.
14. ¿La media de latencia determina p95?
No. Distribuciones diferentes pueden tener la misma media y percentiles distintos.
15. ¿Una lectura del programa en C mide solo el medio?
No. Mide una lectura buffered completa con los efectos de la pila y el entorno.
16. ¿fsync expulsa el archivo de la caché?
No. Sincronizar datos no equivale a descartarlos de la caché de páginas.
17. ¿direct=1 asegura que los datos vienen del plato?
No. Puede haber caché del dispositivo y otras capas. También puede no haber un HDD físico.
18. ¿Cómo se representa un SSD con la fórmula de RPM?
No se utiliza esa fórmula. Se necesita un modelo sin búsqueda mecánica ni rotación.
19. ¿La distancia en cilindros predice exactamente la duración?
No. Es una coordenada de un modelo didáctico; el tiempo real depende de más factores y no suele ser lineal.
20. ¿Qué demuestra el verificador del laboratorio?
Que el cálculo cumple los casos y rechazos probados y que el lector conserva el archivo. No certifica la velocidad física del dispositivo.
Para practicar
- Calcula vuelta completa y espera media a 5400, 7200, 10000 y 15000 RPM.
- Repite el acceso de 4 KiB con tasas de 100, 200 y 400 MB/s y compara el porcentaje de transferencia.
- Calcula el servicio para 1 MiB y comprueba la conversión a MB/s decimales.
- Agrega 5, 20 y 100 ms de cola e identifica qué salida cambia en acceso-disco.c.
- Encuentra el tamaño para el que transferencia y costes fijos son iguales. Con los parámetros iniciales es aproximadamente 2473333 bytes.
- Usa el simulador con 0 %, 50 % y 99,9 % de vuelta; distingue caso concreto de promedio.
- Compara un bloque y 256 bloques en el simulador de agrupación y explica por qué el primer caso tiene relación uno.
- Compila ambos programas y ejecuta el verificador.
- Repite la medición buffered y registra media, p50 y p95 sin exigir un orden entre patrones.
- Si hay soporte, completa las cuatro combinaciones fio de patrón y tamaño para variar un factor a la vez.
- Aplica L = λW a 250 operaciones/s y 12 ms de respuesta media y explica la frontera elegida.
- Describe qué supuestos necesitarías para pasar de un recorrido en cilindros a una estimación temporal.
En el tema 42 estudiaremos FCFS y SSTF: atender por orden de llegada o elegir la solicitud más cercana, con sus efectos sobre recorrido, espera e inanición.