// fsyncbench.c: cuánto cuesta pedir que cada escritura llegue al disco.
//
//   fsyncbench archivo n
// Escribe n registros de 512 B tres veces: sin fsync, con fdatasync y con
// fsync después de cada uno. Los números dependen mucho del disco y, en el
// contenedor, de la capa de virtualización: compárenlos entre sí.

#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

static double now(void) {
    struct timespec t;
    clock_gettime(CLOCK_MONOTONIC, &t);
    return t.tv_sec + t.tv_nsec / 1e9;
}

// modo 0: solo write; 1: write + fdatasync; 2: write + fsync
static double run(const char *path, int n, int mode) {
    char rec[512];
    memset(rec, 'x', sizeof rec);
    int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) { perror(path); exit(1); }
    double t0 = now();
    for (int i = 0; i < n; i++) {
        if (write(fd, rec, sizeof rec) != (ssize_t)sizeof rec) { perror("write"); exit(1); }
        if (mode == 1 && fdatasync(fd) < 0) { perror("fdatasync"); exit(1); }
        if (mode == 2 && fsync(fd) < 0) { perror("fsync"); exit(1); }
    }
    double t = now() - t0;
    close(fd);
    return t;
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "uso: %s archivo n\n", argv[0]);
        return 1;
    }
    int n = atoi(argv[2]);
    if (n <= 0) n = 100;
    const char *label[] = { "write", "write + fdatasync", "write + fsync" };
    for (int m = 0; m < 3; m++) {
        double t = run(argv[1], n, m);
        printf("%-18s %5d registros: %8.1f us por registro\n", label[m], n, t / n * 1e6);
    }
    unlink(argv[1]);
    return 0;
}
